Zum Inhalt springen

Legacy kann man einfach hübsch refactoren

Warum Legacy-Systeme selten an unschönen Komponenten scheitern, sondern an unbekannten Abhängigkeiten, fehlenden Tests und implizitem Fachwissen.

Es klingt nach einer vernünftigen Aufgabe.

„Das ist Legacy. Das müssen wir einfach mal refactoren.“

Ein bisschen aufräumen. Ein paar große Komponenten schneiden. Ein paar Services schöner benennen. Ein paar Dateien verschieben. Danach läuft das Team wieder schneller.

Leider ist das selten Refactoring.

Oft ist es Reverse Engineering mit offenem Herzen.

Denn Legacy-Systeme scheitern selten nur daran, dass Code unschön aussieht. Sie scheitern an unbekannten Abhängigkeiten, fehlenden Tests, implizitem Fachwissen, gewachsenen Sonderfällen und Anforderungen, die niemand mehr sauber aussprechen kann.

Wer das unterschätzt, verwechselt Refactoring mit Putzen.

Und Putzen hilft nicht, wenn niemand weiß, welche Wand tragend ist.

Refactoring ist keine Kosmetik. Refactoring ist kontrollierte Veränderung unter Absicherung.

Der Denkfehler beginnt mit dem Wort „einfach“.

„Einfach refactoren.“

Das klingt, als wäre das Problem bereits verstanden. Als läge die Lösung nur noch in der Umsetzung. Als müsste man dem Team nur genug Zeit geben, damit es den Code hübscher macht.

Aber Legacy-Code ist selten nur unordentlich.

Legacy-Code ist oft Code, dessen Verhalten zwar produktiv genutzt wird, dessen Gründe aber nicht mehr vollständig bekannt sind.

Warum gibt es diese Ausnahme? Warum wird dieses Feld an drei Stellen gesetzt? Warum darf dieser Status nicht gelöscht werden? Warum ruft diese Komponente direkt diese API? Warum hängt diese Validierung im Template? Warum gibt es denselben Zustand im Store, im Formular und im Service?

Die Antwort ist oft nicht im Code sichtbar.

Sie liegt in alten Tickets, vergessenen Bugs, Kundenworkarounds, fachlichen Sonderfällen, Datenmigrationen, historischen Release-Unfällen oder im Kopf einer Person, die inzwischen in einem anderen Team ist.

Das ist nicht hässlicher Code.

Das ist unbekanntes Systemverhalten.

Viele Legacy-Initiativen starten mit Sätzen wie:

„Das muss besser laufen.“

Oder:

„Die Teams müssen schneller werden.“

Oder:

„Die alte Oberfläche muss refactored werden.“

Das Problem: Diese Sätze beschreiben Unzufriedenheit, aber keine steuerbare Veränderung.

Was heißt „besser“?

Schnellere Ladezeit? Weniger Regressionen? Kürzere Feature-Durchlaufzeit? Bessere Testbarkeit? Weniger Kopplung? Einfacheres Onboarding? Sicherere Releases? Weniger manuelle Prüfung? Weniger Angst vor Änderungen?

Ohne diese Klärung wird Refactoring zur Projektionsfläche.

Für Entwickler bedeutet es technische Schulden abbauen. Für Product Owner bedeutet es weniger Bugs. Für Management bedeutet es höhere Geschwindigkeit. Für QA bedeutet es weniger manuelle Tests. Für Betrieb bedeutet es weniger Produktionsrisiko.

Alle sagen „Refactoring“. Alle meinen etwas anderes.

Unzufriedenheit ist ein Signal. Noch keine Refactoring-Strategie.

Ohne Tests ist Refactoring oft Reverse Engineering

Abschnitt betitelt „Ohne Tests ist Refactoring oft Reverse Engineering“

Refactoring bedeutet nicht:

„Ich ändere Code, bis er schöner aussieht.“

Refactoring bedeutet:

„Ich verbessere die innere Struktur, ohne das beobachtbare Verhalten zu verändern.“

Dafür muss man zwei Dinge wissen.

Erstens: Welches Verhalten soll erhalten bleiben? Zweitens: Woran merken wir, dass es noch erhalten ist?

Wenn beides unklar ist, wird Refactoring riskant.

Ohne Tests ist jede Änderung eine Behauptung. Ohne fachliche Beispiele ist jede Vereinfachung eine Vermutung. Ohne bekannte Randfälle ist jede Bereinigung potenziell ein Bug. Ohne Monitoring merkt man manche Fehler erst in Produktion.

In solchen Systemen ist der erste Schritt nicht das große Aufräumen.

Der erste Schritt ist Absicherung.

Manchmal heißt das Unit Tests. Manchmal Integrationstests. Manchmal Characterization Tests. Manchmal Golden-Master-Tests. Manchmal Logging. Manchmal fachliche Beispielkataloge. Manchmal schlicht: erst einmal verstehen, was das System wirklich tut.

Das fühlt sich langsam an.

Aber es ist schneller als mutiges Blindfliegen.

Ohne bekannte Erwartungen ist Refactoring keine Strukturarbeit, sondern geratenes Reverse Engineering.

Natürlich sind 2.000-Zeilen-Komponenten ein Problem.

Aber sie sind selten die eigentliche Ursache.

Eine große Komponente ist oft nur der Ort, an dem viele ungeklärte Entscheidungen sichtbar werden:

  • UI-State und fachlicher State sind vermischt.
  • API-DTOs laufen direkt bis ins Template.
  • Validierung hängt an mehreren Stellen.
  • Seiteneffekte passieren in Lifecycle-Hooks.
  • Berechtigungen sind nicht zentral geklärt.
  • Fehlerbehandlung ist implizit.
  • Daten werden lokal mutiert.
  • Fachlogik wurde in die Oberfläche verschoben.
  • Sonderfälle wurden nie modelliert.

Wenn man nur die Datei teilt, verteilt man das Problem.

Dann gibt es nicht mehr eine große Komponente.

Dann gibt es acht kleinere Dateien, die immer noch dasselbe ungeklärte System bilden.

Das sieht besser aus.

Es ist aber nicht automatisch besser.

Eine große Komponente ist oft nicht das Problem. Sie ist der Ort, an dem das Problem sichtbar wird.

Gutes Legacy-Refactoring beginnt nicht mit Umbenennen.

Es beginnt mit Diagnose.

Welche Teile ändern sich häufig? Welche Teile sind fachlich kritisch? Welche Teile brechen oft? Welche Teile verursachen Angst? Welche Teile blockieren neue Features? Welche Teile sind nur hässlich, aber stabil? Welche Teile müssen wirklich erhalten bleiben? Welche Teile kann man ersetzen?

Danach braucht es eine Reihenfolge.

Nicht jede technische Schuld ist gleich wichtig. Nicht jede hässliche Stelle verdient sofort Aufmerksamkeit. Nicht jeder Rewrite ist falsch. Nicht jedes Refactoring ist sinnvoll.

Eine brauchbare Strategie beantwortet mindestens vier Fragen:

  1. Welches Risiko reduzieren wir?
  2. Welches Verhalten sichern wir vorher ab?
  3. Welche Grenze wollen wir sichtbar machen?
  4. Woran erkennen wir, dass die Veränderung geholfen hat?

Ohne diese Fragen ist Refactoring Beschäftigung.

Mit diesen Fragen wird es Modernisierung.

Legacy-Arbeit ist kein Großputz. Legacy-Arbeit ist kontrollierte Risikoreduktion.

Ein häufiger Fehler ist, Refactoring als reine Entwickleraufgabe zu behandeln.

„Ihr seid doch die Techniker. Macht das einfach nebenbei.“

Das funktioniert selten.

Nicht weil Entwickler sich anstellen. Sondern weil echtes Legacy-Refactoring Entscheidungen braucht.

Welche fachlichen Verhaltensweisen sind kritisch? Welche alten Sonderfälle dürfen verschwinden? Welche Workarounds sind noch relevant? Welche Risiken akzeptieren wir? Welche Bereiche dürfen wir isolieren? Welche Features werden währenddessen langsamer? Welche Metriken zählen wirklich?

Diese Fragen kann ein Entwicklungsteam nicht allein beantworten.

Es kann Optionen sichtbar machen. Es kann Risiken erklären. Es kann technische Wege vorschlagen. Es kann inkrementelle Schritte planen.

Aber wenn niemand fachliche Prioritäten klärt, entsteht kein Plan.

Dann entsteht nur Druck.

Und Druck auf Legacy-Systeme erzeugt selten gute Architektur.

Er erzeugt weitere Abkürzungen.

Refactoring ist nicht nur Entwicklerarbeit. Es braucht Entscheidungen, Prioritäten und Absicherung.

„Teams müssen schneller arbeiten“ ist oft die falsche Diagnose

Abschnitt betitelt „„Teams müssen schneller arbeiten“ ist oft die falsche Diagnose“

Wenn Teams in Legacy-Systemen langsam werden, liegt das nicht automatisch am Team.

Manchmal ist das System langsam.

Jede Änderung braucht zu viel Kontext. Jeder Bugfix hat Nebenwirkungen. Jeder Test ist manuell. Jede kleine Anpassung zieht Abstimmung nach sich. Jeder Release fühlt sich gefährlich an. Jede neue Person braucht Monate, um produktiv zu werden.

Dann ist „arbeitet schneller“ keine Strategie.

Es ist die Aufforderung, auf einer kaputten Straße schneller zu fahren.

Ein gutes Modernisierungsvorhaben fragt nicht nur:

„Warum liefert das Team so langsam?“

Sondern auch:

„Welche strukturellen Bedingungen machen schnelle, sichere Lieferung unwahrscheinlich?“

Das ist eine unbequemere Frage.

Aber meistens die nützlichere.

Man macht Teams nicht schneller, indem man mehr Druck ausübt. Man macht Änderung billiger.

„Kein Rewrite“ ist als pauschale Regel genauso gefährlich wie „alles neu bauen“.

Natürlich sind Big-Bang-Rewrites riskant. Natürlich unterschätzen Teams oft die versteckte Fachlichkeit im alten System. Natürlich ist ein kompletter Neubau selten so sauber, wie er in der Planung klingt.

Aber manchmal ist kosmetische Sanierung teurer als gezieltes Ersetzen.

Wenn ein Bereich fachlich gut verstanden ist, klare Grenzen hat und kaum sinnvolle Testbarkeit nachträglich hergestellt werden kann, kann ein kleiner Rewrite günstiger sein als monatelanges Herumoperieren.

Der entscheidende Unterschied ist nicht Refactoring oder Rewrite.

Der entscheidende Unterschied ist kontrolliert oder unkontrolliert.

Ein kompletter Big-Bang-Rewrite ist gefährlich. Ein gezielter Ersatz eines klar begrenzten Slices kann sehr sinnvoll sein.

Legacy-Modernisierung wird gefährlich, wenn sie heroisch wird.

„Wir bauen das jetzt einmal richtig neu.“

Das klingt gut.

Bis das neue System alle alten Sonderfälle nachbauen muss. Bis Parallelbetrieb notwendig wird. Bis Datenmigrationen komplizierter werden als gedacht. Bis Feature-Druck zurückkommt. Bis das alte System doch weiterlebt. Bis zwei Systeme gepflegt werden müssen.

Inkrementelles Vorgehen ist weniger dramatisch.

Aber meistens professioneller.

Ein neuer Slice neben dem alten System. Eine klare Schnittgrenze. Ein Routing- oder Integrationspunkt. Ein abgesicherter Wechsel. Ein messbarer Nutzen. Dann der nächste Slice.

Das ist weniger romantisch als der große Neubau.

Aber Legacy-Systeme belohnen selten Romantik.

Sie belohnen kleine, sichere Schritte.

Die Frage ist nicht: Refactoring oder Rewrite? Die Frage ist: Wie reduzieren wir Risiko in kleinen, überprüfbaren Schritten?

Statt zu sagen:

„Legacy kann man einfach hübsch refactoren.“

sollten wir genauer sagen:

„Wir müssen zuerst verstehen, welches Verhalten erhalten bleiben muss, welche Risiken uns bremsen und welche Grenze wir als Nächstes sicher ziehen können.“

Das klingt weniger schnell.

Aber es ist ehrlicher.

Legacy-Arbeit ist nicht nur Code-Arbeit. Sie ist Analysearbeit, Absicherungsarbeit, Architekturarbeit und Entscheidungsarbeit.

Wer Refactoring fordert, muss auch die Bedingungen dafür ermöglichen:

  • Zeit für Diagnose
  • Zugang zu fachlichem Wissen
  • Entscheidung über alte Sonderfälle
  • Tests oder andere Sicherheitsnetze
  • kleine Modernisierungsslices
  • klare Prioritäten
  • Akzeptanz, dass nicht jede sichtbare Unschönheit zuerst relevant ist

Refactoring ist kein Zauberstab.

Es ist ein chirurgischer Eingriff.

Und bei Legacy-Systemen sollte man vor dem ersten Schnitt wissen, ob man Haut, Fett oder Arterie vor sich hat.