Das nimmst du mit
- Cutover-Gates definieren
- Resttraffic erkennen
- Daten verifizieren
- Cleanup reversibel planen
Kapitelkompass
Legacy darf erst verschwinden, wenn Traffic, Daten, Betrieb und Rollback gegen explizite Gates geprüft wurden.
Das Neusystem bedient 100 Prozent Traffic, doch ein Batchjob liest noch aus der alten Datenbank.
Code, Daten und Infrastruktur getrennt abbauen und jede Stufe mit Beobachtungszeit versehen.
Zu frühes Entfernen zerstört Rollback, Audit-Spuren oder versteckte Verbraucher.
| Signal | Lesart | Nächster Schritt |
|---|---|---|
| Traffic Gate | Kein relevanter Legacy-Traffic | Nachweis über Metriken und bekannte Verbraucher |
| Data Gate | Migration vollständig und konsistent | Counts, Checksummen und Stichproben vergleichen |
| Rollback Gate | Rückweg getestet und befristet | Verantwortung, Zeitfenster und Datenrichtung dokumentieren |
Kapitel schliesst die Legacy-Refactoring-Vertiefung ab: alte Pfade entfernen, Flags bereinigen, Daten-/Schema-Cleanup planen und den finalen Cutover pruefbar machen.
Viele Modernisierungen wirken erfolgreich, solange alter und neuer Pfad parallel existieren. Der gefaehrliche Teil beginnt erst danach: Feature Flags bleiben liegen, alte Tabellen werden weiter beschrieben, Support-Tools nutzen heimlich Legacy-Endpunkte, Batchjobs laufen nachts weiter, und nach drei Monaten weiss niemand mehr, welche Pfade wirklich produktiv sind. Kapitel behandelt deshalb die Schlussphase als eigenes Architekturthema und nicht als Aufraeumarbeit nebenbei.
Neuen Code zu schreiben erzeugt Optionen. Alten Code zu loeschen entfernt Optionen. Deshalb braucht die Schlussphase belastbare Nachweise: fachliche Parity, keine Legacy-Nutzung, klare Datenmigration, betriebliche Readiness und eine Entscheidung, wann ein Rollback nicht mehr realistisch ist. In Enterprise-Systemen ist diese Entscheidung oft wichtiger als die technische Pull Request Groesse.
Am Ende soll der neue Pfad nicht nur funktionieren. Der alte Pfad soll wirklich weg sein: keine Flags, keine toten Klassen, keine alten Jobs, keine verwaisten Tabellen, keine falschen Runbooks und keine Dashboards, die noch Legacy-Metriken als aktive Produktionssignale darstellen.
Shadow Mode beantwortet die Frage: Kann der neue Pfad dasselbe Ergebnis liefern? Hard Cutover beantwortet eine andere Frage: Kann der neue Pfad allein betrieben werden? Diese Unterscheidung ist wichtig. Ein System kann fachlich korrekt sein und trotzdem nicht cutover-bereit, wenn Monitoring, Support, Rollback oder Datenmigration fehlen.
Die Schlussphase besteht aus Soft Cutover, Beobachtung, Hard Cutover und Cleanup. Soft Cutover routet produktiv auf den neuen Pfad, laesst Legacy aber als Sicherheitsnetz bestehen. Die Beobachtungsphase sammelt Nachweise. Hard Cutover macht den neuen Pfad verbindlich. Cleanup entfernt dann bewusst und kontrolliert die Altlasten.
Parity bedeutet nicht, dass jeder Log-Eintrag gleich ist. Parity bedeutet, dass fachlich relevante Ergebnisse innerhalb definierter Toleranzen gleich sind. Fuer Bestellungen koennen das Bruttobetrag, Steuerland, Rabattentscheidung, Rechnungsstatus und erzeugte Events sein. Technische Unterschiede wie andere interne Decision-Labels koennen erlaubt sein, solange sie keine fachliche Auswirkung haben.
Legacy-Traffic muss ueber ein definiertes Beobachtungsfenster null sein oder fachlich erklaerbar bleiben. Ein einzelner Support-Job, der noch Legacy liest, reicht aus, um Cleanup zu blockieren. Sonst loescht man Pfade, die im Normalbetrieb nicht sichtbar sind, aber in Sonderfaellen gebraucht werden.
Datenmigration ist nicht nur Zeilenanzahl. Wichtig sind referenzielle Konsistenz, fachliche Transformation, historische Sonderfaelle, Auditanforderungen und Backups. Alte Daten duerfen erst entfernt werden, wenn klar ist, ob sie fuer Reports, Steuerpruefung oder Support noch gebraucht werden.
Feature Flags sind in der Migration hilfreich, aber gefaehrlich, wenn sie dauerhaft bleiben. Jeder Flag erzeugt Kombinationszustaende: Legacy an, Legacy aus, Shadow an, Fallback an, Cutover an. Je mehr Flags bleiben, desto schwerer wird Testing und desto unsicherer wird Support.
Ein Flag darf erst entfernt werden, wenn Code, Tests, Dokumentation, Dashboards, Runbooks und Deployment-Parameter keine Referenz mehr haben. Ein reiner Code-Search reicht nicht immer, weil Flags auch in Helm Values, OpenShift ConfigMaps, Jenkins-Parametern oder Support-Skripten stehen koennen.
var refs = new FlagReferenceScanner().findReferences(
"USE_NEW_ORDER_ENGINE",
Map.of("OrderFacade.java", "if (USE_NEW_ORDER_ENGINE) route();"));
var decision = new RemovalGuard().canRemove(plan, gates, refs);
// allowed == false, solange Referenzen existieren
Die groesste Legacy-Klasse zu loeschen ist psychologisch attraktiv, aber oft falsch. Zuerst muessen Aufrufer, Routing, Fallbacks, Batchjobs und Support-Tools entfernt oder umgestellt werden. Danach wird der alte Pfad klein, isoliert und erst dann geloescht.
Eine sichere Reihenfolge ist: keine neuen Aufrufe erlauben, Fallback deaktivieren, Legacy read-only setzen, Beobachtungsfenster abwarten, Architekturregel aktivieren, Klasse entfernen, Testdaten bereinigen, Dokumentation aktualisieren.
Code kann man aus Git wiederherstellen. Datenverlust ist schwieriger. Alte Tabellen, Spalten und Auditdaten duerfen nicht nur aus technischer Sicht betrachtet werden. Man muss Aufbewahrungspflichten, Reporting, Supportfaelle und historische Rekonstruktion pruefen.
In vielen Enterprise-Systemen ist ein Schema-Cleanup mehrstufig: erst Schreibzugriff stoppen, dann Lesepfade entfernen, dann Tabelle archivieren, dann Retention abwarten, dann Drop planen. Direktes Droppen im gleichen Release wie Hard Cutover ist selten eine gute Idee.
var report = new DataMigrationVerifier().verify(legacyRows, targetRows);
if (!report.ready()) {
throw new IllegalStateException("Migration blockiert: " + report.problems());
}
Ein Rollback ist nur real, wenn Datenmodell, Codeversion, Broker-Events, Cache-Zustaende und externe Systeme ihn zulassen. Nach Hard Cutover kann ein Rollback fachlich unmoeglich werden, obwohl technisch ein altes Artefakt noch deploybar waere.
Alte Datenbankobjekte, irreversible Datenmigrationen und Entfernen zentraler Fallback-Pfade sollten getrennte Releases sein. Dadurch kann man Fehler isolieren und vermeidet, dass ein Problem sofort mehrere Wiederherstellungsebenen betrifft.
Es reicht nicht, Legacy-Dashboards zu entfernen. Neue Dashboards muessen die Fragen beantworten, die Betrieb und Support wirklich stellen: Wie viele Orders laufen? Wie viele Fehler? Welche Kundengruppen? Welche Latenz? Welche fachlichen Ablehnungen? Wo ist ein Retry sinnvoll?
Wenn Support bisher in Legacy-Tabellen oder Logs gesucht hat, muss es eine neue Diagnose geben. Sonst wird das Zielsystem zwar technisch sauber, aber organisatorisch nicht betreibbar.
Cutover-Entscheidungen muessen spaeter erklaerbar sein: Wer hat umgeschaltet? Welche Gates waren gruen? Welche Diffs waren akzeptiert? Wann wurde Rollback geschlossen? Welche Daten wurden archiviert?
Die StranglerOrderFacade kapselt Routing und
Parallelbetrieb. Aufrufer kennen nur process. Damit bleibt
die Migration intern steuerbar, ohne jeden Controller, Batchjob oder
Consumer mehrfach anzupassen.
Der CutoverGateEvaluator macht die Cutover-Entscheidung
testbar. Das ist wichtig, weil Migrationen sonst oft durch
Meeting-Entscheidungen und Bauchgefuehl gesteuert werden. Gate-Logik im
Code zwingt zu klaren Kriterien.
Der RemovalGuard verhindert Entfernen trotz
Restreferenzen oder nicht bestandener Gates. Das ist ein bewusstes
Guard/Policy-Object: Cleanup ist nicht nur ein
rm -rf legacy, sondern ein kontrollierter
Architekturakt.
Kapitel testet nicht nur Business-Ergebnisse. Er testet auch Migrationsregeln: Hard Cutover routet nur noch neu, Flag-Referenzen blockieren Cleanup, fehlende Daten blockieren Migration, zu wenige Parity-Samples blockieren Cutover und irreversible Cleanup-Schritte werden erkannt.
Normale Unit Tests sagen, ob der neue Code funktioniert. Migrations-Tests sagen, ob man den alten Code gefahrlos entfernen darf. Das ist eine andere Qualitaetsdimension.
var decision = new RemovalGuard().canRemove(plan, gates, remainingFlagReferences);
assertFalse(decision.allowed(), "cleanup must be blocked while flag is referenced");
Alle kritischen Business-Szenarien sind abgenommen, historische Sonderfaelle sind erklaert, Parity-Diffs sind entweder geloest oder bewusst akzeptiert.
Build, Tests, Architekturregeln, Linkchecks, Runbooks, Monitoring und Deployment-Pipeline sind aktualisiert. Legacy-Abhaengigkeiten sind nicht nur ungenutzt, sondern entfernt.
Ein Migrationsflag ohne Ablaufdatum ist fast immer ein kuenftiger Produktionsfehler. Nach Monaten weiss niemand mehr, welche Kombinationen erlaubt sind. Deshalb braucht jeder Flag Owner, Ablaufdatum, Entfernen-Plan und Teststrategie.
Direktes Droppen alter Tabellen im Cutover-Release spart scheinbar Arbeit, nimmt aber jede Chance auf Diagnose und Wiederherstellung. Besser ist ein kontrolliertes Retention- und Archivierungskonzept.
Wenn Code modernisiert wird, aber Runbooks, Dashboards und Support-Wissen alt bleiben, entsteht ein hybrides System im Kopf der Organisation. Deep Refactoring endet erst, wenn auch Betriebswissen modernisiert ist.
Das Lab liegt unter
code/legacy-strangler-cleanup-Kapitel und kompiliert mit
JDK 21 ohne externe Dependencies. Verwendete Patterns sind im Code
kommentiert und in docs/design-patterns.html
dokumentiert.