Ihr kritischstes Geschäftssystem läuft auf Code, den niemand mehr vollständig versteht. Die ursprünglichen Entwickler sind vor Jahren gegangen. Die Dokumentation ist veraltet oder fehlt. Jede Änderung fühlt sich an wie das Entschärfen einer Bombe. Kommt Ihnen das bekannt vor? Sie haben es mit Legacy Code zu tun - und Sie sind nicht allein.

Studien zeigen, dass 70-80% der IT-Budgets für die Wartung bestehender Systeme aufgewendet werden. Der Großteil dieser Wartung ist Brandbekämpfung: Bugs patchen, Einschränkungen umgehen, Systeme trotz ihres Alters am Laufen halten. Die wahren Kosten sind nicht nur Entwicklerzeit - es sind die Opportunitätskosten von Features, die nicht gebaut werden können, Integrationen, die nicht möglich sind, und Geschäftsagilität, die nicht erreicht werden kann.

Aber hier ist, was die meisten Organisationen falsch machen: Die Antwort ist nicht immer ein komplettes Neuschreiben. Tatsächlich scheitern Rewrites häufiger als sie erfolgreich sind. Die echte Lösung ist systematische Legacy Code Recovery - verstehen was Sie haben, es stabilisieren und schrittweise modernisieren.

Was genau ist Legacy Code und warum wird er problematisch?

Michael Feathers, Autor von “Working Effectively with Legacy Code”, definiert Legacy Code einfach: “Code ohne Tests.” Ohne Tests können Sie Code nicht sicher ändern. Ohne sichere Änderungen wird Code eingefroren. Eingefrorener Code wird Legacy.

Aber Legacy Code hat viele Dimensionen:

Wissenslücke. Die Menschen, die den Code geschrieben haben, sind weg. Die Begründung hinter Entscheidungen ist nicht dokumentiert. Stammwissen ist verloren gegangen.

Technologielücke. Der Code verwendet Frameworks, Bibliotheken oder Sprachen, die veraltet, nicht unterstützt oder den aktuellen Entwicklern unbekannt sind.

Dokumentationslücke. Vorhandene Dokumentation ist unvollständig, veraltet oder widerspricht dem, was der Code tatsächlich macht.

Architekturlücke. Die ursprüngliche Architektur (falls es eine gab) ist erodiert. Schnelle Fixes haben sich angesammelt. Die Systemstruktur entspricht keinem kohärenten Design mehr.

Testlücke. Automatisierte Tests fehlen, sind unvollständig oder selbst unzuverlässig. Manuelles Testen erfordert tiefes Wissen, das nur wenige besitzen.

Legacy Code ist nicht immer alter Code. Eine sechs Monate alte Codebasis kann Legacy sein, wenn der Entwickler gegangen ist, keine Tests geschrieben wurden und niemand anderes sie versteht. Umgekehrt ist ein zwanzig Jahre altes System mit umfassenden Tests, klarer Dokumentation und kompetenten Betreuern nicht wirklich Legacy - es ist ausgereift.

Warum scheitern Legacy Code Rewrites?

Die Statistiken sind ernüchternd: 70% der kompletten System-Rewrites liefern nicht den erwarteten Wert. Viele werden vor der Fertigstellung aufgegeben. Warum?

Second-System-Effekt. Teams versuchen, alles auf einmal zu reparieren. Das neue System wird überentwickelt und versucht, nicht nur aktuelle Probleme zu lösen, sondern jeden denkbaren zukünftigen Bedarf.

Unterschätzung bestehender Komplexität. Dieses “einfache” alte System behandelt Hunderte von Randfällen, an die sich niemand erinnert. Jeder Randfall wurde hinzugefügt, weil ein echter Kunde ein echtes Problem hatte. Rewrites übersehen diese Fälle, bis die Produktion sie offenlegt.

Bewegliches Ziel. Das Geschäft steht nicht still, während Sie neu schreiben. Das alte System bekommt weiterhin Patches und Features. Das neue System muss ein bewegliches Ziel treffen.

Big-Bang-Deployment. Rewrites erfordern oft das Umschalten von allem auf einmal. Es gibt keine schrittweise Einführung, keine Möglichkeit alt und neu zu vergleichen, keinen Fallback außer komplettem Rollback.

Budget- und Zeitdruck. Rewrites dauern immer länger als geschätzt. Wenn das Budget aufgebraucht ist, haben Sie zwei unvollständige Systeme statt einem funktionierenden.

Die Alternative - schrittweise Recovery und Modernisierung - ist langsamer, aber sicherer. Sie haben immer ein funktionierendes System. Sie lernen während Sie fortschreiten. Sie können den Ansatz basierend auf Ihren Entdeckungen anpassen.

Wie bewerten Sie den aktuellen Zustand einer Legacy Codebasis?

Bevor Sie Legacy Code wiederherstellen können, müssen Sie verstehen, womit Sie es zu tun haben. Die Bewertung hat mehrere Dimensionen:

Statische Analyse. Führen Sie Tools wie SonarQube, CodeClimate oder sprachspezifische Linter aus. Sie werden offenlegen:

  • Zyklomatische Komplexität (wie verworren ist die Logik?)
  • Code-Duplikation (wie viel Copy-Paste?)
  • Toter Code (was wird nicht verwendet?)
  • Sicherheitslücken (welche Risiken bestehen?)
  • Abhängigkeitsstatus (was ist veraltet oder verwundbar?)

Hotspot-Analyse. Tools wie CodeScene oder git-basierte Analyse zeigen:

  • Welche Dateien ändern sich am häufigsten?
  • Welche Dateien haben die meisten Bugs?
  • Welche Dateien werden von den meisten verschiedenen Personen geändert?

Dateien, die sich häufig ändern, viele Bugs haben und von vielen Entwicklern berührt werden, sind Ihre Hotspots - die Bereiche, die am dringendsten Recovery benötigen.

Architektur-Recovery. Kartieren Sie die tatsächliche Architektur (nicht was die Dokumentation sagt):

  • Was sind die Hauptkomponenten?
  • Wie kommunizieren sie?
  • Was sind die Abhängigkeiten?
  • Wo sind die Grenzen (oder wo sollten sie sein)?

Wissens-Mapping. Identifizieren Sie:

  • Wer weiß was über das System?
  • Welche Bereiche haben niemanden, der sie versteht?
  • Welche Dokumentation existiert und ist sie genau?

Business-Kritikalitäts-Mapping. Verstehen Sie:

  • Welche Teile des Systems sind geschäftskritisch?
  • Was sind die Kosten von Ausfallzeiten für jede Komponente?
  • Welche Features werden genutzt vs. nicht genutzt?
  • Welche Änderungen oder Ergänzungen sind geplant?

Kombinieren Sie diese Bewertungen zur Priorisierung: hohe Geschäftskritikalität + hohe Änderungshäufigkeit + niedriges Verständnis = höchste Priorität für Recovery.

Was sind die wichtigsten Strategien für Legacy Code Recovery?

Strategie 1: Charakterisierungstests

Bevor Sie Legacy Code ändern können, müssen Sie wissen, was er macht. Charakterisierungstests erfassen bestehendes Verhalten - nicht was der Code tun sollte, sondern was er tatsächlich tut.

Der Prozess:

  1. Identifizieren Sie ein Stück Code zum Verstehen
  2. Schreiben Sie einen Test, der diesen Code mit einer Eingabe aufruft
  3. Lassen Sie den Test fehlschlagen - sehen Sie was der Code tatsächlich zurückgibt
  4. Aktualisieren Sie den Test, um diese tatsächliche Ausgabe zu erwarten
  5. Wiederholen Sie mit verschiedenen Eingaben

Jetzt haben Sie ein Sicherheitsnetz. Wenn Sie den Code ändern und Tests fehlschlagen, haben Sie das Verhalten geändert. Vielleicht war das gewollt; vielleicht offenbart es eine unbeabsichtigte Konsequenz.

Charakterisierungstests geht es nicht darum, ob das Verhalten korrekt ist - es geht darum, aktuelles Verhalten zu dokumentieren, damit Sie Code sicher ändern können.

Strategie 2: Naht-Identifikation (Seams)

Eine Naht ist ein Ort, an dem Sie Verhalten ändern können, ohne bestehenden Code zu modifizieren. Nähte sind entscheidend für das Testen und Refactoring von Legacy Code.

Arten von Nähten:

  • Objekt-Nähte: Ersetzen Sie ein Objekt durch ein Test-Double via Dependency Injection
  • Präprozessor-Nähte: Verwenden Sie Präprozessor oder Build-Konfiguration zum Austausch von Implementierungen
  • Link-Nähte: Ersetzen Sie Bibliotheken oder Module zur Link-/Ladezeit

Nähte in Legacy Code finden:

  1. Identifizieren Sie den Code, den Sie testen müssen
  2. Verfolgen Sie seine Abhängigkeiten
  3. Suchen Sie nach Punkten, an denen Sie diese Abhängigkeiten ersetzen können
  4. Erstellen Sie Interfaces oder Abstraktionen an diesen Punkten

Sobald Sie Nähte haben, können Sie Code isoliert testen und sicher mit dem Refactoring beginnen.

Strategie 3: Strangler Fig Pattern

Benannt nach Würgefeigen, die um Wirtsbäume wachsen, ersetzt dieses Muster schrittweise ein Legacy-System:

  1. Identifizieren Sie eine Funktionalität zum Ersetzen
  2. Bauen Sie neue Funktionalität neben der alten
  3. Leiten Sie Traffic/Aufrufe zur neuen Funktionalität um
  4. Verifizieren Sie, dass die neue Funktionalität korrekt funktioniert
  5. Entfernen Sie alte Funktionalität
  6. Wiederholen Sie

Der Schlüssel ist, nie einen “Big Bang” Umstieg zu haben. Sie haben immer funktionierenden Code. Sie können altes und neues Verhalten vergleichen. Sie können spezifische Teile zurückrollen.

Praktische Implementierung:

  • Verwenden Sie Feature Flags zum Routing zwischen alt und neu
  • Führen Sie beide Versionen parallel aus und vergleichen Sie Ausgaben
  • Erhöhen Sie schrittweise den Traffic zur neuen Version
  • Überwachen Sie sorgfältig bei jedem Schritt

Strategie 4: Branch by Abstraction

Ähnlich wie Strangler Fig, aber für interne Komponenten statt ganzer Features:

  1. Erstellen Sie eine Abstraktion (Interface) für die Komponente, die Sie ersetzen möchten
  2. Implementieren Sie die Abstraktion mit dem bestehenden Code
  3. Aktualisieren Sie Clients zur Nutzung der Abstraktion
  4. Bauen Sie neue Implementierung der Abstraktion
  5. Wechseln Sie zur neuen Implementierung (kann schrittweise mit Feature Flags sein)
  6. Entfernen Sie die alte Implementierung

Dieses Muster ermöglicht das Ersetzen interner Komponenten bei identischem externem Verhalten.

Strategie 5: Mikado-Methode

Für komplexes Refactoring mit vielen Abhängigkeiten:

  1. Setzen Sie Ihr Ziel (das Refactoring, das Sie erreichen möchten)
  2. Versuchen Sie es direkt zu erreichen
  3. Wenn Sie auf einen Compile-/Testfehler stoßen, notieren Sie was zuerst passieren muss
  4. Setzen Sie auf den Ausgangszustand zurück
  5. Arbeiten Sie zuerst an den Voraussetzungen
  6. Wiederholen Sie bis Sie Ihr Ziel erreichen

Die Mikado-Methode erstellt einen Abhängigkeitsgraphen Ihres Refactorings. Sie sehen was zuerst passieren muss, verfolgen den Fortschritt und haben immer funktionierenden Code.

Wie bauen Sie institutionelle Wissens-Recovery auf?

Code Recovery geht nicht nur um Code - es geht um das Wissen darum.

Code-Archäologie. Nutzen Sie die Versionskontrollhistorie:

  • git log und git blame zeigen wer was wann geändert hat
  • Commit-Nachrichten (wenn sinnvoll) erklären warum Änderungen gemacht wurden
  • Zugehörige Issue-Tracker-Tickets liefern Geschäftskontext

Interviews mit ausscheidenden Entwicklern. Bevor jemand geht, erfassen Sie sein Wissen:

  • Nehmen Sie Durchgänge durch kritische Systeme auf
  • Dokumentieren Sie die Begründung hinter Schlüsselentscheidungen
  • Kartieren Sie Fallstricke, bekannte Probleme und Stammwissen

Erstellen Sie lebende Dokumentation. Dokumente, die sich automatisch aktualisieren:

  • Architekturdiagramme aus Code generiert
  • API-Dokumentation aus Code-Annotationen
  • Abhängigkeitsgraphen aus Build-Dateien

Entscheidungsprotokolle (ADRs). Für neue Entscheidungen während der Recovery:

  • Welche Entscheidung wurde getroffen?
  • Was war der Kontext?
  • Welche Alternativen wurden betrachtet?
  • Warum wurde diese Option gewählt?

Wie priorisieren Sie, was zuerst wiederhergestellt werden soll?

Nicht aller Legacy Code braucht Recovery. Manches kann in Ruhe gelassen werden. Manches sollte außer Betrieb genommen werden. Manches ist kritisch.

Der Recovery-Quadrant:

Niedriger GeschäftswertHoher Geschäftswert
Niedrige ÄnderungshäufigkeitIn Ruhe lassenÜberwachen und dokumentieren
Hohe ÄnderungshäufigkeitStilllegung erwägenPriorität für Recovery

Hoher Geschäftswert + hohe Änderungshäufigkeit = höchste Priorität. Sie ändern es oft (daher zählt Stabilität) und es ist geschäftskritisch (daher ist Ausfall kostspielig).

Zusätzliche Priorisierungsfaktoren:

  • Risiko von Sicherheitslücken
  • Kommende geplante Features in diesem Bereich
  • Verfügbarkeit von Personen mit Wissen
  • Existenz von Tests oder Dokumentation
  • “Zinssatz” der technischen Schulden

Was sind häufige Fallstricke bei Legacy Code Recovery?

Fallstrick 1: Gold Plating. Versuchen, wiederhergestellten Code perfekt statt nur angemessen zu machen. Recovery sollte Code wartbar machen, nicht Architekturpreise gewinnen.

Fallstrick 2: Unzureichendes Testen. Überspringen von Charakterisierungstests um schneller zu sein. Jede Abkürzung hier riskiert die Einführung von Bugs.

Fallstrick 3: Zu viel auf einmal ändern. Fünf Refactorings in einem Commit machen. Halten Sie Änderungen klein und rückgängig machbar.

Fallstrick 4: Geschäfts-Stakeholder nicht einbeziehen. Recovery braucht Zeit und Ressourcen. Das Business muss die Investition und erwarteten Renditen verstehen.

Fallstrick 5: Organisatorische Probleme ignorieren. Code wurde aus Gründen Legacy - Zeitdruck, Fluktuation, fehlende Praktiken. Wenn Sie die Grundursachen nicht angehen, wird neuer Code auch Legacy.

Fallstrick 6: Keine Definition of Done. Wann ist Recovery “fertig”? Ohne klare Kriterien geht die Arbeit endlos weiter oder endet vorzeitig.

Wie messen Sie Erfolg bei Legacy Code Recovery?

Definieren Sie Erfolgsmetriken vor dem Start:

Entwicklungsgeschwindigkeit. Zeit zur Implementierung von Features im wiederhergestellten Bereich vs. vorher. Sollte sinken.

Änderungsfehlerrate. Prozentsatz der Änderungen, die Incidents im wiederhergestellten Bereich verursachen. Sollte sinken.

Zykluszeit. Zeit vom Code-Commit bis Produktion für den wiederhergestellten Bereich. Sollte sinken.

Entwicklervertrauen. Umfrage: “Wie zuversichtlich sind Sie bei Änderungen in Bereich X?” Sollte steigen.

Testabdeckung. Prozentsatz des Codes, der von automatisierten Tests abgedeckt wird. Sollte steigen.

Incident-Rate. Produktions-Incidents im Zusammenhang mit dem wiederhergestellten Bereich. Sollte sinken.

Wissensverteilung. Wie viele Entwickler können in dem Bereich arbeiten? Sollte steigen.

Verfolgen Sie diese Metriken über Zeit. Zeigen Sie Trends. Nutzen Sie Daten zur Rechtfertigung weiterer Investitionen und zum Nachweis des Werts.

Checkliste: Start Ihrer Legacy Code Recovery

  1. Bewertung

    • Statische Analysetools ausführen
    • Hotspots identifizieren (häufige Änderungen + Bugs)
    • Aktuelle Architektur kartieren
    • Wissensträger identifizieren
    • Geschäftskritikalität dokumentieren
  2. Vorbereitung

    • Geschäftszustimmung und Budget erhalten
    • Erfolgsmetriken definieren
    • Monitoring und Messung einrichten
    • Recovery-Backlog erstellen
    • Nach Wert und Risiko priorisieren
  3. Durchführung

    • Mit Charakterisierungstests beginnen
    • Nähte zum Testen identifizieren
    • Kleine, schrittweise Änderungen machen
    • Strangler Fig für Feature-Ersetzung nutzen
    • Entscheidungen mit ADRs dokumentieren
  4. Nachhaltigkeit

    • Grundursachen der Legacy-Entstehung angehen
    • Quality Gates für neuen Code etablieren
    • Wissen im Team teilen
    • Regelmäßige Überprüfung von Fortschritt und Ansatz

Legacy Code Recovery ist nicht glamourös. Es gibt keine große Enthüllung, keinen dramatischen Launch. Es ist die langsame, stetige Arbeit des Verstehens, Stabilisierens und Verbesserns dessen, was bereits existiert. Aber gut gemacht, erschließt es Geschäftsagilität, die komplette Rewrites selten erreichen.

Die wichtigste Erkenntnis: Legacy Code ist ein Asset, das über Jahre Wert und Geschäftslogik angesammelt hat. Werfen Sie es nicht weg - stellen Sie es wieder her, bewahren Sie diesen Wert und bauen Sie darauf auf.

Bei ARDURA Consulting sind wir auf Software Rescue Software Rescue durch experten Body Leasing Staff Augmentation spezialisiert. Unsere erfahrenen Entwickler und Architekten haben unzählige Legacy-Systeme wiederhergestellt - von COBOL-Mainframes bis zu jahrzehntealten Java-Monolithen. Wir können Ihrem Team helfen, Ihre kritischen Systeme ohne das Risiko kompletter Rewrites zu verstehen, stabilisieren und modernisieren.

Kontaktieren Sie uns um zu besprechen, wie wir bei Ihren Legacy Code Herausforderungen helfen können.