Alte Systeme haben zwei Dinge gemeinsam: Sie funktionieren, und sie machen alle nervös. Sie funktionieren, weil jahrelange Nutzung in der Praxis sie um das tatsächliche Geschäft herum geformt hat. Sie machen Menschen nervös, weil niemand sie anfassen will, die Dokumentation dünn ist und einer der ursprünglichen Entwickler vor drei Stellen gegangen ist.
Was tatsächlich als "Legacy" gilt
Nicht jedes alte System ist Legacy. Eine 10 Jahre alte Anwendung, die auf einem unterstützten Stack läuft, gut getestet ist und aktiv gepflegt wird, ist schlicht ausgereift. Legacy bedeutet typischerweise:
- Plattform- oder Framework-Versionen, die keine Sicherheitsupdates mehr erhalten.
- Wissen, das sich auf ein oder zwei Personen konzentriert.
- Angst, Dinge zu ändern, weil kleine Änderungen entfernte Funktionen kaputtmachen.
- Manuelle Deployments, manuelle Tests, alles manuell.
- Eine wachsende Kluft zwischen dem, was das Geschäft braucht, und dem, was das System kann.
Die zwei scheiternden Ansätze
- Nichts tun. Das Risiko verstärkt sich. Irgendwann bricht etwas Kritisches zu einem schlechten Zeitpunkt.
- Von Grund auf neu schreiben. Ein Big-Bang-Neuschreiben dauert fast immer länger als geplant, entdeckt versteckte Anforderungen zu spät und läuft so lange parallel zum alten System, dass das Team aufgibt.
Die Ansätze, die tatsächlich funktionieren, liegen zwischen diesen beiden Extremen.
Strategie 1: Das alte System erdrosseln
Halten Sie das Legacy-System am Laufen, leiten Sie aber neue Funktionalität nach und nach durch eine neue Schicht. Mit der Zeit leben immer mehr Funktionen im neuen System; das alte wird kleiner, bis es schließlich abgeschaltet wird.
Das funktioniert für Websites (Seite für Seite verschieben), interne Tools (Modul für Modul verschieben) und Integrationen (ein neues API-Gateway einführen und Aufrufer dann dorthin umleiten).
Strategie 2: An Ort und Stelle modernisieren
Manchmal ist die Architektur in Ordnung, aber der Stack ist zu alt. In diesem Fall aktualisieren Sie eine Schicht nach der anderen: Sprache/Laufzeit, dann Bibliotheken, dann Framework, dann Deployment. Fügen Sie unterwegs automatisierte Tests hinzu, damit jeder Schritt überprüfbar ist. Nicht glamourös, aber sehr wirksam.
Strategie 3: Aufteilen, dann modernisieren
Große monolithische Systeme lassen sich oft leichter Stück für Stück modernisieren, sobald die Nahtstellen klar sind. Das bedeutet, zuerst Grenzen innerhalb des bestehenden Systems zu definieren (Module, Bounded Contexts) und dann Module unabhängig zu ersetzen. Die Grenzen zählen mehr als die Technologiewahl.
Was vor dem Schreiben von Code zu tun ist
- Schreiben Sie auf, was das System heute tatsächlich tut, nicht, was es tun sollte.
- Identifizieren Sie die Funktionen, auf die sich Menschen täglich verlassen, diese müssen den Übergang über funktionieren.
- Identifizieren Sie tote Funktionen, sie machen oft einen bedeutenden Anteil des Codes aus und sollten still stillgelegt werden.
- Erfassen Sie Daten, Integrationen, Berechtigungen und Exporte. Hier geraten Modernisierungsprojekte meist in Schwierigkeiten.
- Legen Sie in messbaren Begriffen fest, was "fertig" bedeutet.
Risikokontrollen, die einen echten Unterschied machen
- Backups und getestete Wiederherstellungen, vor jeder Änderung.
- Automatisierte Tests für das Verhalten, das Ihnen wichtig ist, schrittweise eingeführt.
- Feature-Flags, damit Änderungen in der Produktion ohne Deploy zurückgerollt werden können.
- Observability, Logs, Error-Tracking, zentrale Geschäftskennzahlen, damit Sie schnell merken, wenn sich etwas verschlechtert.
- Ein gestaffeltes Rollout: Neuer Code läuft zuerst für interne Nutzer, dann für einen kleinen Prozentsatz echter Nutzer, dann für alle.
Strangler-Fig-Muster. Statt eines riskanten Big-Bang-Neuschreibens leiten Sie den Traffic durch eine dünne Fassade, die nach und nach immer mehr Anfragen an neue Dienste weiterleitet, während das Legacy-System den Rest weiter bearbeitet, bis es leer ist.
Beispiel: eine minimale Fassaden-Route
Ein kleines Gateway kann pro Route entscheiden, ob eine Anfrage an den Legacy-Monolithen oder an den neuen Dienst geht. Framework-neutral geschrieben:
app.use((req, res, next) => {
if (req.path.startsWith('/api/v2/invoices')) {
return proxy(req, res, NEW_SERVICE_URL);
}
return proxy(req, res, LEGACY_URL);
});
Jedes migrierte Modul verschiebt ein Präfix von LEGACY_URL zu NEW_SERVICE_URL. Nutzer sehen nie eine Big-Bang-Umstellung; das Team liefert kleine, umkehrbare Schritte.
Häufige Falle: das neue und das alte System so zu schreiben, als müssten sie am ersten Tag funktional gleichwertig sein. Müssen sie nicht. Beginnen Sie mit dem kleinsten Teil, der echten geschäftlichen Wert hat, beweisen Sie den Migrationsweg und wiederholen Sie ihn dann.
Häufige Fehler
- Modernisierung zur Frage der Technologiewahl machen. Schicke neue Stacks helfen nicht, wenn das Datenmodell und die Integrationen weiterhin rätselhaft sind.
- Auf ein komplettes Redesign warten, bevor man die sichtbaren Probleme behebt. Die Nutzer zahlen den Preis; die Dynamik stirbt.
- Das alte System zu früh abschalten. Lassen Sie beide lange genug parallel laufen, um dem neuen zu vertrauen.
- Institutionelles Wissen verlieren. Arbeiten Sie mit den Menschen zusammen, die das System kennen. Ihr Wissen ist das eigentliche Kapital.
Ein realistischer Zeitplan
- Stabilisieren: Backups, Monitoring, Sicherheits-Patches, minimale Tests.
- Kartieren: dokumentieren, was existiert und was tatsächlich genutzt wird.
- Priorisieren: das Teil wählen, das jetzt schmerzhaft und wertvoll zu modernisieren ist.
- Ersetzen: die neue Version dieses Teils neben dem alten bauen.
- Umschalten: Traffic umleiten, überwachen, Probleme beheben.
- Stilllegen: das alte Teil erst außer Betrieb nehmen, nachdem es eine Weile ruhig war.
- Für das nächste Teil wiederholen.

