Die Modernisierung einer Legacy-Rails-App ist kein Wochenendprojekt. Sie erfordert einen strukturierten Ansatz über vier Phasen: Audit, Planung, Durchführung und Validierung. Wer eine Phase überspringt, riskiert Produktionsausfälle, Datenverlust oder ein halbfertiges Upgrade, das monatelang stillsteht.

Diese Checkliste deckt die konkreten Schritte für das Upgrade von Rails-Anwendungen ab: von älteren Versionen (3.x bis 6.x) auf Rails 7.x+, einschliesslich Ruby-Versionswechsel, Gem-Ersatz und Datenbankänderungen.

USEOs Erfahrungswerte

Nach über 15 Jahren Wartung und Modernisierung von Rails-Anwendungen sehen wir immer wieder dieselben Muster:

  • 80 % der Legacy-Rails-Apps, die wir auditieren, laufen auf Rails 4.x oder 5.x mit Ruby 2.5 oder 2.6. Diese Versionen sind End-of-Life und erhalten keine Sicherheitspatches mehr.
  • Testabdeckung unter 40 % ist die Norm. Die meisten Legacy-Apps haben entweder keine Tests oder eine fragile Test-Suite, der niemand vertraut. Das ist der grösste Risikofaktor bei jedem Upgrade.
  • Die typische Modernisierungsdauer beträgt 3-6 Monate für eine mittelgrosse App (50-150 Models), bei einem dedizierten Entwickler. Apps ohne Testabdeckung benötigen zusätzlich 4-8 Wochen für das Schreiben einer Baseline-Test-Suite, bevor die eigentliche Upgrade-Arbeit beginnen kann.
  • Häufigster Blocker: verwaiste Gems. Wir treffen regelmässig auf Apps, die an alte Rails-Versionen gebunden sind, weil ein einzelnes Gem keinen gepflegten Fork hat.
  • Inkrementelle Upgrades schlagen Big-Bang-Rewrites. Wir upgraden jeweils eine Minor-Version (5.0 auf 5.1 auf 5.2 auf 6.0 usw.). Mehrere Major-Versionen in einem Schritt zu überspringen, ist der häufigste Grund für gescheiterte Projekte.

Besonders bei Schweizer Unternehmen sehen wir oft Rails-Apps, die seit 2016/2017 nicht mehr aktualisiert wurden und jetzt zwei oder drei Major-Versionen hinterherhinken.

Phase 1: Audit des bestehenden Rails-Stacks

Bevor Sie eine einzige Zeile Code ändern, brauchen Sie ein vollständiges Bild des Ist-Zustands.

Ruby- und Rails-Versionsinventar

  • Aktuelle Ruby-Version dokumentieren (ruby -v) und Rails-Version (rails -v)
  • Prüfen, ob Ihre Ruby-Version noch Sicherheitspatches erhält (Ruby Maintenance Branches)
  • Prüfen, ob Ihre Rails-Version noch unterstützt wird (Rails Maintenance Policy)
  • Ziel-Versionen für Ruby und Rails definieren

Ruby End-of-Life Referenz:

Ruby-VersionEnd of Life
2.7März 2023
3.0März 2024
3.1März 2025
3.2März 2026
3.3März 2027

Gem-Audit

  • bundle outdated ausführen, um alle veralteten Gems aufzulisten
  • bundler-audit check ausführen, um Gems mit bekannten CVEs zu identifizieren
  • Gems markieren, die verwaist sind (keine Commits seit 2+ Jahren, keine Reaktion auf Issues)
  • Gems identifizieren, die mit der Ziel-Rails-Version nicht funktionieren
  • Nach Gems suchen, die spezifische Rails- oder Ruby-Versionen in ihrer Gemspec pinnen

Häufig problematische Gems in Legacy-Apps:

Legacy-GemStatusErsatz
paperclipDeprecated (2018)active_storage (in Rails 5.2+ integriert)
will_paginateNicht gepflegt für neuere Railspagy oder kaminari
attr_encryptedVeraltetlockbox oder Rails 7 Encrypted Attributes
cancanAufgegebencancancan (gepflegter Fork)
therubyracerAufgegebenmini_racer oder entfernen bei Webpacker/jsbundling
coffee-railsDeprecatedCoffeeScript in ES6+ umschreiben
sass-railsErsetztdartsass-rails oder cssbundling-rails
sprockets (< 4.0)Veraltetsprockets 4.x, propshaft oder jsbundling-rails
webpackerDeprecated (Rails 7)jsbundling-rails + cssbundling-rails
delayed_jobVeraltetsolid_queue (Rails 8) oder sidekiq
globalizeVeraltetmobility

Testabdeckung bewerten

  • Test-Suite ausführen. Pass/Fail-Verhältnis und Gesamtlaufzeit dokumentieren
  • simplecov installieren und Zeilenabdeckung in Prozent messen
  • Kritische Pfade ohne Testabdeckung identifizieren (Authentifizierung, Zahlungen, Kern-Geschäftslogik)
  • Testqualität bewerten: Prüfen die Tests tatsächlich Verhalten, oder führen sie nur Code aus, ohne Ergebnisse zu verifizieren?

Infrastruktur und Abhängigkeiten

  • Datenbankversion dokumentieren (PostgreSQL, MySQL) und Kompatibilität mit Ziel-Rails-Version prüfen
  • Alle externen Service-Integrationen auflisten (Payment-Gateways, E-Mail-Provider, APIs)
  • Redis-/Memcached-Versionen prüfen, falls für Caching oder Background-Jobs verwendet
  • Deployment-Pipeline dokumentieren (Capistrano, Docker, Heroku usw.)
  • Aktuellen Ruby-Prozessmanager erfassen (Puma, Unicorn, Passenger)

Code-Qualität Baseline erfassen

  • rubocop mit Standard-Config ausführen und Anzahl Verstösse dokumentieren
  • rails_best_practices ausführen und Output prüfen
  • brakeman für Sicherheitsanalyse ausführen
  • Nach Monkey-Patches auf Rails-Internals suchen (diese brechen bei Upgrades)
  • Nach alias_method_chain suchen (in Rails 5 entfernt, ersetzt durch Module#prepend)

Phase 2: Upgrade-Pfad planen

Versionstreppe definieren

Überspringen Sie niemals Major-Versionen. Upgraden Sie innerhalb jeder Major-Version eine Minor-Version nach der anderen, dann auf die nächste Major.

Beispiel-Upgrade-Pfad für eine Rails-4.2-App mit Ziel Rails 7.2:

Rails 4.2 / Ruby 2.3
  -> Rails 5.0 / Ruby 2.4
  -> Rails 5.1 / Ruby 2.5
  -> Rails 5.2 / Ruby 2.6
  -> Rails 6.0 / Ruby 2.7
  -> Rails 6.1 / Ruby 3.0
  -> Rails 7.0 / Ruby 3.1
  -> Rails 7.1 / Ruby 3.2
  -> Rails 7.2 / Ruby 3.3

Jeder Schritt ist ein separates Deployment auf Produktion. Bündeln Sie nicht mehrere Versionssprünge.

  • Ihren spezifischen Versionspfad vom Ist- zum Ziel-Zustand aufzeichnen
  • Für jeden Schritt den Rails Upgrade Guide der entsprechenden Version durchgehen
  • Aufwand pro Schritt schätzen (typisch: 1-3 Wochen pro Minor-Versionssprung)
  • Den schwierigsten Schritt identifizieren (meist die Major-Grenzen: 4.x auf 5.0, 5.x auf 6.0, 6.x auf 7.0)

Breaking Changes nach Rails-Version

Rails 4.x auf 5.0:

  • ApplicationRecord als neue Basisklasse (ersetzt ActiveRecord::Base als Parent)
  • ApplicationJob, ApplicationMailer Basisklassen hinzugefügt
  • belongs_to erfordert optional: true für nullable Associations
  • halt_callback_chain_on_return_false entfernt
  • rails-Befehl ersetzt rake für die meisten Tasks

Rails 5.x auf 6.0:

  • Autoloader wechselt von Classic zu Zeitwerk (Autoload-Probleme müssen behoben werden)
  • Action Cable, Active Storage, Action Mailbox, Action Text als Defaults hinzugefügt
  • update_attributes deprecated zugunsten von update
  • Host-Authorization-Middleware hinzugefügt (config.hosts konfigurieren)

Rails 6.x auf 7.0:

  • Webpacker ersetzt durch jsbundling-rails / importmap-rails
  • Neues Encryption-Framework für Active Record
  • Async Queries eingeführt
  • Änderungen an Rails.application.credentials
  • button_to generiert <button> statt <input type="submit">

Rails 7.0 auf 7.1+:

  • Composite Primary Keys Support
  • normalizes-API für Active Record
  • Dockerfile wird standardmässig generiert
  • config.autoload_lib eingeführt

Risikobewertung und Rollback-Plan

  • Rollback-Kriterien definieren: Welche Fehler lösen ein Rollback aus?
  • Sicherstellen, dass Datenbank-Migrationen reversibel sind (down-Methoden schreiben)
  • Feature Flags planen, um aufgerüstete Code-Pfade zu isolieren
  • Staging-Umgebung einrichten, die Produktionsdaten spiegelt (anonymisiert)
  • Rollback-Prozedur für jeden Upgrade-Schritt dokumentieren

Ressourcen einplanen

  • Einen dedizierten Entwickler (oder Pair) für das Upgrade zuweisen. Kontextwechsel töten Upgrade-Projekte
  • Zeit für das Upgrade im Sprint-Planning blockieren. Upgrades, die “wenn wir Zeit haben” gemacht werden, werden nie fertig
  • Code-Freeze-Perioden für Major-Versionssprünge einplanen
  • Gesamtbudget schätzen (unsere Faustregel: 2-4 Entwicklerwochen pro Major-Rails-Versionssprung)

Phase 3: Upgrade durchführen

Vorbereitung

  • Langlebigen Feature-Branch für das Upgrade erstellen (upgrade/rails-X.Y)
  • CI einrichten, um Tests gegen den Upgrade-Branch laufen zu lassen
  • Bei Testabdeckung unter 60 %: Tests für kritische Pfade schreiben, bevor das Upgrade beginnt
  • Produktionsdatenbank sichern
  • bundler selbst zuerst aktualisieren: gem install bundler (neueste stabile Version)

Ruby-Version upgraden

Upgraden Sie Ruby vor Rails. Jede Rails-Version hat Mindestanforderungen an Ruby.

  • .ruby-version (oder Gemfile Ruby-Constraint) auf Zielversion aktualisieren
  • bundle install ausführen und Gem-Kompatibilitätsprobleme beheben
  • Test-Suite ausführen. Fehler durch Ruby-Syntax-/Verhaltensänderungen beheben
  • Auf diese häufigen Ruby-Upgrade-Probleme achten:
    • Ruby 2.7: Keyword-Argument-Separation-Warnungen (werden in 3.0 zu Fehlern)
    • Ruby 3.0: **kwargs-Separation wird erzwungen, Änderungen bei Frozen-String-Literals
    • Ruby 3.1: Psych 4.0 bricht YAML-Laden (nutzen Sie YAML.unsafe_load oder permitted_classes)
    • Ruby 3.2: Struct keyword_init wird Opt-in, Object#=~ entfernt

Rails-Version upgraden (pro Schritt wiederholen)

Für jeden Minor-/Major-Versionssprung:

  1. rails-Gem-Version im Gemfile aktualisieren
  2. bundle update rails ausführen
  3. rails app:update ausführen und jede generierte Diff sorgfältig prüfen
  4. config/application.rb aktualisieren: config.load_defaults X.Y
  5. config/initializers/new_framework_defaults_X_Y.rb prüfen und aktualisieren
  6. rails db:migrate ausführen, um Migrationen zu verifizieren
  7. Vollständige Test-Suite ausführen
  8. Deprecation-Warnungen beheben (sie werden in der nächsten Major-Version zu Fehlern)
  9. Auf Staging deployen und Smoke-Test durchführen
  10. Auf Produktion deployen

Gem-Ersatz-Checkliste

Diese Gem-Wechsel beim passenden Rails-Versionsschritt durchführen:

  • Paperclip zu Active Storage (beim Rails-5.2-Schritt)

    • Active Storage installieren: rails active_storage:install
    • Datei-Metadaten in Active-Storage-Tabellen migrieren
    • Model-Attachments von has_attached_file auf has_one_attached umstellen
    • Beide Systeme parallel laufen lassen, bevor umgeschaltet wird
  • Webpacker zu jsbundling-rails (beim Rails-7.0-Schritt)

    • Installieren: rails new myapp -j esbuild (oder zu bestehendem Projekt hinzufügen)
    • JS-Einstiegspunkte von app/javascript/packs/ nach app/javascript/ verschieben
    • javascript_pack_tag durch javascript_include_tag ersetzen
    • webpacker-Gem und Config-Dateien entfernen
  • Sprockets zu Propshaft (optional, ab Rails 7.0+)

    • sprockets-rails durch propshaft im Gemfile ersetzen
    • Asset-Pipeline-Config aus config/initializers/assets.rb übernehmen
    • Sicherstellen, dass alle Asset-Pfade Digested-URLs verwenden
  • Coffee-Rails entfernen

    • .coffee-Dateien in .js oder .es6 konvertieren
    • decaffeinate für automatische Konvertierung nutzen
    • coffee-rails-Gem entfernen

Datenbank-Aspekte

  • rails db:migrate:status ausführen, um ausstehende oder fehlende Migrationen zu prüfen
  • Testen, dass alle Migrationen von Grund auf laufen: rails db:drop db:create db:migrate
  • Bei gleichzeitigem PostgreSQL-Upgrade: zuerst mit neuer PG-Version auf Staging testen
  • Active-Record-Änderungen prüfen: umbenannte Methoden, geänderte Default-Scopes
  • Bei Nutzung von schema.rb: nach jedem Rails-Versionssprung neu generieren: rails db:schema:dump

Phase 4: Validierung und Absicherung

Regressionstests

  • Vollständige Test-Suite ausführen. Null Fehler vor dem Produktions-Deploy
  • brakeman erneut ausführen und mit der Phase-1-Baseline vergleichen
  • bundler-audit check ausführen, um keine neuen Verwundbarkeiten zu bestätigen
  • Manuelle Smoke-Tests auf kritischen User-Flows (Login, Checkout, Admin-Panels)
  • Background-Jobs testen: Verarbeitung mit neuer Rails-Version verifizieren

Performance-Benchmarking

  • Antwortzeiten für Schlüssel-Endpoints vergleichen (vorher vs. nachher)
  • Speicherverbrauch der neuen Ruby-/Rails-Version unter Last prüfen
  • rack-mini-profiler auf kritischen Seiten ausführen, um N+1-Queries oder langsame Views zu finden
  • Caching verifizieren (Fragment Cache, Russian Doll Caching, HTTP-Cache-Headers)
  • Staging-Umgebung mit realistischen Traffic-Mustern lasttesten

Sicherheitsvalidierung

  • brakeman mit --confidence-level=1 für gründliches Scanning ausführen
  • CSRF-Schutz verifizieren
  • Content-Security-Policy-Headers prüfen
  • SSL/TLS-Konfiguration nach Deploy bestätigen
  • config/credentials.yml.enc prüfen und sicherstellen, dass keine Secrets exponiert sind
  • Neue Rails-Sicherheitsstandards aktivieren (in new_framework_defaults-Dateien prüfen)

Aufräumarbeiten nach dem Upgrade

  • Deprecated Gem-Versionen und ungenutzte Gems aus dem Gemfile entfernen
  • Alte Migrationsdateien löschen, falls Ihr Team diese Praxis verfolgt (schema.rb / structure.sql behalten)
  • CI-Konfiguration aktualisieren: nur noch gegen neue Ruby-/Rails-Versionen testen
  • Dokumentation und README mit neuen Versionsanforderungen aktualisieren
  • Upgrade-Branch nach Merge archivieren
  • Nächstes Upgrade einplanen (Kalender-Erinnerung in 6 Monaten setzen)

Kurzreferenz: Tools für jede Phase

PhaseToolZweck
Auditbundler-auditGems mit bekannten CVEs finden
AuditbrakemanStatische Sicherheitsanalyse
AuditrubocopCode-Qualität und Stil
Auditrails_best_practicesRails-spezifische Code-Smells
AuditsimplecovTestabdeckung messen
Planungrails app:updateConfig-Diffs für neue Rails-Version generieren
Planungnext_rails GemGems finden, die das Rails-Upgrade blockieren
DurchführungdecaffeinateCoffeeScript in modernes JS konvertieren
Durchführungdual_boot GemZwei Rails-Versionen parallel betreiben
Validierungrack-mini-profilerPerformance-Profiling
Validierungderailed_benchmarksSpeicher- und Bootzeit-Analyse

Häufig gestellte Fragen

Wie lange dauert ein vollständiges Rails-Upgrade?

Für einen einzelnen Major-Versionssprung (z. B. Rails 5.2 auf 6.1) sollten Sie 4-8 Wochen fokussierte Arbeit für eine mittelgrosse App einplanen. Apps mit geringer Testabdeckung brauchen zusätzliche Zeit vorab, um ein Sicherheitsnetz aufzubauen. Upgrades über mehrere Major-Versionen (z. B. 4.2 auf 7.2) erstrecken sich typischerweise über 3-6 Monate.

Kann ich Rails-Versionen beim Upgrade überspringen?

Minor-Versionen innerhalb derselben Major können Sie überspringen (z. B. direkt von 6.0 auf 6.1). Aber überspringen Sie niemals Major-Versionen. Die internen API-Änderungen sind zu umfangreich, und Sie verpassen kritische Deprecation-Warnungen, die den Upgrade-Pfad leiten.

Was tun, wenn ein kritisches Gem die Ziel-Rails-Version nicht unterstützt?

Drei Optionen: (1) Einen gepflegten Fork auf GitHub finden, (2) das Gem vendorn und selbst patchen, oder (3) es durch eine Alternative ersetzen. Das next_rails-Gem hilft, blockierende Gems zu identifizieren, bevor Sie starten.

Ruby oder Rails zuerst upgraden?

Upgraden Sie Ruby zuerst auf die Mindestversion, die Ihre Ziel-Rails-Version erfordert, dann upgraden Sie Rails. So vermeiden Sie, Ruby- und Rails-Probleme gleichzeitig debuggen zu müssen.

Lohnt sich ein Upgrade, oder besser gleich ein Rewrite?

In den allermeisten Fällen: Upgrade. Ein Rewrite kostet erfahrungsgemäss 3-5x mehr als ein inkrementelles Upgrade und birgt das Risiko, Geschäftslogik zu verlieren, die über Jahre gewachsen ist. Rewrites sind nur dann sinnvoll, wenn die Codebasis so stark verrottet ist, dass kein Test mehr grün wird und die ursprünglichen Entwickler nicht mehr verfügbar sind.

Verwandte Artikel