Als eine regionale Spedition auf mich zukam, lief ihr zentrales Auftragsmanagement-System auf PHP 5.6. Die Codebasis bestand aus 11.000 Zeilen prozeduralem, stark gekoppeltem PHP, das mit einer MySQL 5.5 Datenbank kommunizierte. Es gab kein Framework, keine Dependency Injection und vor allem: null Testabdeckung.
Das System war geschäftskritisch. Wenn es ausfiel, verließen keine Lkw das Depot.
Dies ist die Geschichte, wie wir es in 14 Wochen auf PHP 8.3 und Laravel migriert haben – mit null Minuten Ausfallzeit.
Das Problem mit "Big Bang" Neuentwicklungen
Der erste Gedanke des Kunden war, einfach eine "V2" von Grund auf neu zu bauen und an einem Wochenende umzuschalten. Das ist eine klassische Falle. "Big Bang" Neuentwicklungen von Legacy-Systemen scheitern fast immer, weil:
- Die alte Codebasis Jahre an undokumentierter Geschäftslogik und Sonderfällen enthält.
- Bis V2 fertig ist, haben sich die Geschäftsanforderungen bereits wieder geändert.
- Das Umschalt-Wochenende ist ein hochriskantes Ereignis unter hohem Stress, das meist mit Rollbacks endet.
Die Lösung: Das Strangler-Fig-Muster
Statt einer kompletten Neuentwicklung nutzten wir das Strangler-Fig-Muster (Würgefeigen-Muster). Wir richteten eine moderne Laravel-Anwendung unter PHP 8.3 ein, die parallel zur alten Anwendung lief.
Wir konfigurierten den Webserver (Nginx) so, dass er den Traffic routete:
- Wenn eine Route in der neuen Laravel-App existierte, lieferte Nginx diese über Laravel aus.
- Wenn die Route in Laravel nicht existierte, griff Nginx auf die alte PHP 5.6 Anwendung zurück.
Das ermöglichte es uns, die Anwendung Route für Route, Feature für Feature zu migrieren.
Schritt 1: Datenbank-Abstraktion
Der schwierigste Teil einer Strangler-Migration ist die Datenbank. Beide Anwendungen mussten während der Übergangsphase dieselbe MySQL-Datenbank nutzen.
Wir erstellten Eloquent-Modelle in Laravel, die auf die alten Tabellenstrukturen abgebildet wurden. Wir änderten das Schema nicht; wir änderten nur die Art und Weise, wie wir mit ihm kommunizierten. So konnte der neue Code Daten lesen und schreiben, die der alte Code immer noch verstehen konnte.
Schritt 2: Inkrementelle Feature-Migration
Wir begannen mit den risikoärmsten Funktionen – schreibgeschützte Ansichten wie das Dashboard der Auftragshistorie. Wir bauten die Ansicht in Laravel neu, pushten sie in die Produktion, und Nginx begann automatisch, den Traffic zum neuen Code zu leiten.
Gab es einen Fehler, konnten wir die Nginx-Regel sofort zurücknehmen und den Traffic wieder auf den alten, funktionierenden Code lenken. Das Risiko ging gegen null.
Schritt 3: Den Kern angehen
Als wir Fahrt aufgenommen hatten, nahmen wir uns den komplexen, schreibintensiven Kern vor: den Prozess der Auftragserstellung. Hier war das Fehlen von Tests im alten System besonders gefährlich.
Wir schrieben umfassende PHPUnit-Tests gegen den neuen Laravel-Code, um das erwartete Verhalten zu definieren. Wir ließen beide Systeme für kurze Zeit parallel laufen und verglichen die Ausgaben (Shadow Writing), bevor wir die Route offiziell umschalteten.
Das Ergebnis
Nach 14 Wochen wurde die letzte Route auf Laravel migriert. Wir löschten den alten PHP 5.6 Ordner.
Das Ergebnis:
- Ausfallzeit: 0 Minuten.
- Testabdeckung: Stieg von 0% auf 68% auf den kritischen Pfaden.
- Deployment: Wurde von einem stressigen, zweistündigen manuellen Prozess zu einer automatisierten, 5-minütigen GitHub Actions Pipeline.
Legacy-Modernisierung muss kein Albtraum sein. Mit der richtigen Strategie ist es ein vorhersehbarer, inkrementeller Engineering-Prozess.