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-Version | End of Life |
|---|---|
| 2.7 | März 2023 |
| 3.0 | März 2024 |
| 3.1 | März 2025 |
| 3.2 | März 2026 |
| 3.3 | März 2027 |
Gem-Audit
-
bundle outdatedausführen, um alle veralteten Gems aufzulisten -
bundler-audit checkausfü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-Gem | Status | Ersatz |
|---|---|---|
paperclip | Deprecated (2018) | active_storage (in Rails 5.2+ integriert) |
will_paginate | Nicht gepflegt für neuere Rails | pagy oder kaminari |
attr_encrypted | Veraltet | lockbox oder Rails 7 Encrypted Attributes |
cancan | Aufgegeben | cancancan (gepflegter Fork) |
therubyracer | Aufgegeben | mini_racer oder entfernen bei Webpacker/jsbundling |
coffee-rails | Deprecated | CoffeeScript in ES6+ umschreiben |
sass-rails | Ersetzt | dartsass-rails oder cssbundling-rails |
sprockets (< 4.0) | Veraltet | sprockets 4.x, propshaft oder jsbundling-rails |
webpacker | Deprecated (Rails 7) | jsbundling-rails + cssbundling-rails |
delayed_job | Veraltet | solid_queue (Rails 8) oder sidekiq |
globalize | Veraltet | mobility |
Testabdeckung bewerten
- Test-Suite ausführen. Pass/Fail-Verhältnis und Gesamtlaufzeit dokumentieren
-
simplecovinstallieren 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
-
rubocopmit Standard-Config ausführen und Anzahl Verstösse dokumentieren -
rails_best_practicesausführen und Output prüfen -
brakemanfür Sicherheitsanalyse ausführen - Nach Monkey-Patches auf Rails-Internals suchen (diese brechen bei Upgrades)
- Nach
alias_method_chainsuchen (in Rails 5 entfernt, ersetzt durchModule#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:
ApplicationRecordals neue Basisklasse (ersetztActiveRecord::Baseals Parent)ApplicationJob,ApplicationMailerBasisklassen hinzugefügtbelongs_toerfordertoptional: truefür nullable Associationshalt_callback_chain_on_return_falseentferntrails-Befehl ersetztrakefü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_attributesdeprecated zugunsten vonupdate- Host-Authorization-Middleware hinzugefügt (
config.hostskonfigurieren)
Rails 6.x auf 7.0:
Webpackerersetzt durchjsbundling-rails/importmap-rails- Neues Encryption-Framework für Active Record
- Async Queries eingeführt
- Änderungen an
Rails.application.credentials button_togeneriert<button>statt<input type="submit">
Rails 7.0 auf 7.1+:
- Composite Primary Keys Support
normalizes-API für Active RecordDockerfilewird standardmässig generiertconfig.autoload_libeingefü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
-
bundlerselbst 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(oderGemfileRuby-Constraint) auf Zielversion aktualisieren -
bundle installausfü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.0bricht YAML-Laden (nutzen SieYAML.unsafe_loadoderpermitted_classes) - Ruby 3.2:
Structkeyword_init wird Opt-in,Object#=~entfernt
Rails-Version upgraden (pro Schritt wiederholen)
Für jeden Minor-/Major-Versionssprung:
-
rails-Gem-Version imGemfileaktualisieren -
bundle update railsausführen -
rails app:updateausführen und jede generierte Diff sorgfältig prüfen -
config/application.rbaktualisieren:config.load_defaults X.Y -
config/initializers/new_framework_defaults_X_Y.rbprüfen und aktualisieren -
rails db:migrateausführen, um Migrationen zu verifizieren - Vollständige Test-Suite ausführen
- Deprecation-Warnungen beheben (sie werden in der nächsten Major-Version zu Fehlern)
- Auf Staging deployen und Smoke-Test durchführen
- 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_fileaufhas_one_attachedumstellen - Beide Systeme parallel laufen lassen, bevor umgeschaltet wird
- Active Storage installieren:
-
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/nachapp/javascript/verschieben javascript_pack_tagdurchjavascript_include_tagersetzenwebpacker-Gem und Config-Dateien entfernen
- Installieren:
-
Sprockets zu Propshaft (optional, ab Rails 7.0+)
sprockets-railsdurchpropshaftim 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.jsoder.es6konvertieren- decaffeinate für automatische Konvertierung nutzen
coffee-rails-Gem entfernen
Datenbank-Aspekte
-
rails db:migrate:statusausfü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
-
brakemanerneut ausführen und mit der Phase-1-Baseline vergleichen -
bundler-audit checkausfü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-profilerauf 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
-
brakemanmit--confidence-level=1fü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.encprü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
Gemfileentfernen - Alte Migrationsdateien löschen, falls Ihr Team diese Praxis verfolgt (
schema.rb/structure.sqlbehalten) - 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
| Phase | Tool | Zweck |
|---|---|---|
| Audit | bundler-audit | Gems mit bekannten CVEs finden |
| Audit | brakeman | Statische Sicherheitsanalyse |
| Audit | rubocop | Code-Qualität und Stil |
| Audit | rails_best_practices | Rails-spezifische Code-Smells |
| Audit | simplecov | Testabdeckung messen |
| Planung | rails app:update | Config-Diffs für neue Rails-Version generieren |
| Planung | next_rails Gem | Gems finden, die das Rails-Upgrade blockieren |
| Durchführung | decaffeinate | CoffeeScript in modernes JS konvertieren |
| Durchführung | dual_boot Gem | Zwei Rails-Versionen parallel betreiben |
| Validierung | rack-mini-profiler | Performance-Profiling |
| Validierung | derailed_benchmarks | Speicher- 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.