Ein Symfony-Projekt, das seit fünf oder acht Jahren im Produktivbetrieb läuft, ist kein Sanierungsfall. Es ist der Beweis, dass die Fachlogik trägt. Das eigentliche Problem sitzt fast immer im Code drumherum — im Framework-Layer, der sich unter der Anwendung weiterbewegt hat, während die Fachlogik stillhielt. Genau hier setzen Symfony Deprecations an: Sie sind die Vorwarnung des Frameworks, dass ein bestimmter Weg im nächsten Major-Release verschwindet.
Wer diese Warnungen ignoriert, zahlt später doppelt: Beim Sprung von Symfony 5 auf 6 oder von 6 auf 7 schlägt dann alles auf einmal auf. Wer sie dagegen laufend abarbeitet, verwandelt den gefürchteten Major-Upgrade in ein fast langweiliges Routine-Ereignis. Das ist die gute Nachricht dieses Artikels.
Was Symfony Deprecations eigentlich sind
Eine Deprecation ist keine Exception und kein Fehler. Ihr Code funktioniert weiter. Symfony löst intern einen E_USER_DEPRECATED-Trigger aus, sobald Sie eine Funktion, eine Konfigurationsoption oder ein Verhalten nutzen, das in der nächsten Major-Version entfernt oder geändert wird. Das Framework hält sich strikt an Semantic Versioning: Innerhalb einer Major-Linie (etwa 6.x) bricht nichts, aber deprecated markierter Code kann im nächsten Major (7.0) ersatzlos verschwinden.
Das bedeutet praktisch: Die Menge Ihrer offenen Deprecations ist ein direktes Maß für die Distanz zwischen Ihrem Code und der nächsten Framework-Generation. Null Deprecations auf der aktuellen Minor-Version heißt in der Regel: Der nächste Major-Sprung ist technisch trivial.
Symfony Deprecations sichtbar machen
Der erste Schritt ist immer Sichtbarkeit. Die meisten Deprecations laufen im Alltag unsichtbar durch, weil niemand ins richtige Log schaut. Symfony bietet mehrere Ebenen, um sie ans Licht zu holen.
Im Profiler und in der Web Debug Toolbar: Im Dev-Modus zeigt die Toolbar am unteren Bildschirmrand einen Zähler für Deprecations pro Request. Ein Klick öffnet die vollständige Liste mit Stacktrace. Das ist der schnellste Einstieg, deckt aber nur die Pfade ab, die Sie im Browser tatsächlich durchklicken.
Im Log: Deprecations landen im deprecation-Channel von Monolog. Prüfen Sie, ob Ihre monolog.yaml diesen Channel überhaupt schreibt — in vielen Legacy-Projekten wird er stillschweigend verworfen. Ein separater Handler für den deprecation-Channel macht die Warnungen dauerhaft nachvollziehbar.
In den Tests — der wichtigste Hebel: Die symfony/phpunit-bridge sammelt während der Testläufe alle ausgelösten Deprecations ein und gibt am Ende eine gruppierte Zusammenfassung aus. Sie unterscheidet dabei sauber zwischen direct (Ihr eigener Code ruft deprecated Symfony-API auf) und indirect (eine Ihrer Abhängigkeiten tut es). Diese Unterscheidung ist Gold wert, weil sie Ihnen sagt, was Sie selbst beheben können und was Sie an einem Paket-Update festmachen müssen.
Vom Rauschen zur Prioritätenliste
Wenn Sie zum ersten Mal die Bridge über eine gewachsene Testsuite laufen lassen, sehen Sie oft hunderte Meldungen. Das ist normal und kein Grund zur Panik. Entscheidend ist, sie zu sortieren, statt sie zu zählen.
Über die Umgebungsvariable SYMFONY_DEPRECATIONS_HELPER steuern Sie das Verhalten der Bridge. Mit max[direct]=0 lassen Sie die Testsuite fehlschlagen, sobald Ihr eigener Code eine neue Deprecation auslöst — ohne dass indirekte Meldungen aus Fremdpaketen den Build blockieren. Das ist die pragmatische Trennlinie: Ihren Code halten Sie sauber, für die Abhängigkeiten warten Sie auf deren Updates.
Sortieren Sie die Restmenge nach drei Kriterien:
- Häufigkeit: Eine Deprecation, die 200-mal auftritt, stammt fast immer aus einer einzigen zentralen Stelle — einer Basisklasse, einem Trait, einem Service. Ein Fix, 200 Meldungen weg.
- Direct vor indirect: Beheben Sie zuerst, was in Ihrer Hand liegt. Indirekte Deprecations verschwinden oft von selbst mit dem nächsten
composer update. - Konfiguration vor Code: Viele Deprecations betreffen YAML- oder XML-Konfiguration (veraltete Bundle-Optionen, alte Service-Definitionen). Die sind meist schnell und risikoarm zu ändern.
Beseitigen ohne die Fachlogik anzufassen
Der entscheidende Punkt bei der Arbeit an Deprecations: Sie ändern in aller Regel nur den Framework-Kontakt, nicht Ihre Geschäftsregeln. Ein typisches Beispiel ist der Wechsel von Annotations zu PHP-Attributen bei Doctrine-Entities oder Routen. Die Semantik bleibt identisch, nur die Syntax wandert von einem Kommentarblock in echte Sprachkonstrukte. Die Berechnung im Service dahinter bleibt Zeichen für Zeichen dieselbe.
Für genau solche mechanischen Umschreibungen lohnt sich Rector. Mit den Symfony-Rule-Sets (rector/rector plus rector/rector-symfony) automatisieren Sie einen großen Teil der Migrationen — Attribut-Umstellung, geänderte Methodensignaturen, umbenannte Konstanten. Wichtig ist die Disziplin, die Rector Ihnen aufzwingt: Set-Listen einzeln laufen lassen, Diff prüfen, Tests grün, committen. Ein Set pro Commit. Wer alles auf einmal durchlaufen lässt, verliert die Nachvollziehbarkeit und damit die Fähigkeit, einen einzelnen Fehler sauber zurückzurollen.
Und genau hier zeigt sich der Wert einer laufenden Anwendung: Ihre Testsuite — auch eine unvollständige — ist das Sicherheitsnetz, das jede dieser Änderungen absichert. Fehlt sie, schreiben Sie vor der Deprecation-Arbeit erst ein paar grobe Charakterisierungs-Tests um die wichtigsten Endpunkte. Das ist billiger und ehrlicher als jede Neuentwicklung, die dieselbe Fachlogik noch einmal von Hand nachbauen müsste.
Deprecations dauerhaft draußen halten
Deprecations einmal auf null zu bringen ist Arbeit — sie unten zu halten ist Gewohnheit. Verankern Sie die phpunit-bridge mit max[direct]=0 in Ihrer CI-Pipeline. Ab dann kann kein Merge mehr eine neue eigene Deprecation einschleusen, ohne dass der Build rot wird. Kombiniert mit regelmäßigen, kleinen composer update-Läufen bleibt die indirekte Menge ebenfalls im Griff.
Der Effekt ist strategisch: Sie entkoppeln die Framework-Wartung von der großen, angstbesetzten Migration. Statt alle drei Jahre ein Wochenende lang zu bangen, ob Symfony 7 überhaupt startet, arbeiten Sie kontinuierlich in Minuten-Häppchen. Der Major-Sprung selbst reduziert sich dann meist auf das Anheben der Versionsnummer in der composer.json und ein paar wenige echte Bruchstellen.
Deprecations sind keine Schuld, die man abträgt. Sie sind ein Frühwarnsystem, das Ihnen kostenlos sagt, wo Ihr Code und das Framework auseinanderdriften.
Ehrlicher Praxis-Tipp zum Schluss
Fangen Sie nicht mit dem Ziel „null Deprecations" an — das lähmt. Fangen Sie mit einem einzigen Testlauf über die Bridge an, notieren Sie die drei häufigsten Meldungen, und beheben Sie diese eine Woche lang. Meistens ist der zentrale Fix schon nach zwei Stunden erledigt und räumt die halbe Liste ab. Der Rest folgt der Verhältnismäßigkeit: Was oft auftritt und in Ihrem Code liegt, zuerst; der Long-Tail aus Fremdpaketen wartet auf das nächste Update.
Wenn Ihr System zu groß, zu ungetestet oder zu geschäftskritisch ist, um diesen Weg allein zu gehen, unterstützt LegacyWerk genau bei solchen Symfony-Migrationen — mit dem Grundsatz, die bewährte Fachlogik zu erhalten statt sie riskant neu zu bauen.