Laravel Legacy Upgrade

Laravel Legacy Upgrade: Wo die wahren Probleme beginnen

„Lass uns Laravel aktualisieren“ klingt nach einer einfachen Aufgabe. Bis man die composer.json öffnet. Ein Legacy-Projekt kann unter Laravel 7 oder 8 einwandfrei laufen, aber ein Upgrade auf eine moderne Version kann eine ganze Kette von Kompatibilitätsproblemen aufdecken.

Hier sind einige der häufigsten Probleme bei einem Laravel Legacy Upgrade.

1. Ein vom alten Projekt verwendetes Paket ist nicht mit dem neuen Laravel kompatibel

Dies ist wahrscheinlich das frustrierendste Szenario. Die Anwendung verwendet ein Paket, das unter Laravel 8 völlig in Ordnung war:

Laravel 8
└── package-x 2.4

Man aktualisiert auf Laravel 11:

Laravel 11
└── package-x 2.4 ❌

Composer weigert sich, es zu installieren, da das Paket eine ältere Version von illuminate/* benötigt.

Nun hat man die Wahl:

  • eine neuere Version finden;
  • das Paket ersetzen;
  • es forken und selbst pflegen;
  • oder das Laravel-Upgrade verschieben.

Und manchmal wird das Paket nicht mehr gepflegt (abandoned), sodass es überhaupt keinen Upgrade-Pfad gibt.

2. Composer-Abhängigkeitskonflikte beim Laravel Legacy Upgrade

Ein einziges Upgrade kann Konflikte aufdecken, die mehrere Ebenen tief gehen.

Zum Beispiel:

Laravel 11
   ↓
requires Symfony 7
   ↓
Package A requires Symfony 6
   ↓
Package B requires Package A

Composer kann all diese Anforderungen nicht erfüllen. Das Frustrierende daran ist, dass das problematische Paket vielleicht gar nicht direkt von Ihnen verwendet wird. Es kann die Abhängigkeit einer anderen Abhängigkeit sein. Genau hier wird ein einfaches Framework-Upgrade zur Abhängigkeits-Archäologie.

3. composer.lock kann den alten Abhängigkeitsbaum beibehalten

Man aktualisiert die composer.json:

"laravel/framework": "^11.0"

Aber Composer meldet immer noch Konflikte, weil die composer.lock Versionen aus der alten Anwendung enthält. Die Lock-Datei repräsentiert den Abhängigkeitsbaum, der zuvor aufgelöst wurde.

Daher muss man manchmal nicht nur Laravel aktualisieren, sondern eine sorgfältig ausgewählte Gruppe verwandter Abhängigkeiten – ohne einfach blind die composer.lock zu löschen und auf das Beste zu hoffen.

4. Die neue Laravel-Version erfordert ein neueres PHP

Zum Beispiel:

Alte Anwendung

Laravel 8
PHP 7.4

wird zu:

Laravel 11
PHP >= 8.2

Aber ein PHP-Upgrade kann eine weitere Schicht von Problemen aufdecken. Einige alte Pakete unterstützen PHP 8.2 möglicherweise nicht mehr. Die Abhängigkeitskette sieht dann wie folgt aus:

Laravel upgrade
      ↓
PHP upgrade
      ↓
old packages become incompatible
      ↓
packages need upgrading/replacing
      ↓
application code needs changes

5. Das Paket hat eine neue Version – aber seine API hat sich geändert

Dies lässt sich leicht unterschätzen. Man findet eine kompatible Version und Composer installiert sie erfolgreich. Großartig.

Außer dass der alte Code:

SomePackage::process($data);

jetzt möglicherweise Folgendes erfordert:

SomePackage::process($data, $options);

Oder die Methode wurde vielleicht umbenannt, entfernt oder ihr Rückgabetyp hat sich geändert. Also: Composer-Erfolg ≠ Anwendungs-Erfolg.

6. Symfony-Upgrades können Laravel-Anwendungen beeinträchtigen

Laravel verlässt sich stark auf Symfony-Komponenten. Wenn Laravel auf neuere Symfony-Versionen umsteigt, können benutzerdefinierter Code oder Pakete, die auf älterem Symfony-Verhalten aufbauen, kaputtgehen. Dies ist besonders relevant für Projekte mit benutzerdefinierten Integrationen, HTTP-Handling, Konsolenbefehlen, E-Mails, Events oder Dateisystem-Funktionalitäten (Filesystem).

7. Flysystem-Upgrades können Dateisystem-Integrationen beschädigen

Ein gutes Beispiel ist der Wechsel zu Flysystem 3. Eine alte Anwendung könnte Folgendes haben:

Laravel
└── Flysystem 1/2
    └── custom storage adapter

Nach dem Upgrade:

Laravel
└── Flysystem 3
    └── old adapter ❌

Die Laravel-Anwendung selbst mag kompatibel sein, aber ein benutzerdefinierter Adapter oder ein Paket, das um die alte Flysystem-API herum aufgebaut wurde, ist es möglicherweise nicht.

8. Authentifizierungs-Pakete haben ihre eigenen Upgrade-Pfade

Laravel Passport, Sanctum, Socialite und andere Ökosystem-Pakete bewegen sich nicht zwangsläufig im Gleichschritt mit Laravel. Es kann passieren, dass:

Laravel → new version
Passport → old version
      ↓
incompatible dependencies

Oder man aktualisiert Passport und stellt dann fest, dass Authentifizierungsabläufe, Konfigurationen oder Datenbankmigrationen angepasst werden müssen.

9. Migrationen von Paketen können sich ändern

Dies ist ein weiteres subtiles Problem. Ein Paket kann korrekt installiert werden, aber sein Datenbank-Setup funktioniert in einer neueren Version möglicherweise anders. Man aktualisiert das Paket, stellt die Anwendung bereit (Deploy) und stellt plötzlich fest, dass erwartete Tabellen, Spalten oder Indizes nicht vorhanden sind. Deshalb müssen Paket-Upgrades als Anwendungsänderungen behandelt werden – und nicht nur als Composer-Änderungen.

10. PHPUnit und die Test-Suite können zu einer weiteren Migration werden

Der Produktionscode sieht vielleicht immer noch gut aus. Dann führt man Folgendes aus:

php artisan test

und die Hälfte der Test-Suite schlägt fehl. Warum? Weil sich die von Laravel unterstützten PHPUnit-Versionen im Laufe der Zeit ändern und PHPUnit selbst veraltete APIs (deprecated APIs) entfernt. Ein Laravel-Upgrade kann also bedeuten, dass Folgendes aktualisiert werden muss:

Laravel
+
PHP
+
Composer packages
+
PHPUnit
+
test code

11. Konfiguration und Anwendungsstruktur können sich ändern

Eine neuere Laravel-Version führt möglicherweise eine andere Standardstruktur oder einen anderen Konfigurationsansatz ein. Die Versuchung ist groß, eine frische Laravel-Installation zu nehmen und alles in das alte Projekt zu kopieren. Das ist oft eine schlechte Idee. Eine Legacy-Anwendung hat Jahre an benutzerdefinierten Konfigurationen, Middleware, Service Providern und Integrationen hinter sich. Das Ziel ist nicht, die alte Anwendung wie eine frische Laravel-Installation aussehen zu lassen. Das Ziel ist es, die bestehende Anwendung sicher auf die neue Framework-Version zu migrieren.

Und genau das macht Laravel-Upgrades so schwierig. Das Problem lautet selten:
„Wie installiere ich Laravel 11?“

Das eigentliche Problem ist:
„Welche Teile dieses 7 Jahre alten Abhängigkeitsbaums können tatsächlich auf Laravel 11 migriert werden, ohne die Anwendung kaputt zu machen?“

Vor einem Upgrade würde ich Folgendes wissen wollen:

  • Welche Pakete blockieren das Upgrade?
  • Welche Pakete werden nicht mehr gepflegt (abandoned)?
  • Welche Pakete müssen ersetzt werden?
  • Welche PHP-Version ist erforderlich?
  • Welche Symfony/Flysystem-Versionen werden sich ändern?
  • Welche Versionen sind aktuell in der composer.lock festgeschrieben (pinned)?
  • Welche APIs weisen Breaking Changes auf?
  • Welche Datenbankmigrationen sind betroffen?
  • Wird die bestehende Test-Suite noch laufen?
  • Welche geschäftskritischen Abläufe benötigen Regressionstests?

Denn bei einer Legacy-Laravel-Anwendung ist der Upgrade-Pfad oft komplizierter als das Upgrade selbst.

Was war der größte Blocker bei einem Laravel-Upgrade, auf den Sie jemals gestoßen sind – ein inkompatibles Paket, Composer-Konflikte, PHP oder etwas anderes?

Mehr lesen:

– Modernisierung bestehender Systeme in der öffentlichen Verwaltung: Ein praktischer Weg zur Modernisierung kritischer Systeme ohne Betriebsunterbrechungen