Verlassenes Symfony-Projekt wiederbeleben: Ein Recovery-Playbook

#Symfony-Projekt wiederbeleben
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Es beginnt mit einem Support-Ticket. Die App wirft beim Checkout einen 500-Fehler. Du leitest es an den Entwickler weiter. Es gibt keinen Entwickler - er ist vor acht Monaten gegangen. Der Backup-Entwickler schaut sich das an, öffnet das Projekt in seiner IDE und schließt es wieder. "Ich kenne diesen Codebase eigentlich nicht", sagt er.

Dieses Szenario wiederholt sich jede Woche in mittelständischen Unternehmen. Eine maßgefertigte Symfony-Applikation wurde vor Jahren von einer einzigen Person gebaut, die jeden Winkel davon verstand. Diese Person hat das Unternehmen verlassen. Niemand anderes wurde eingearbeitet. Die Dokumentation, falls sie überhaupt existiert, ist eine einzige README-Datei aus dem Jahr 2021, die dir erklärt, wie man das Projekt auf einem Mac mit Catalina installiert. Die App stürzt wöchentlich ab, niemand traut sich, sie anzufassen, und das Unternehmen ist im Wesentlichen Geisel eines Codebases, den es nicht mehr versteht.

Wenn du gerade die Schlüssel zu dieser Applikation hältst, ist dieser Beitrag ein schrittweises Recovery-Playbook. Kein Rewrite, kein magisches Refactoring - ein methodischer Wiederherstellungsprozess, der das Projekt stabil, verständlich und wieder einstellbar macht, ohne auf einen Big-Bang-Rebuild zu setzen.

Warum Wiederbeleben besser ist als Neuschreiben (meistens)

Der Instinkt angesichts eines unverständlichen Legacy-Symfony-Codebases ist, ihn wegzuwerfen und von vorne zu beginnen. Rewrites sind verlockend, weil sie einen sauberen Start versprechen. Sie liefern ihn selten. Ein Rewrite bedeutet monatelanges paralleles Betreiben, versteckte Edge Cases, die nur das alte System verstanden hat, und einen harten Cutover-Termin, gegen den das Unternehmen ankämpfen wird, bis es gefährlich spät ist.

Die vorhandene Codebase wiederzubeleben - zu stabilisieren, zu dokumentieren und schrittweise zu modernisieren - liefert fast immer schneller Mehrwert bei geringerem Risiko. Das Ziel in Phase eins ist kein schöner Code. Es ist sicherer Code: Code, den du deployen kannst, ohne den Atem anzuhalten, Code, den du debuggen kannst, wenn um 2 Uhr nachts etwas kaputt geht, Code, den du einem neuen Entwickler übergeben kannst, ohne einen Monat Einzelunterricht.

Phase 1: Eine sichere Baseline etablieren

Bevor du eine einzige Zeile Logik anfasst, musst du verstehen, was du hast. Führe folgende Analyse durch, bevor du irgendetwas anderes tust.

Lies jede Datei im Projektstamm. composer.json, .env.example, docker-compose.yml falls vorhanden, Makefile, alle Shell-Skripte. Diese Dateien verraten dir, wie der ursprüngliche Entwickler erwartete, dass das Projekt läuft. Sie sind oft falsch, aber sie sind dein bester Ausgangspunkt.

Kartiere die Entry Points. In einer Symfony-Applikation sind Entry Points Routes. Führe php bin/console debug:router aus und speichere die Ausgabe. Dies ist deine Karte von allem, was die Applikation tun soll. Wenn Routen nicht geladen werden können, ist das dein erster Bug.

Erfasse das Fehlerprotokoll. Verschaffe dir Zugang zum Produktionsfehlerprotokoll - ob das ein Sentry-Projekt, ein Papertrail-Stream oder eine rohe var/log/prod.log-Datei ist. Repariere noch nichts. Lies es einfach. Die Fehler, die am häufigsten wiederkehren, sind dort, wo die Applikation tatsächlich bricht, und sie werden deine frühe Arbeit fokussieren.

Friere die Umgebung ein. Der ursprüngliche Entwickler hatte wahrscheinlich eine bestimmte PHP-Version, einen bestimmten Satz PHP-Erweiterungen und bestimmte Speicherlimits im Sinn. Überprüfe composer.json auf die require.php-Einschränkung und stelle sicher, dass deine Entwicklungsumgebung exakt damit übereinstimmt. Abweichungen zwischen lokalen und Produktions-PHP-Versionen sind eine häufige Quelle mysteriöser Fehler bei geerbten Projekten.

Dokumentiere, was du findest. Öffne eine RECOVERY.md im Projektstamm und beginne zu schreiben. Jede Annahme, die du aufdeckst, jede Problemumgehung, die du entdeckst, jede Umgebungsvariable, deren Zweck unklar ist - schreib es auf. Dieses Dokument ist der Beginn des institutionellen Wissens, das du wiederaufbaust.

Phase 2: Tests und CI wiederherstellen

Das Gefährlichste an einem verlassenen Codebase ist, dass niemand weiß, was kaputt geht, wenn man etwas ändert. Tests - auch unvollständige - geben dir ein Sicherheitsnetz. CI zum Laufen zu bringen gibt dem gesamten Team die Gewissheit, dass ihre Änderungen die Applikation nicht still und heimlich kaputt machen.

Prüfe zunächst die bestehende Test-Suite. Führe php bin/phpunit oder ./vendor/bin/phpunit aus und schau, was passiert. In geerbten Projekten findest du typischerweise einen von drei Zuständen: gar keine Tests, eine Suite, die seit Jahren nicht mehr ausgeführt wurde und sofort scheitert, oder eine Suite, die besteht, aber nur einen Bruchteil der Applikation abdeckt. Jeder Zustand erfordert eine andere Reaktion.

Wenn es keine Tests gibt, versuche nicht, umfassende Abdeckung zu schreiben, bevor du irgendetwas anderes tust - du wirst Monate damit verbringen, Tests für Code zu schreiben, den du gerade ändern wirst. Schreibe stattdessen Smoke-Tests für die am meisten besuchten Routen und die kritischsten Geschäftsabläufe (Zahlung, Authentifizierung, Kerndaten-Mutationen). Diese geben dir eine Regressions-Baseline, ohne eine vollständige Test-Prüfung zu erfordern.

Wenn Tests existieren, aber scheitern, repariere die scheiternden Tests, bevor du Applikationslogik anfasst. Die Fehler sind fast immer umgebungsbedingt (falsche Datenbankanmeldedaten, fehlende Umgebungsvariablen, veraltete PHPUnit-Konfiguration) und keine echten Regressionen. Repariere die Umgebung, nicht die Tests.

Richte CI vom ersten Tag an ein. Selbst ein GitHub-Actions-Workflow, der composer install und php bin/phpunit ausführt, ist besser als nichts. Der psychologische Effekt eines grünen CI-Badges ist erheblich: Entwickler beginnen dem Codebase mehr zu vertrauen, sobald eine Maschine ihre Arbeit überprüft. Unsere Web-Applikationsentwicklungs-Dienstleistungen beinhalten CI-Einrichtung immer als nicht verhandelbare Baseline.

Phase 3: Abhängigkeiten sichern

Geerbte Symfony-Projekte haben fast immer eines von zwei Abhängigkeitsproblemen: alles ist veraltet und niemand hat seit zwei Jahren composer update ausgeführt, oder die composer.json verwendet Wildcard-Versionseinschränkungen und die installierten Pakete sind auf jeder Umgebung subtil unterschiedlich.

Committe composer.lock in die Versionskontrolle, falls es noch nicht dort ist. Dies ist die einzeln wirkungsvollste Änderung, die du machen kannst, um ein Legacy-Projekt zu stabilisieren. Mit einer committeten Lockfile arbeiten jeder Entwickler und jeder CI-Lauf mit identischen Paketversionen. Ohne sie spielst du jedes Mal russisches Roulette, wenn jemand composer install auf einem neuen Rechner ausführt.

Führe noch kein composer update aus. Alle Abhängigkeiten auf einmal zu aktualisieren ist der schnellste Weg, eine Kaskade von Breaking Changes einzuführen, die du nicht isolieren kannst. Führe stattdessen composer outdated aus und kategorisiere, was du siehst. Sicherheitslücken zuerst (prüfe mit composer audit), dann Pakete mit großen Versionsprüngen, die Code-Änderungen erfordern, dann kleinere Updates. Arbeite sie eine Kategorie nach der anderen durch.

Überprüfe dein Symfony-Versions-Support-Fenster. Symfony veröffentlicht klare End-of-Life-Daten. Wenn du Symfony 4.4 betreibst, bist du auf dem letzten 4.x LTS, aber der Support endet im November 2026. Wenn du irgendetwas Älteres betreibst, läuft du bereits nicht unterstützte Software in Produktion. Zu verstehen, wo du im Support-Zeitrahmen stehst, gibt dir einen Migrationsplan - und unser Legacy-Code-Optimierungsservice kann dir helfen, diesen Plan zu erstellen.

Phase 4: Den Codebase verständlich machen

Ein Codebase, den nur eine Person navigieren kann, ist eine Verbindlichkeit, kein Vermögen. Das Ziel dieser Phase ist es, das Projekt für einen kompetenten PHP-Entwickler verständlich zu machen, der es noch nie gesehen hat - denn das ist es, was jede zukünftige Einstellung sein wird.

Dokumentiere die Architektur. Schreibe eine ARCHITECTURE.md, die die Hauptmodule, das Datenmodell und die nicht offensichtlichen Entscheidungen erklärt, die der ursprüngliche Entwickler getroffen hat. Wenn du eine Entscheidung nicht erklären kannst, kennzeichne sie explizit: "Diese Service-Klasse macht X. Wir wissen nicht, warum sie so gebaut wurde. Mit Vorsicht behandeln."

Benenne Dinge korrekt. Symfony-Projekte, die von einer einzelnen Person unter Druck gebaut wurden, haben oft Service-Namen, die in einer Person's Kopf Sinn ergaben, aber niemand sonst: AppBundle\Service\Helper, AppBundle\Manager\Processor. Benenne diese um, damit sie widerspiegeln, was sie tatsächlich tun. Dies ist risikoarme, hochwertige Arbeit, die sich jedes Mal auszahlt, wenn jemand den Code liest.

Extrahiere Inline-SQL und Geschäftslogik aus Controllern. In Legacy-Symfony-Projekten erledigen Controller häufig die Arbeit von Services: rohe SQL-Abfragen ausführen, Geschäftsregeln implementieren, externe APIs aufrufen. Das macht sie unmöglich zu testen und schwierig zu verstehen. Diese Logik in ordentlich benannte Service-Klassen zu verschieben ist das einzeln effektivste Refactoring, das du für das Entwicklerverständnis tun kannst. Arbeite es inkrementell durch - eine Controller-Methode pro Pull Request.

Phase 5: Das Projekt einstellbar machen

"Einstellbar" klingt vielleicht nach einem seltsamen Ziel für einen Codebase, aber es ist das praktischste. Wenn ein Projekt so verworren ist, dass kein kompetenter Entwickler daran arbeiten möchte, hast du ein strukturelles Geschäftsrisiko. Dein Ziel ist ein Codebase, bei dem ein Senior-PHP-Entwickler innerhalb eines Tages produktiv werden kann.

Die Signale, die ein Entwickler verwendet, um einen Codebase zu beurteilen, bevor er eine Stelle annimmt, sind vorhersehbar: Ist die Einrichtung dokumentiert? Funktioniert docker-compose up (oder das Äquivalent) tatsächlich? Gibt es Tests? Läuft CI? Sind die Abhängigkeiten einigermaßen aktuell? Ist der Code nach Symfony-Konventionen strukturiert - Services in src/Service, Entities in src/Entity, Events wo sie hingehören?

Ein Projekt, das diese Überprüfungen besteht, ist einstellbar. Ein Projekt, das die meisten davon nicht besteht, wird dir Entwicklerkandidaten verlieren, sobald diese ihre eigene Due Diligence durchführen, bevor sie ein Angebot annehmen. Wir haben dies mehr als einmal erlebt: Ein Unternehmen verbringt drei Monate damit, für eine Stelle zu rekrutieren, macht ein Angebot, und der Kandidat kehrt seine Entscheidung nach einem Code-Review-Tag um.

Die Nicht-Verhandelbaren vor dem nächsten Produktionsdeploy

Bevor du Recovery-Arbeit wieder in die Produktion mergst, validiere drei Dinge. Erstens: Führe die vollständige Test-Suite aus (auch wenn die Abdeckung noch partiell ist) und bestätige, dass alles besteht. Zweitens: Führe composer audit aus und bestätige, dass keine bekannten Sicherheitslücken in deinem Abhängigkeitsbaum verbleiben. Drittens: Mache einen manuellen Walkthrough der kritischen Nutzerflows - Checkout, Login, Datenexport - gegen eine Staging-Umgebung, die der Produktion so genau wie möglich entspricht.

Diese Überprüfungen nehmen weniger als eine Stunde in Anspruch. Sie zu überspringen ist der Grund, warum Stabilisierungsarbeit versehentlich neue Vorfälle einführt.

Wie lange dauert Recovery wirklich?

Für ein typisches mittelgroßes Symfony-Projekt - 50.000 bis 150.000 Codezeilen, keine aktiven Tests, läuft auf einer PHP-Version, die ein oder zwei Hauptversionen hinter ist - sieht ein realistischer Recovery-Zeitplan so aus: zwei bis drei Tage, um die sichere Baseline zu etablieren und zu dokumentieren, was existiert; eine Woche, um CI mit Smoke-Tests zum Laufen zu bringen; zwei bis vier Wochen, um Abhängigkeiten zu stabilisieren und die häufigsten Produktionsfehler zu beheben; und vier bis acht Wochen inkrementelles Refactoring, um den Codebase einstellbar zu machen.

Das ist ein sechs bis zwölf Wochen dauerndes Engagement, kein sechsmonatiges. Der inkrementelle Ansatz bedeutet, dass das Unternehmen ab Woche drei eine sicherere, stabilere Applikation sieht, anstatt auf eine große Enthüllung am Ende zu warten.


Wenn dein Team eine Symfony-Applikation geerbt hat, an die sich niemand traut, hat Wolf-Tech diesen Recovery-Prozess dutzende Male durchgeführt - von einfachen CMS-Ablösungen bis hin zu komplexen B2B-SaaS-Plattformen. Wir wissen, wo die Probleme in verlassenen PHP-Codebases begraben sind, und wir wissen, wie man sie wieder stabil und produktiv macht, ohne deinen Geschäftsbetrieb zu stören.

Melde dich unter hello@wolf-tech.io oder besuche wolf-tech.io, um deine Situation zu besprechen. Das erste Gespräch ist immer kostenlos.