Case Study · B2B-Plattform eines Automobilherstellers

Von

Aus einem Serverumzug wurde eine Modernisierung von Laravel 8 auf 13.

Der Kunde wollte eine gewachsene B2B-Anwendung auf einen neuen Server bringen. Ich hätte sie unverändert umziehen können. Damit wäre sie zwar auf neuer Infrastruktur gelaufen, aber weiterhin auf einem alten Framework mit veralteten Abhängigkeiten und kaum Schutz für ihre wichtigsten Abläufe.

Ich schlug deshalb keinen Rewrite vor, sondern einen kontrollierten Upgrade-Pfad. Das Ziel war eine wieder wartbare Anwendung, ohne Bestellung, Lager, Logistik, Dokumente oder Abrechnung unterwegs neu zu erfinden.

Weg der B2B-Anwendung von Laravel 8 auf Laravel 13
Die eigentliche Aufgabe war größer als der geplante Serverumzug. Das System sollte auf einen aktuellen, überprüfbaren Stand kommen.

Ausgangslage

Der Auftrag war kein gewöhnlicher Serverumzug

Die Plattform steuert Bestellungen, Lagerbewegungen, Logistik, Dokumente und Abrechnung. Sie tauscht außerdem Daten mit externen Systemen aus. Ein Fehler betrifft deshalb nicht irgendeine alte Verwaltungssoftware. Er kann echte Arbeit stoppen oder falsche Geschäftsdaten erzeugen.

Bei der Bestandsaufnahme fand ich Laravel 8, PHP 8.0, Livewire 2, Laravel Mix und mehrere Abhängigkeiten, die neue Framework-Versionen blockierten. Gleichzeitig gab es zu wenig automatisierte Absicherung, um einen großen Versionssprung guten Gewissens durchzuführen.

Das änderte die Aufgabenstellung. Der Serverumzug blieb notwendig, aber er wurde zum letzten Schritt einer größeren Modernisierung.

Entscheidung

Warum ich die Anwendung modernisiert und nicht neu geschrieben habe

Ein Rewrite hätte aufgeräumter gewirkt. Er hätte aber auch jahrelang gewachsene Regeln wieder zu Vermutungen gemacht. Welche Bestellpositionen dürfen zusammengeführt werden? Wann darf eine Rechnung erneut versendet werden? Welche Berechtigung gilt noch, wenn sie zwischen zwei Requests entzogen wurde?

Solche Regeln stehen selten vollständig in einem Pflichtenheft. Sie stecken im Code, in Datenbankstrukturen und in den Erwartungen der Menschen, die täglich mit dem System arbeiten.

Deshalb blieb der fachliche Kern bestehen. Ich modernisierte den technischen Unterbau schrittweise und änderte Geschäftslogik nur dort, wo Tests einen Fehler oder eine unsichere Grenze sichtbar machten.

Vorgehen

Zuerst musste die alte Anwendung ihr Verhalten erklären

Bevor ich Composer aktualisierte, arbeitete ich mich durch Routen, Livewire-Komponenten, Jobs, Migrationen und die wichtigsten Abläufe in der laufenden Anwendung. Daraus entstanden Tests, Factories und Hilfscode für die vorhandenen Geschäftsregeln.

Diese erste Phase fügte 10.423 Zeilen hinzu. Die Zahl ist kein Selbstzweck. Entscheidend war, was die Tests festhielten: Bestellungen, Berechtigungen, Warenkorbverhalten, Dokumente, Hintergrundjobs und externe Übergaben.

Die Arbeit fand auch Fehler, die schon unter Laravel 8 vorhanden waren. Ich behob sie vor dem Upgrade. So musste ich später nicht raten, ob ein Problem neu war oder nur zum ersten Mal sichtbar wurde.

Umsetzung

Fünf Laravel-Versionen, aber kein großer Sprung ins Ungewisse

Ich ging von Laravel 8 über jede Hauptversion bis Laravel 13. Nach jeder Stufe mussten PHP-Tests, Frontend-Build und Browser-Flows wieder funktionieren. Erst dann kam die nächste Version.

Das klingt langsamer als ein großer Sprung. In der Praxis spart es Sucharbeit. Wenn nach einem Upgrade etwas bricht, liegt die Ursache in einem kleinen Änderungssatz statt irgendwo zwischen fünf Framework-Releases und einem komplett neuen Frontend-Build.

Die Modernisierung blieb dadurch in 35 fokussierten Commits nachvollziehbar. Jeder Schritt hatte eine erkennbare Aufgabe und einen überprüfbaren Endzustand.

BereichVorherNachher
BackendLaravel 8 · PHP 8.0Laravel 13 · PHP 8.3
UILivewire 2 · Tailwind 2Livewire 4 · Tailwind 4
Build / TestsMix · PHPUnit 9Vite 8 · PHPUnit 12

Die schwierigen Stellen

Die eigentliche Arbeit lag zwischen den Framework-Upgrades

Laravel selbst war selten das größte Problem. Schwieriger waren die Stellen, an denen das alte System stillschweigende Annahmen mitgebracht hatte.

Ein aufgegebenes Warenkorb-Paket blockierte das Upgrade

Ich kopierte weder das ganze Paket noch schrieb ich den Checkout gleichzeitig neu. Stattdessen übernahm die Anwendung nur den kleinen Vertrag, den sie tatsächlich brauchte. Dazu gehörten Warenkorb-Instanzen, Artikel, Optionen, Events, Summen und gespeicherte Warenkörbe. Bestehende Tests hielten das Verhalten fest.

Livewire machte Browserzustand zu einer Sicherheitsfrage

Öffentliche Properties behandelte ich wie Request-Daten. Preise wurden serverseitig neu berechnet, Datensätze beim Speichern erneut dem Benutzer zugeordnet und entzogenes Zugriffsrecht auch zwischen zwei Requests geprüft. Render-Tests hätten diese Fehler nicht gefunden, deshalb testete die Suite vollständige Aktionen.

Hintergrundjobs mussten Wiederholungen aushalten

Doppelte Requests durften keine zweite Bestellung erzeugen. Ein abgebrochener Worker durfte Dokumente oder E-Mails nicht doppelt ausliefern. Persistente Statusdaten, eindeutige Jobs und Sperren machten diese Abläufe wiederholbar.

Ein grüner PHP-Test beweist keinen funktionierenden Produktions-Build

Beim Wechsel von Mix zu Vite und von Tailwind 2 auf 4 konnten dynamisch erzeugte Klassen aus dem fertigen CSS verschwinden. Deshalb prüft ein Build-Test 22 benötigte Selektoren in der tatsächlich erzeugten Datei. Außerdem ersetzte ich 124 Verwendungen externer Icon-Komponenten durch lokale SVGs und entfernte 17 Composer-Pakete.

Produktionswechsel

Der neue Code war erst fertig, als er sicher live lief

Die aktualisierte Anwendung lief zuerst isoliert auf dem neuen Runtime-Stack. Anonymisierte Daten machten reale Geschäftsabläufe prüfbar. Mailpit fing ausgehende Nachrichten ab, damit ein Testlauf keinen Kunden erreichte.

Vor dem Wechsel prüfte ich Scheduler, Queue, öffentliche Einstiegspunkte, sechs Browser-Flows, Blade-Kompilierung, Composer-Audit und den Produktions-Build. Nach der Kundenabnahme folgten Daten-Freeze, finale Datenbankmigration und Domain-Umschaltung.

Auf dem neuen Host prüfte ich Anmeldung, Bestellablauf, Dokumente, E-Mail-Übergaben, Queue und Scheduler noch einmal. Erst danach war die Modernisierung für mich abgeschlossen.

Ergebnis

Was der Kunde am Ende bekommen hat

Der Kunde bekam keine neue Anwendung, die erst wieder lernen musste, wie das Geschäft funktioniert. Er bekam die vertrauten Abläufe auf einem aktuellen technischen Fundament, mit deutlich mehr Schutz für künftige Änderungen.

Laravel 8 → 13
Auf einen unterstützten Framework-Stand gebracht
698 PHP-Tests
Mit 4.593 Assertions für die Geschäftsabläufe
6 Browser-Flows
Auf dem echten Produktions-Build geprüft
17 Pakete entfernt
Weniger Abhängigkeiten im laufenden System

Die Anwendung läuft heute mit Laravel 13 auf dem neuen Host. Die Tests dokumentieren nicht nur, dass Seiten rendern. Sie halten die Geschäftsabläufe fest, die bei der nächsten Änderung nicht verloren gehen dürfen.

Rückblick

Was ich aus dieser Modernisierung mitnehme

Ich würde wieder mit dem Verhalten beginnen, nicht mit der Zielversion. Laravel lässt sich nach einem dokumentierten Pfad aktualisieren. Schwieriger ist es, die Regeln einer gewachsenen Anwendung so gut zu verstehen, dass man erkennt, was sich ändern darf und was nicht.

Ich würde auch wieder einen Hauptversionsschritt nach dem anderen machen. Große Sprünge sehen in einem Projektplan kürzer aus. Kleine, vollständig geprüfte Schritte sind in einer geschäftskritischen Anwendung meist schneller, weil Fehler einen klaren Ursprung haben.

Und ich würde den Produktionswechsel wieder als Teil der Entwicklung behandeln. Eine Modernisierung endet nicht bei grünem CI. Sie endet, wenn die Menschen, Jobs und Schnittstellen auf dem neuen System zuverlässig weiterarbeiten.

Meine Rolle

Ich habe die Modernisierung geplant, umgesetzt und bis in Produktion begleitet

Ich bin Mathias Onea, Software Engineer mit Schwerpunkt Laravel, PHP und gewachsenen Geschäftsanwendungen. In diesem Projekt verantwortete ich Bestandsaufnahme, Upgrade-Strategie, Tests, Sicherheitskorrekturen, Frontend-Migration und Produktionswechsel.

Die Zahlen auf dieser Seite wurden gegen den finalen Upgrade-Branch und den erfolgreichen CI-Lauf geprüft.

FAQ

Fragen zur Laravel-Modernisierung

Wann ist ein Laravel-Upgrade sinnvoller als ein Rewrite?

Wenn die Anwendung fachlich funktioniert und viel gewachsene Geschäftslogik enthält. Dann schützt ein schrittweises Upgrade den bestehenden Wert, während ein Rewrite jedes Detail neu beweisen müsste.

Muss jede Laravel-Hauptversion einzeln aktualisiert werden?

Bei einem großen Versionsabstand ist das meist der besser überprüfbare Weg. Nach jeder Stufe lassen sich Fehler einem kleinen Änderungssatz zuordnen und beheben.

Wie lässt sich das Risiko einer Laravel-Modernisierung begrenzen?

Mit Tests für echte Geschäftsabläufe, kleinen Upgrade-Schritten, einer produktionsnahen Staging-Umgebung und einem geplanten Wechsel mit klaren Prüfungen vor und nach dem Cutover.

Kann die Anwendung während der Modernisierung weiterentwickelt werden?

Ja, wenn Upgrade-Arbeit und laufende Produktänderungen sauber getrennt und regelmäßig zusammengeführt werden. Wie viel Parallelität sinnvoll ist, hängt von Testabdeckung, Team und Änderungsrate ab.

Ähnliche Ausgangslage?

Lassen Sie uns zuerst klären, was erhalten bleiben muss.

Für eine erste Einschätzung brauche ich die aktuelle Laravel-Version, die wichtigsten Geschäftsabläufe und einen Überblick über Tests und Deployment.

Modernisierung besprechen