🔄 Shop-Migrationen

Legacy PHP → Symfony: Case Study einer Migration nach zwölf Jahren Code

Zwölf Jahre Custom-PHP 5.4, keine Dokumentation und ein Bus-Faktor von eins. Wie wir den Shop mit dem Strangler-Fig-Muster auf Symfony 6.4 neu geschrieben haben, ohne Ausfall und ohne Positionsverluste.

14 Min. Lesezeit

Der Inhaber eines Möbelshops kommt mit einem Problem, das viele kennen: Der Shop läuft, verkauft, hat gute Google-Positionen — und steht auf Code, den niemand anzufassen wagt. Zwölf Jahre Custom Development auf PHP 5.4, ein Programmierer, der sich noch erinnert, warum bestimmte Dinge so und nicht anders funktionieren, und eine immer länger werdende Liste von Funktionen, die man „besser nicht anrührt". Das ist keine Geschichte über veraltete Technologie. Es ist eine Geschichte über geschäftliches Risiko, das im Code eingefroren ist.

Im Folgenden nehmen wir die Migration von Bogart Meble auseinander — eines Shops, der von Legacy-PHP auf Symfony 6.4 gewechselt ist, ohne einen einzigen Tag Ausfall und ohne Positionsverluste. Wir zeigen den Ausgangspunkt (technische Schulden), den Grund, warum wir ein Neuschreiben „auf einmal" verworfen haben, die Wahl des Stacks, einen Sechzehn-Wochen-Plan in vier Phasen sowie das, was sich real verändert hat. Ohne Marketing — mit der Mechanik, die man auf den eigenen Shop übertragen kann.

Der Ausgangspunkt: zwölf Jahre technische Schulden

Wir haben mit einem Audit des Bestehenden begonnen. Das Bild war typisch für einen Shop, der über Jahre schneller gewachsen ist als sein technisches Fundament. Fünf Probleme, von denen jedes einzeln überlebbar ist, die zusammen aber einen Zustand erhöhten Risikos erzeugen.

  • PHP 5.4 nach dem Support-Ende. Fehlende Sicherheitspatches bedeuten eine offene Tür und ein wachsendes Kompatibilitätsproblem — Zahlungs-Gateways und Integrationen verlangen neuere Versionen, und jeder Monat vertieft die Schuld.

  • Custom-Framework ohne Dokumentation. Das Ganze verstand in der Praxis ein einziger Mensch. Das ist der klassische Bus-Faktor von eins: Krankheit, Urlaub oder das Ausscheiden einer Person lässt den Shop ohne echte Betreuung zurück.

  • Keine Tests. Jede Änderung war ein Roulette, weil die Kosten einer Regression unbekannt waren. Der Effekt ist immer derselbe — das Team ändert nichts mehr, um nichts kaputtzumachen, und die Entwicklung stirbt.

  • Monolithische Datenbank mit Abfragen von mehreren Sekunden. Das ist kein abstraktes Ingenieursproblem. Langsame Abfragen treffen unmittelbar die Core Web Vitals und über sie Konversion und Positionen.

  • Keine Möglichkeit der Integration mit einer modernen API. BaseLinker, ein neues Zahlungs-Gateway oder einen Marketplace anzubinden war praktisch undurchführbar. Der Shop konnte sich nicht in Richtungen entwickeln, die längst Marktstandard waren.

Keiner dieser Punkte wirft für sich allein das Geschäft um. Zusammen erzeugen sie eine Situation, in der Entwicklung immer mehr kostet und das Risiko mit jedem Monat wächst — bis eine kleine Störung existenziell werden kann. Das ist meist der Moment, in dem ein solcher Shop bei uns landet: nicht weil etwas kaputtgegangen ist, sondern weil der Inhaber ausgerechnet hat, was ihn eine Störung kosten würde, die niemand beheben kann.

Warum wir ein „Big-Bang"-Neuschreiben verworfen haben

Der verlockendste Weg ist meist der schlechteste: einen neuen Shop von null daneben zu schreiben und dann an einem Wochenende den Traffic vom alten auf den neuen umzuschalten. Es klingt sauber. In der Praxis ist das bei zwölf Jahren undokumentierter Logik das Herbeirufen einer Katastrophe.

Beim „Big-Bang"-Ansatz entsteht der gesamte Wert erst am Tag der Umschaltung — Monate Arbeit ohne jeden Effekt bis zum Flag Day. Wenn etwas schiefgeht, ist ein Rollback oft unmöglich, weil das alte System in der Zwischenzeit sein eigenes Leben geführt hat. Und das Gefährlichste ist: Von der Existenz eines Teils der Geschäftsregeln erfährt man erst, wenn ein Kunde einen Pfad anklickt, den niemand nachgebaut hat — also schon in der Produktion, vor den Augen der Käufer.

Wir haben das Muster Strangler Fig gewählt. Der Name kommt von der Würgefeige, die einen Baum umschlingt und schrittweise seinen Platz übernimmt. Genauso umschlingt das neue System das alte: Es übernimmt Funktionen Modul für Modul, bis der alte Code schließlich entfernt werden kann. Die Geschäftslogik haben wir in Symfony Stück für Stück nachgebaut, während der alte Shop die ganze Zeit lief und verkaufte. Jedes Modul erhielt Tests, bevor es echten Traffic übernahm, das Risiko war also über die Zeit verteilt und das Zurücknehmen einer Änderung lokal, auf der Ebene eines einzelnen Moduls und nicht des ganzen Shops.

Technisch hält das eine Routing-Schicht vor beiden Systemen zusammen. Sie entscheidet, welche Anfrage noch der alte Code bedient und welche das neue System bereits übernommen hat — zuerst gingen die risikoärmsten Pfade dorthin, zuletzt Checkout und Zahlungen. Für den Kunden und für Google bleibt die Adresse dieselbe; darunter ändert sich nur, welches System antwortet. Dadurch ist das Umschalten des Traffics schrittweise und umkehrbar: Verhält sich ein neues Modul anders als vorgesehen, gehen wir auf den alten Pfad zurück, ohne den Rest des Shops zu berühren.

Kriterium „Big Bang" (Neuschreiben auf einmal) Strangler Fig (Modul für Modul) Risiko am Umschalttag Extrem — alles oder nichts Verteilt — jedes Modul separat Rollback Oft unmöglich Lokal, auf Modulebene Undokumentierte Logik Kommt nach dem Launch beim Kunden zum Vorschein Kommt unterwegs zum Vorschein, Modul für Modul Wert vor der Umschaltung Null bis zum „Flag Day" Wächst mit jedem Modul Test an echtem Traffic Keiner — der erste Traffic ist die Feuertaufe Parallel Run an Produktions-Traffic Wann sinnvoll Kleiner, gut verstandener Code Großer, alter, schlecht dokumentierter Monolith

Bei zwölf Jahren undokumentiertem Code haben wir nicht gefragt, ob ein „Big Bang" gelingt. Wir haben gefragt, was passiert, wenn er nicht gelingt — und diese Antwort wollte niemand hören.

Der neue Stack — und warum genau dieser

Die Wahl der Technologie ist keine Frage der Mode, sondern der Konsequenzen, mit denen der Kunde jahrelang lebt. Jedes Element des Stacks sollte ein konkretes Problem aus der Liste der technischen Schulden lösen und nicht ein weiteres hinzufügen.

Schicht Technologie Wofür (Mechanismus) Backend Symfony 6.4 Ein ausgereiftes Framework mit langem Support und riesigem Ökosystem — Ende des Bus-Faktors von eins API API Platform REST und GraphQL sofort verfügbar — ein fertiges Fundament für Integrationen (BaseLinker, Marketplaces) und ein Headless-Frontend in der Zukunft Datenbank MariaDB 10.11 Eine stabile, gut indexierte Datenbank anstelle eines Monolithen mit Abfragen von mehreren Sekunden Cache und Sessions Redis Der Hauptheld beim Rückgang der Ladezeit — entlastet die Datenbank auf den häufigsten Pfaden Frontend Twig + Stimulus Serverseitiges Rendering, leicht und SEO-freundlich, ohne das Gewicht einer vollen SPA Suche Elasticsearch Schnelle und treffsichere Suche über einen großen Möbelkatalog, den eine relationale Datenbank nicht trägt Warteschlangen RabbitMQ Asynchrone Aufgaben (Mails, Integrationen, Neuberechnungen) außerhalb der Nutzeranfrage — die Seite wartet nicht Deployments Docker + GitLab CI Wiederholbare, getestete Deploys — Ende von „bei mir läuft's" und manuellem Hochladen von Dateien

Eine Sache sollte man ehrlich festhalten: Symfony ist dasselbe Fundament, auf dem Sylius steht. Custom auf Symfony ist dort sinnvoll, wo die Geschäftslogik dick und untypisch ist — so wie bei Bogart. Wenn ein Shop überwiegend in die Standards der Plattform passt, empfehlen wir häufiger fertiges Sylius als einen Bau von null, weil das schneller und günstiger ist. Die Wahl „Custom gegen fertige Plattform" ist eine Entscheidung, die wir in der Phase des Audits treffen und nicht während des Projekts.

Sechzehn Wochen in vier Phasen

Das gesamte Projekt war in vier Phasen über sechzehn Wochen abgeschlossen — rund vier Monate. Das war der Zeitplan von Bogart; bei größerem Katalog oder dickerer Logik kann so ein Szenario deutlich länger dauern. Wichtiger als die Zahl der Wochen ist die Reihenfolge der Schritte.

Phase 1 (Wochen 1–3): Audit und Reverse Engineering

Das wichtigste Produkt dieser Phase war nicht Code, sondern Dokumentation. Das Team rekonstruierte die im alten Code verborgene Geschäftslogik und kartierte URL- und Datenbankstruktur. Es entstand auch ein Dokument, das wir in jeder unserer Migrationen „Was wir unbedingt erhalten" nennen — eine Liste von einigen Hundert der wichtigsten URLs im Hinblick auf Traffic und Konversion. Sie markiert die Grenzen, die in den weiteren Phasen nicht überschritten werden dürfen.

Das Rekonstruieren undokumentierter Logik ist Detektivarbeit, kein Raten. Man liest die Codepfade dort, wo Entscheidungen fallen — wie Rabatte, Lieferkosten und Variantenverfügbarkeit berechnet werden — und konfrontiert sie mit dem, was real in der Produktion passiert, an echten Bestellungen. Wo nur noch ein Mensch übrig war, der sich erinnerte, „warum das genau so funktioniert", landete sein Wissen auf Papier, statt weiter ausschließlich in seinem Kopf zu existieren. Diese Phase begleicht den größten Teil der Schuld noch vor der ersten Zeile neuen Codes — denn sie verwandelt einen Bus-Faktor von eins in gemeinsames, festgehaltenes Teamwissen.

Phase 2 (Wochen 4–12): Neuschreiben der Module

Das Herz des Projekts. Katalog, Warenkorb, Checkout, Kundenkonto und Integrationen — Modul für Modul nachgebaut, jedes mit automatischen Tests, die die kritischen Kaufpfade abdecken. Die Tests waren keine Dekoration: Sie erlaubten es, weitere Module sicher zu übergeben, weil sie eine eindeutige Antwort auf die Frage gaben, ob die neue Version genau das tut, was die alte tat.

Datenmigration: wo stille Migrationen sterben

Die Übertragung von Katalog, historischen Bestellungen und Kunden ist der Moment, in dem man am leichtesten etwas unwiederbringlich verliert. Wir führten die Datenmigration mit Integritätsvalidierung durch — nach jedem Durchgang wurden die Daten mit der Quelle verglichen, damit kein Datensatz verschwindet oder still verfälscht wird. Ergebnis: keine verlorenen Bestellungen im gesamten Prozess. Bei Bestellungen ist das keine „nette Metrik", sondern eine Randbedingung — eine verlorene Bestellung bedeutet einen echten Kunden und einen echten Streit.

301 für tausende URLs — die Schicht, auf der Migrationen Traffic verlieren

Die häufigste Ursache für Positionsverluste nach einer Migration ist nicht die Technologie, sondern verwaiste Adressen. Deshalb haben wir ein vollständiges Mapping der 301-Weiterleitungen für alle URLs vorbereitet — eins zu eins, von der alten Struktur auf die neue. Das entscheidende Detail: Die Karte validierte ein SEO-Spezialist und nicht nur ein Programmierer, und sie war noch vor dem Go-live in die Regressionstests importiert, um jeden gebrochenen Link zu finden, bevor Google ihn sieht.

Dieser Ansatz hat Deckung in Zahlen. Der Branchen-Benchmark für den Rückgang des organischen Traffics nach einer Migration liegt meist bei 15–30%; in unseren Projekten bleiben wir unter 3%. Bei Bogart bedeutete das nahezu alle erhaltenen Positionen — mehr dazu im Abschnitt mit den Ergebnissen.

Migrationen verlieren Traffic nicht an komplizierten Code. Sie verlieren ihn an verwaiste Adressen — deshalb validiert bei uns ein SEO-Spezialist die Weiterleitungskarte und nicht nur ein Programmierer.

Phasen 3–4 (Wochen 13–16): Parallel Run und Umschaltung ohne Ausfall

Rund zwei Wochen liefen das alte und das neue System parallel. Der Parallel Run erlaubte, den neuen Shop an echtem Produktions-Traffic zu testen — einschließlich Lasttests —, bevor irgendetwas vom neuen Code abhing. Der Sinn dieser Etappe ist einfach: Statt zu glauben, dass sich das neue System korrekt verhält, prüfen Sie es an echtem Traffic, unter Bedingungen, die keine Testumgebung nachbildet. Unterschiede in den Ergebnissen zwischen altem und neuem System — Preis im Warenkorb, Lieferkosten, Verfügbarkeit einer Variante — kommen dann noch bei laufendem alten Shop zum Vorschein und nicht erst, nachdem ein Kunde sie meldet. Erst als das neue System echten Traffic getragen hatte und sich nicht mehr dort vom alten unterschied, wo es das nicht sollte, gingen wir zur Umschaltung über.

Die Umschaltung selbst haben wir außerhalb der Spitzenzeiten gemacht, mit fertigem Rückzugsplan. Im Fall von Bogart war der Cutover so anspruchsvoll, dass wir ihn zeitlich auf drei Etappen verteilt haben — jede in einem Fenster mit geringem Traffic, und keine erforderte das Abschalten des Shops. Die ersten Stunden nach jeder Etappe bedeuteten verstärktes Monitoring, und die SEO-Positionen sowie das Systemverhalten beobachteten wir noch lange nach dem Start. Kunden konnten die ganze Zeit normal einkaufen.

Ergebnisse — und wie man sie liest

Vier Effekte beschreiben das Ergebnis der Migration am besten. Wir nennen sie als Projektergebnis, aber betrachten wir sie als Illustration des Mechanismus und nicht als Garantie — jeder Shop startet von einem anderen Punkt.

  • Ladezeit der Produktseite: von mehreren Sekunden auf unter eine Sekunde. Eine mehrfache Beschleunigung, vor allem dank Redis und dem Neuschreiben der schwersten Abfragen. Das ist der direkteste Effekt — und der Ausgangspunkt für die übrigen.

  • Deutlicher Anstieg der Konversion innerhalb von drei Monaten. Hier ist Ehrlichkeit wichtiger als eine eindrucksvolle Zahl: Zum Anstieg trugen sowohl der neue Checkout als auch die Geschwindigkeit selbst bei. Wir schreiben nicht alles einer Variablen zu — aber die Richtung stimmt mit dem überein, was die Daten der gesamten Branche zeigen.

  • Nahezu alle Google-Positionen erhalten. Das Ergebnis des 301-Mappings und der Beibehaltung derselben semantischen Struktur. Bei einem Branchen-Benchmark von 15–30% Rückgang ist das der Unterschied zwischen einem ruhigen Start und Monaten des Traffic-Wiederaufbaus.

  • Keine verlorenen Bestellungen und kein Ausfall. Das Resultat von Parallel Run, etappenweiser Umschaltung und Validierung der Datenintegrität.

Warum schlägt Geschwindigkeit so stark auf den Umsatz durch? Weil die Kosten einer langsamen Seite messbar sind. In unseren Beobachtungen korreliert ein TTFB über 500 ms mit einem Konversionsrückgang von 5–10% — deshalb zielen wir unter 200 ms. Von mehreren Sekunden auf unter eine Sekunde zu kommen ist keine Kosmetik, sondern die Beseitigung einer realen Verkaufsbremse. Das Team auf Kundenseite hatte diese Migration jahrelang gefürchtet; die häufigste Reaktion nach der Umschaltung lief auf Ungläubigkeit hinaus, dass es ohne Ausfall und ohne Einbruch bei Google möglich war.

Es gibt noch ein Ergebnis, das in den Zahlen des Starttags nicht sichtbar ist und langfristig am meisten verändert: Der Shop kann sich wieder entwickeln. Was früher undurchführbar war — BaseLinker anbinden, weitere Marketplaces betreten, ein neues Zahlungs-Gateway ergänzen — wird auf einem Stack mit API Platform zu einer gewöhnlichen Aufgabe und nicht zu einem Projekt mit dem Risiko, das Ganze zu beschädigen. Die Migration war also kein Ziel an sich. Sie war die Bedingung dafür, dass alle nachfolgenden Verbesserungen überhaupt möglich wurden.

Wann sich so eine Migration lohnt — und wann nicht

Legacy-PHP auf Symfony neu zu schreiben ist das längste, teuerste und schwierigste Migrationsszenario, das wir begleiten. Nicht jeder Shop braucht es — und wir sagen es als Erste.

Es ist sinnvoll, wenn:

  • die Geschäftslogik dick und untypisch ist — Konfiguratoren, nicht standardmäßige Preisbildung, Regeln, die keine fertige Plattform abbildet;

  • organischer Traffic der Hauptkanal ist, die Kosten eines Positionsverlusts also in Hunderttausenden zählen;

  • die Größe des Shops bedeutet, dass ein Ausfall oder ein Sichtbarkeitsverlust echte Verluste sind — und denken wir daran, dass eine falsche Dienstleisterwahl einen Mid-Market-Shop im ersten Jahr 500.000 PLN und mehr kosten kann.

Es ist nicht sinnvoll, wenn:

  • der Shop zu 90% ein Standardkatalog mit Warenkorb ist — dann ist eine fertige Plattform (Shopify Plus oder Sylius) günstiger und schneller als ein Bau von null;

  • es kein Budget und keine Zeit für ein mehrmonatiges Projekt gibt — dann existieren viele leichtere Migrationswege;

  • niemand auf Unternehmensseite die alten Regeln versteht und niemand sie rekonstruieren will — ohne dieses Wissen oder die Bereitschaft, es zu heben, wächst das Risiko unabhängig von der Technologie.

Mit anderen Worten: Strangler Fig und Custom auf Symfony sind ein Werkzeug für ein konkretes Problem und keine Standardantwort. Unsere anderen Projekte — etwa die Migration von Topeshop auf Shopify Plus oder von KMK Klinkier auf Sylius — zeigen, dass für viele Shops die richtige Antwort eine fertige Plattform ist und nicht das Neuschreiben von Code.

Fazit

Die Migration von Bogart Meble zeigt, dass zwölf Jahre technische Schulden nicht mit einem riskanten Sprung beglichen werden müssen. Strangler Fig erlaubte, die Logik Modul für Modul nachzubauen, Parallel Run und etappenweise Umschaltung beseitigten den Ausfall, und ein von SEO validiertes 301-Mapping schützte die Positionen. Die wichtigste Lehre ist jedoch strategisch: Eine gute Migration beginnt mit einer Entscheidung über die Reihenfolge — zuerst, was wir unbedingt schützen, und erst dann, wie wir es neu schreiben.

Wenn Sie auf Code stehen, den Sie anzufassen fürchten, beginnen Sie mit der Erkundung des Terrains und nicht mit der Wahl der Technologie. Sehen Sie, wie wir an die Shop-Migration herangehen, oder schreiben Sie uns über unsere Kontaktseite — wir sehen uns Ihren Fall an und sagen ehrlich, ob ein Neuschreiben Sinn hat oder eine fertige Plattform der bessere Weg ist.

Tags: #migracja #Symfony #legacy #case study
Teilen: 𝕏 in f

Bleiben Sie auf dem Laufenden

Neue Artikel über e-commerce und skalowaniu

Einmal pro Woche, freitags. Kein Spam, kein Blabla. Nur praktisches Wissen von Menschen, die Shops mit über 10 Mio. PLN GMV pro Jahr migriert haben.

🔒 DSGVO-konform. Abmeldung mit 1 Klick in der Fußzeile jeder E-Mail.