Ein Symfony-Upgrade scheitert selten an Symfony selbst. Der Kern ist gut getestet, die Upgrade-Pfade sind dokumentiert, die Deprecations werden Version für Version sauber angekündigt. Was Upgrades in der Praxis blockiert, sind die Drittanbieter-Bundles: das JWT-Bundle, das seit drei Jahren nicht mehr angefasst wurde, das API-Platform in einer Version, die noch auf Annotations statt Attributes setzt, oder das eine interne Bundle, das mal jemand geschrieben hat, der die Firma längst verlassen hat.
Die gute Nachricht vorweg: Eine Anwendung, die seit Jahren stabil läuft, ist kein Sanierungsfall. Sie ist der Beweis, dass die Fachlogik funktioniert. Das Upgrade-Problem sitzt fast immer im Code drumherum — und die Bundle-Landschaft ist genau dieser Code drumherum. Wer die Symfony Bundle Kompatibilität vor dem Upgrade sauber prüft, verwandelt ein diffuses Risiko in eine abarbeitbare Liste.
Warum die Symfony Bundle Kompatibilität der eigentliche Engpass ist
Symfony gibt Ihnen mit den semantischen Versionsnummern ein klares Versprechen: Innerhalb eines Major-Zweigs (etwa 6.x) bleibt die öffentliche API stabil, Breaking Changes kommen erst mit dem nächsten Major (7.0). Deprecations werden vorher als Log-Meldungen ausgegeben, sodass Sie Zeit zum Anpassen haben.
Bundles halten sich nicht zwingend an dieses Versprechen — jedenfalls nicht synchron zu Ihrem Zeitplan. Jedes Bundle hat einen eigenen Maintainer, einen eigenen Release-Rhythmus und eine eigene Support-Matrix. Das führt zu drei typischen Bruchstellen:
- Version-Constraints in der
composer.json: Das Bundle erlaubt in seinerrequire-Sektion schlicht keinsymfony/*: ^7.0. Composer verweigert dann die Installation, bevor überhaupt Code läuft. - Interne API-Nutzung: Das Bundle greift auf Symfony-Klassen zu, die als
@internalmarkiert oder in der neuen Version entfernt wurden. Composer lässt es durch, aber zur Laufzeit fliegt einFatal error. - Verlassene Bundles: Es gibt schlicht keine kompatible Version, weil das Projekt seit dem letzten Release keinen Commit mehr gesehen hat.
Diese drei Fälle brauchen unterschiedliche Antworten. Deshalb lohnt es sich, sie vor dem Upgrade sauber auseinanderzuhalten, statt blind composer update aufzurufen und zu hoffen.
Schritt 1: Die Bundle-Landschaft inventarisieren
Bevor Sie irgendeine Version ändern, verschaffen Sie sich einen Überblick. composer show listet alle installierten Pakete mit ihren Versionen. Interessant sind für den Anfang nur die, die tatsächlich ein Symfony-Bundle registrieren — werfen Sie einen Blick in Ihre config/bundles.php, dort steht die verbindliche Liste der aktiven Bundles.
Für jedes dieser Bundles klären Sie zwei Dinge: Wird es noch gepflegt, und welche Symfony-Versionen unterstützt es? Die verlässlichste Quelle ist nicht die Marketing-Seite, sondern die composer.json des Bundles im jeweiligen Repository. Der require-Block auf symfony/framework-bundle oder die einzelnen Komponenten sagt Ihnen unmissverständlich, welche Major-Version erlaubt ist. Ein Blick auf das Datum des letzten Releases und die offenen Issues verrät den Pflegezustand.
Schritt 2: Composer den Konflikt trocken auflösen lassen
Der schnellste Weg, harte Versionskonflikte zu finden, ist der Trockenlauf. Passen Sie in Ihrer composer.json die Symfony-Constraints auf die Zielversion an und lassen Sie Composer rechnen, ohne etwas zu installieren:
composer update "symfony/*" --with-all-dependencies --dry-run
Composer meldet jetzt jeden Konflikt namentlich: welches Bundle welche Symfony-Version blockiert und in welcher Version es das tut. Das ist Gold wert, weil Sie damit die harte Kategorie ("geht mit Composer gar nicht erst") vollständig abdecken, bevor Sie eine Zeile Anwendungscode anfassen. Für einzelne Kandidaten hilft composer why-not symfony/framework-bundle 7.0, um die genaue Blockade-Kette zu sehen.
Wichtig ist die Reihenfolge: Aktualisieren Sie zuerst die Bundles auf ihre neuesten, noch mit Ihrer aktuellen Symfony-Version kompatiblen Stände. Oft haben die Maintainer die Symfony-7-Unterstützung in einem Minor-Release nachgezogen, ohne selbst Breaking Changes einzuführen. Danach ziehen Sie Symfony hoch. Dieser Zwischenschritt entzerrt das Upgrade erheblich.
Schritt 3: Deprecations sichtbar machen, bevor sie zu Fehlern werden
Composer prüft nur Versionsnummern. Ob ein Bundle intern noch alte, deprecated Symfony-APIs benutzt, sehen Sie erst zur Laufzeit. Genau dafür gibt es zwei Werkzeuge, die Sie bereits vor dem Upgrade einsetzen sollten, während Sie noch auf der alten Version sind:
- Der Symfony Deprecation-Header im Profiler: Im dev-Modus zählt die Debug-Toolbar die ausgelösten Deprecations pro Request. Viele davon stammen nicht aus Ihrem Code, sondern aus Bundles — und verraten Ihnen, welche Bundles beim nächsten Major stolpern werden.
- Die PHPUnit-Bridge: Das Paket
symfony/phpunit-bridgesammelt während der Testläufe alle Deprecation-Meldungen und gibt am Ende eine gruppierte Zusammenfassung aus. Läuft Ihre Testsuite grün, aber mit dutzenden Deprecations aus einem bestimmten Bundle, wissen Sie, wo nach dem Upgrade die Arbeit wartet.
Dieser Schritt setzt eine funktionierende Testabdeckung voraus. Fehlt sie — was bei Legacy-Anwendungen die Regel ist — lohnt es sich, wenigstens die kritischen Pfade (Login, Bezahlvorgang, Kernprozesse) mit einigen Funktionstests abzusichern, bevor Sie das Upgrade angehen. Diese Tests sind ohnehin die Versicherung, die Ihnen sagt, ob die Anwendung nach dem Upgrade noch das tut, was sie soll.
Schritt 4: Was tun, wenn ein Bundle nicht mitzieht?
Nicht jedes Bundle wird eine kompatible Version haben. Für diesen Fall gibt es eine klare Abstufung, von der günstigsten zur teuersten Option:
- Warten oder Branch nutzen: Oft existiert bereits ein Pull Request oder ein
dev-Branch mit der neuen Kompatibilität. Ein temporäres Repository-Constraint auf diesen Branch überbrückt die Lücke. - Fork und Patch: Ist das Bundle klein und der nötige Eingriff überschaubar, patchen Sie es selbst — idealerweise mit
cweagans/composer-patches, damit der Patch versioniert und nachvollziehbar bleibt. Der Pull Request zurück ins Original erspart Ihnen die Pflege beim nächsten Mal. - Ersetzen: Manche Bundles lösen ein Problem, das Symfony inzwischen selbst abdeckt. Ein Bundle für Umgebungsvariablen oder simples Caching ist heute oft überflüssig.
- Eigenständig herauslösen: Bietet das Bundle nur eine dünne Funktion, kann es günstiger sein, diese als eigenen Service in Ihre Anwendung zu ziehen, statt eine externe Abhängigkeit weiterzuschleppen.
Neuentwicklung der ganzen Anwendung, nur weil ein Bundle klemmt, steht bewusst nicht auf dieser Liste. Sie ist fast immer die teuerste und riskanteste Option — und in aller Regel unnötig, denn das eigentliche Problem ist ein einzelnes Paket, nicht Ihre über Jahre erprobte Fachlogik.
Ehrlicher Praxis-Tipp
Machen Sie das Upgrade in zwei getrennten Schritten und niemals in einem großen Commit: erst alle Bundles auf den neuesten Stand innerhalb Ihrer aktuellen Symfony-Version, dann Symfony selbst. Jeder Schritt bekommt einen eigenen grünen Testlauf. So wissen Sie im Fehlerfall exakt, welche Änderung die Ursache war, statt in einem Wust aus fünfzig aktualisierten Paketen zu suchen. Das kostet einen Nachmittag mehr Vorbereitung und spart Ihnen oft mehrere Tage Fehlersuche.
Wenn Sie vor genau dieser Bundle-Landschaft stehen und nicht abschätzen können, welche Pakete Sie ausbremsen werden, ist das einer der Bereiche, in denen ich mit LegacyWerk regelmäßig unterstütze — mit einer nüchternen Bestandsaufnahme statt eines Rundumschlags.