· aktualisiert am

Legacy Code: Bedeutung, Refactoring und wie du fremden Code übernimmst

Was Legacy Code bedeutet, wie du eine fremde Codebase mit Git und KI verstehst, sie mit Charakterisierungstests absicherst und schrittweise refaktorierst.

LegacyRefactoringKI

Thema: Web-Entwicklung · KI & Coding

Inhalt (10 Abschnitte)
Legacy Code: Bedeutung, Refactoring und wie du fremden Code übernimmst

Legacy Code ist bestehender Code, den du ändern musst, aber nicht sicher ändern kannst, weil Tests, Dokumentation oder Verständnis fehlen. Der Weg heraus ist kein Neuschreiben, sondern eine Reihenfolge: Code und Geschichte verstehen, das heutige Verhalten mit Charakterisierungstests festhalten, dann in kleinen Schritten refaktorieren und alte Teile schrittweise ersetzen.

Stand: 13. Oktober 2026. Begriffe und Befehle stammen aus den Seiten von Martin Fowler, den Verlagsangaben zu Michael Feathers’ Buch, der Dokumentation von Git, Node.js und Claude Code, abgerufen an diesem Tag. Die Quellen stehen am Ende.

Was Legacy Code bedeutet

Das englische „legacy“ übersetzt das Cambridge-Wörterbuch mit „Vermächtnis“. Gemeint ist Code, den du von anderen übernommen hast oder der so alt ist, dass niemand ihn mehr ganz versteht. Eine feste Altersgrenze gibt es nicht.

Ein bekanntes Buch zum Thema ist „Working Effectively with Legacy Code“ von Michael Feathers, erschienen 2004, auf Deutsch als „Effektives Arbeiten mit Legacy Code“ bei mitp. Der deutsche Verlag fasst die Idee in drei Fragen zusammen: Kannst du deinen Code leicht ändern? Bekommst du fast unmittelbar Rückmeldung, wenn du ihn änderst? Verstehst du ihn? Wer eine davon mit Nein beantwortet, arbeitet mit Legacy Code. Ein Kernpunkt des Buchs ist laut englischem Verlag, Tests zu schreiben, die sicherstellen, dass man die Anwendung beim Umbau nicht unbeabsichtigt verändert.

MerkmalWoran du es erkennst
keine oder lückenhafte TestsÄnderungen werden „vorsichtig“ gemacht und von Hand geprüft
fehlendes Wissendie ursprünglichen Entwickler sind weg, Dokumentation fehlt oder ist veraltet
langsame RückmeldungBuild oder Deployment dauern so lange, dass niemand oft testet
enge Kopplungeine kleine Änderung bricht etwas an unerwarteter Stelle
veraltete AbhängigkeitenFramework- oder Sprachversion ohne Sicherheitsupdates

Die ersten Tage: verstehen statt ändern

Die Versuchung ist groß, sofort aufzuräumen. Nach Einschätzung lohnt es sich, die ersten Tage dem Verständnis zu widmen, bevor du etwas veränderst. Vier Schritte helfen dabei.

1. Das Projekt lokal zum Laufen bringen. Ohne lauffähige Umgebung kannst du weder ausprobieren noch Tests schreiben. Was dabei hakt, ist gleich die erste Notiz für eine bessere README.

2. Eine Landkarte bauen. Einstiegspunkte, Datenmodell, externe Abhängigkeiten wie Datenbanken, Queues, Cronjobs und fremde APIs. Halte das in einer eigenen Notizdatei fest.

3. Einen Kernablauf von vorne bis hinten verfolgen. Jede Anwendung hat einen Ablauf, der ihr Herz ist: die Bestellung im Shop, der Upload im Dokumentenwerkzeug. Verfolge ihn einmal vom Request über Validierung und Geschäftslogik bis zur Datenbank und zu Nebenwirkungen wie Mails oder Events.

4. Mit Menschen sprechen. Wo die größten Schmerzen liegen, welche Bereiche heikel sind und wo ein Umbau schon gescheitert ist, steht selten im Code.

Die Geschichte des Codes mit Git lesen

Code zeigt, was ist, aber nicht, warum. Für das Warum ist die Versionsgeschichte die beste Quelle. Drei Befehle aus der Git-Dokumentation sind dabei besonders nützlich:

# Geschichte einer Datei, auch über Umbenennungen hinweg, mit Änderungen
git log --follow -p -- src/billing/preis.js

# Commits finden, die eine bestimmte Zeichenkette hinzugefügt oder entfernt haben
git log -S "SOMMER" --oneline

# Wer welche Zeile zuletzt geändert hat, Leerzeichenänderungen ignoriert
git blame -w -L 1,40 src/billing/preis.js

--follow setzt die Geschichte einer einzelnen Datei über Umbenennungen hinweg fort. -S sucht nach Commits, die die Anzahl der Vorkommen einer Zeichenkette verändern, und findet so den Commit, mit dem eine Regel eingeführt wurde. git blame -w ignoriert Änderungen an Leerzeichen; mit --ignore-rev lassen sich zusätzlich reine Formatierungs-Commits ausblenden.

Commit-Nachrichten und verknüpfte Tickets erklären oft Eigenheiten, die sonst wie Fehler aussehen. Achte außerdem auf Kommentare wie TODO, FIXME oder HACK und auf auskommentierte Blöcke: Sie markieren nach Einschätzung häufig halbfertige Umbauten.

Fremde Codebase mit KI verstehen

Coding-Assistenten mit Zugriff auf das Repository beschleunigen das Einarbeiten, weil sie Dateien selbst durchsuchen und Zusammenhänge zusammenfassen. Die Dokumentation von Claude Code beschreibt dafür zwei Abläufe, einen Überblick über die Codebase und das Finden relevanter Stellen: im Projektverzeichnis claude starten und zuerst breit fragen, dann enger werden. Beispiel-Prompts aus beiden Abläufen:

give me an overview of this codebase
explain the main architecture patterns used here
what are the key data models?
how is authentication handled?
trace the login process from front-end to database

Dazu empfiehlt die Dokumentation, mit allgemeinen Fragen zu beginnen und dann einzelne Bereiche einzugrenzen, nach Konventionen und Mustern im Projekt zu fragen, sich ein Glossar der projekteigenen Begriffe geben zu lassen und die Fachsprache des Projekts zu benutzen.

Drei Regeln machen diesen Weg nach Einschätzung verlässlich:

  • Antworten belegen lassen. Lass dir zu jeder Aussage über die Architektur Dateipfade und Zeilen nennen und öffne einige davon selbst. Sprachmodelle können plausible, aber falsche Zusammenhänge beschreiben.
  • Nach dem Fehlenden fragen. Fragen wie „Welche Fehlerfälle behandelt dieser Ablauf nicht?“ zeigen oft mehr als Erklärungen, wie etwas funktioniert.
  • Grenzen kennen. Was in Produktion konfiguriert ist, welche Cronjobs wirklich laufen und wie sich das System unter Last verhält, steht nicht im Repository. Das zeigen nur Logs, Metriken und die echte Umgebung.

Wie du Claude Code einrichtest, steht in der Anleitung Claude Code installieren.

Charakterisierungstests: das Sicherheitsnetz

Bevor du Legacy Code umbaust, brauchst du einen Weg festzustellen, dass du nichts kaputt gemacht hast. Normale Tests prüfen das gewünschte Verhalten. Charakterisierungstests halten dagegen fest, was der Code heute tatsächlich tut, auch wenn das merkwürdig oder falsch wirkt. Solange sie grün bleiben, hast du das Verhalten beim Umbau nicht verändert.

Ein Beispiel mit dem in Node.js eingebauten Testrunner. Die Funktion stammt aus dem Bestand, ihr Verhalten ist nicht dokumentiert:

// preis.js (Bestand)
export function berechnePreis(positionen, gutschein) {
  let summe = 0;
  for (const p of positionen) summe += p.preis * p.menge;
  if (gutschein === 'SOMMER') summe = summe * 0.9;
  if (summe > 100) summe = summe - 5;
  return Math.round(summe * 100) / 100;
}
// preis.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { berechnePreis } from './preis.js';

test('ohne Gutschein unter 100', () => {
  assert.equal(berechnePreis([{ preis: 20, menge: 2 }]), 40);
});

test('Gutschein SOMMER zieht 10 Prozent ab', () => {
  assert.equal(berechnePreis([{ preis: 50, menge: 1 }], 'SOMMER'), 45);
});

test('über 100 werden 5 abgezogen, auch nach dem Gutschein', () => {
  assert.equal(berechnePreis([{ preis: 120, menge: 1 }], 'SOMMER'), 103);
});

test('unbekannter Gutschein wird still ignoriert', () => {
  assert.equal(berechnePreis([{ preis: 10, menge: 1 }], 'WINTER'), 10);
});

Ausgeführt wird das mit node --test. Die Erwartungswerte stammen nicht aus einer Spezifikation, sondern aus dem, was der Code heute liefert. Die beiden letzten Tests halten zwei Eigenheiten fest, die beim Umbau leicht verloren gingen: Der Abzug von 5 wird nach dem Gutschein berechnet, und ein unbekannter Gutschein löst keinen Fehler aus. Ob das so bleiben soll, ist eine fachliche Frage, die du nach dem Umbau getrennt klärst.

Beginne mit den Abläufen, die du als Nächstes ändern musst oder die geschäftskritisch sind. Wo sich einzelne Funktionen nicht isoliert testen lassen, helfen Tests von außen, etwa über die HTTP-Schnittstelle. Welche Testarten sich im kleinen Team lohnen, beschreibt die Teststrategie für kleine Teams.

Refactoring in kleinen Schritten

Martin Fowler definiert Refactoring als disziplinierte Technik, die innere Struktur von bestehendem Code zu verändern, ohne sein äußeres Verhalten zu ändern. Ihr Kern ist eine Folge kleiner, verhaltenserhaltender Umformungen. Weil jede einzelne klein ist, geht laut Fowler weniger schief.

Für Legacy Code heißt das praktisch:

SchrittWas du tust
1. AbsichernCharakterisierungstests für den Bereich schreiben, den du änderst
2. Eine Umformungetwa eine Funktion herauslösen, eine Variable umbenennen, eine Bedingung vereinfachen
3. PrüfenTests laufen lassen, bei Rot zurück zum letzten grünen Stand
4. Committenjede Umformung einzeln, damit sie sich zurücknehmen lässt
5. Wiederholenerst wenn die Struktur trägt, das Verhalten fachlich ändern

Struktur und Verhalten nicht im selben Schritt zu ändern, ist nach Einschätzung die wichtigste Regel. Wer beides mischt, kann bei einem roten Test nicht mehr sagen, ob der Umbau oder die neue Funktion schuld ist.

Neu schreiben oder schrittweise ersetzen?

Der Wunsch, alles neu zu schreiben, ist bei Legacy Code verständlich. Für größere Umbauten hat Martin Fowler ein Bild geprägt, das heute oft für schrittweise Modernisierung benutzt wird: die Würgefeige („Strangler Fig“). Sie wächst an einem Wirtsbaum, bis sie sich selbst trägt, und ersetzt ihn nach und nach. Übertragen heißt das: Neue Teile wachsen neben dem alten System, übernehmen Funktion um Funktion, bis das alte abgeschaltet werden kann.

VorgehenPasst nach Einschätzung, wenn
Refactoring im Bestanddie Architektur grundsätzlich trägt und nur Teile verwahrlost sind
schrittweises Ersetzen (Strangler Fig)einzelne Bereiche sich abgrenzen lassen, etwa hinter einer Schnittstelle
Neuschreibendas System klein ist, das Verhalten vollständig bekannt ist und Tests es beschreiben

Welche Altlasten du bewusst stehen lässt und welche du abbaust, ist eine Frage der Prioritäten.

Technische Schulden priorisieren

Technische Schulden kosten nicht durch hässlichen Code, sondern durch Zinsen: Jede Änderung in einem verwahrlosten Bereich dauert länger als nötig. Daraus folgt eine einfache Regel. Code, den niemand mehr anfasst, kostet dich nichts, auch wenn er schlecht lesbar ist. Code, den du jede Woche änderst, kostet dich jede Woche. Welche Dateien am häufigsten geändert werden, zeigt die Git-Historie:

git log --since="12 months ago" --name-only --format= | sort | uniq -c | sort -rn | head -20

Die Schnittmenge aus „wird ständig geändert“ und „tut beim Ändern weh“ ist deine Aufräumliste. Martin Fowler unterscheidet Schulden nach zwei Achsen: absichtlich oder versehentlich, umsichtig oder leichtsinnig. Eine bewusste Abkürzung mit Plan („wir liefern jetzt und bauen es um, sobald die Nachfrage feststeht“) ist eine Entscheidung; eine Abkürzung aus Bequemlichkeit ohne Notiz wird teuer.

Damit eine bewusste Abkürzung nicht vergessen wird, schreib beim Aufnehmen drei Dinge auf: was abgekürzt wurde, warum, und woran man merkt, dass es jetzt fällig ist, zum Beispiel „reicht bis 1.000 Datensätze, darüber paginieren“. Ein „TODO: aufräumen“ ohne Auslöser wird nie erledigt. Aufgeräumt wird dann entlang der Arbeit: Wer ein Feature in einem verwahrlosten Modul baut, räumt genau den Teil auf, den er dafür anfassen muss, und liefert beides zusammen. Ein großes Umbauprojekt ohne Nutzen für die Nutzer bleibt dagegen leicht halb fertig liegen, und dann gibt es zwei halbe Systeme statt eines.

Quellen und Stand

Alle Angaben wurden am 13. Oktober 2026 geprüft an:

Häufige Fragen

Was bedeutet Legacy Code?

Legacy Code ist bestehender Code, der sich schwer ändern lässt, weil Tests, Dokumentation oder Verständnis fehlen, oft übernommen von anderen Entwicklern. Der deutsche Verlag von Michael Feathers’ Buch zum Thema fasst es in drei Fragen: Lässt sich der Code leicht ändern, gibt es schnelle Rückmeldung bei Änderungen, und verstehst du ihn? Ein Nein reicht.

Was heißt Legacy Code auf Deutsch?

Das Cambridge-Wörterbuch übersetzt „legacy“ mit „Vermächtnis“, sinngemäß also übernommener oder geerbter Code. Die deutsche Ausgabe von Feathers’ Buch behält den englischen Begriff im Titel: „Effektives Arbeiten mit Legacy Code“.

Wie refaktoriert man Legacy Code?

Zuerst das heutige Verhalten mit Charakterisierungstests absichern, dann die Struktur in kleinen, verhaltenserhaltenden Schritten ändern und nach jedem Schritt testen und committen. Martin Fowler beschreibt Refactoring als Folge solcher kleinen Umformungen. Fachliche Änderungen kommen erst, wenn die Struktur trägt.

Was sind Charakterisierungstests?

Tests, die festhalten, was ein Code heute tatsächlich tut, statt was er tun soll. Die Erwartungswerte stammen aus der Ausführung des bestehenden Codes. Sie dienen als Sicherheitsnetz beim Umbau: Bleiben sie grün, hat sich das Verhalten nicht verändert.

Kann KI beim Verstehen von Legacy Code helfen?

Ja. Assistenten mit Repository-Zugriff wie Claude Code durchsuchen den Code selbst und erklären Architektur, Datenmodelle und Abläufe. Aussagen über Zusammenhänge solltest du dir mit Dateipfaden belegen lassen und stichprobenartig prüfen. Laufzeitverhalten und Produktionskonfiguration sieht die KI nicht.

Sollte man Legacy Code neu schreiben?

Meist nach Einschätzung nicht auf einen Schlag. Ein Neuschreiben muss das gesamte bestehende Verhalten nachbauen, auch das undokumentierte. Bei größeren Systemen ist schrittweises Ersetzen nach dem Strangler-Fig-Muster oder Refactoring im Bestand meist das geringere Risiko. Für kleine, gut verstandene und getestete Systeme kann ein Neuschreiben passen.

Newsletter

Eine Mail pro Woche. Alles Wichtige aus KI und Software.

Jeden Montag früh: die Videos der Woche und die wichtigsten Nachrichten aus KI und Software, kurz erklärt und mit Quelle.

  • Montags früh
  • Kostenlos
  • Abmelden mit einem Klick

Du bekommst zuerst eine Mail zum Bestätigen. Abmelden mit einem Klick. Datenschutz