Use Cases (Aktualisiert: 2.6.2026)

Legacy-Code mit Claude Code sicher modernisieren

Ein praktischer Workflow für Legacy-Code mit Claude Code: Tests, TypeScript-Migration, Risiken, lauffähige Beispiele und Prüfung.

Legacy-Code mit Claude Code sicher modernisieren

Modernisierung scheitert, wenn sie zu groß startet

Legacy-Code ist nicht einfach alter Code. Es ist Code, dessen Verhalten schwer beweisbar ist, dessen Tests dünn sind und dessen Änderung schnell Bestellungen, Abrechnung, Support oder interne Abläufe beschädigen kann. Claude Code ist in dieser Lage hilfreich, aber nicht als Knopf für eine Massenneuschreibung.

Der sichere Ablauf ist: zuerst untersuchen, dann bestehendes Verhalten mit Tests festhalten, danach in kleinen Schritten refaktorisieren. Charakterisierungstests dokumentieren, wie sich das System heute verhält, auch wenn dieses Verhalten nicht ideal ist. Das Harness ist das Arbeitsgerüst für den Agenten: Tests, Berechtigungen, Befehle, Projektregeln und Review-Checklisten.

Die offizielle Dokumentation zu Claude Code common workflows beschreibt genau solche Schritte: Codebasis verstehen, refaktorisieren, mit Tests arbeiten, PRs erstellen und Worktrees nutzen. Für Legacy-Code ist das die richtige Haltung. Claude Code erhöht die Umsetzungsgeschwindigkeit, aber Menschen behalten Risiko, Priorisierung und Abnahme.

Drei typische Anwendungsfälle

Modernisiere nicht zuerst den hässlichsten Code, sondern den Code, bei dem weniger Unsicherheit echten Schaden verhindert.

AnwendungsfallZielWas Claude Code leisten kannWas Menschen prüfen müssen
Bestellung, Rechnung, ZahlungFehler bei Geld und Kundenstatus vermeidenVerhalten kartieren, Tests ergänzen, Grenzfälle findenSteuern, Rabatte, Rundung, Compliance
JavaScript zu TypeScriptKünftige Änderungen sicherer machenTypen hinzufügen, any reduzieren, Typfehler stapelweise behebenÖffentliche API und Build-Kompatibilität
Callback-Hölle oder riesige FunktionenWartbarkeit verbessern, Verhalten behaltenVerantwortlichkeiten trennen, Namen vorschlagen, Diffs erklärenFehlerpfade, Retries, Seiteneffekte

Bei ClaudeCodeLab nutze ich dieses Muster für alte Publishing-Skripte, Checkout-Integration und Content-Transformationen. Entscheidend ist nicht, dass Claude Code schnell Code schreibt. Entscheidend ist, dass jede Änderung klein genug für ein ernsthaftes Review ist und eine Prüfung dahintersteht.

flowchart LR
  A[Untersuchen] --> B[Verhalten mit Tests sichern]
  B --> C[Klein refaktorisieren]
  C --> D[Typen und Dependencies ordnen]
  D --> E[Menschliches Risiko-Review]
  E --> B

Mit einem Read-only-Audit beginnen

Der erste Prompt sollte Änderungen ausdrücklich verbieten. In dieser Phase brauchst du eine Landkarte des Systems, keinen Patch auf unvollständiger Grundlage.

Lies @src/legacy und @test.
Ändere noch keine Dateien.

Gib dieses Audit zurück:
1. Wichtige Dateien und Verantwortlichkeiten
2. Externe I/O, Datenbank, API, Dateischreibzugriffe und Seiteneffekte
3. Verhalten, das kompatibel bleiben muss
4. Fehlende Tests und riskante Zweige
5. Sicherste Reihenfolge für kleine Änderungen

Wenn eine Regel unklar ist, schreibe "menschliche Bestätigung nötig" statt zu raten.

Die Seite How Claude Code works erklärt, dass Claude Code Dateien lesen, Befehle ausführen und Code bearbeiten kann. Diese Fähigkeit ist nützlich, aber im ersten Schritt muss sie begrenzt werden. In alten Systemen kann eine logisch wirkende Verbesserung einen Rückgabewert verändern, auf den externe Nutzer angewiesen sind.

Minimalbeispiel zum Ausführen

Das Beispiel ist bewusst klein, folgt aber demselben Ablauf wie eine echte Modernisierung.

mkdir legacy-modernization-demo
cd legacy-modernization-demo
npm init -y
npm install -D vitest typescript @types/node
npm pkg set type="module"
npm pkg set scripts.test="vitest run"
npm pkg set scripts.typecheck="tsc --noEmit"
mkdir -p src/legacy test

Der alte Order Processor mischt Validierung, Berechnung und Rückgabeformat in einer Datei. Das ist kein künstlich schlechter Code, sondern eine typische Form von Code, der lange funktioniert hat und deshalb ungern angefasst wird.

// src/legacy/orderProcessor.js
export function processOrder(order) {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return { status: "error", message: "items is required" };
  }

  const subtotal = order.items.reduce((sum, item) => {
    return sum + item.price * item.qty;
  }, 0);

  const discount = order.customer?.type === "vip" ? subtotal * 0.1 : 0;

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

Nun sichern wir das bestehende Verhalten mit Vitest. Diese Tests beschreiben nicht das perfekte Modell, sondern den Vertrag, den wir nicht unabsichtlich brechen dürfen.

// test/orderProcessor.test.ts
import { describe, expect, it } from "vitest";
import { processOrder } from "../src/legacy/orderProcessor.js";

describe("processOrder legacy behavior", () => {
  it("calculates total for a regular customer", () => {
    const result = processOrder({
      items: [
        { id: "A1", qty: 2, price: 1000 },
        { id: "B2", qty: 1, price: 500 }
      ],
      customer: { id: "C1", type: "regular" }
    });

    expect(result).toMatchObject({
      status: "confirmed",
      total: 2500,
      discount: 0
    });
  });

  it("applies a 10 percent VIP discount", () => {
    const result = processOrder({
      items: [{ id: "A1", qty: 1, price: 10000 }],
      customer: { id: "C2", type: "vip" }
    });

    expect(result.status).toBe("confirmed");
    expect(result.total).toBe(9000);
    expect(result.discount).toBe(1000);
  });

  it("returns an error when items are empty", () => {
    const result = processOrder({
      items: [],
      customer: { id: "C3", type: "regular" }
    });

    expect(result.status).toBe("error");
    expect(result.message).toContain("items");
  });
});

Führe npm test aus. Erst wenn die Tests grün sind, sollte Claude Code bearbeiten.

Lies @src/legacy/orderProcessor.js und @test/orderProcessor.test.ts.
Migriere diesen Code nach TypeScript, während die Tests grün bleiben.

Regeln:
- Öffentlichen Funktionsnamen processOrder behalten
- status, total, discount und message kompatibel halten
- Erst Typdefinitionen ergänzen, dann Verantwortlichkeiten trennen
- Danach npm test und npm run typecheck ausführen
- Erklären, welche Kompatibilität jeder Diff bewahrt

Eine besser prüfbare TypeScript-Struktur

Nach der Modernisierung sind Typen, Validierung, Berechnung und Orchestrierung getrennt. Ziel ist nicht Abstraktion um der Abstraktion willen, sondern überprüfbare Geschäftslogik.

// src/orderTypes.ts
export type CustomerType = "regular" | "vip";

export type OrderItem = {
  id: string;
  qty: number;
  price: number;
};

export type OrderInput = {
  items: OrderItem[];
  customer: {
    id: string;
    type: CustomerType;
  };
};

export type OrderResult =
  | {
      status: "confirmed";
      total: number;
      items: OrderItem[];
      discount: number;
    }
  | {
      status: "error";
      message: string;
    };

// src/validators.ts
import type { OrderInput } from "./orderTypes";

export function validateOrder(order: OrderInput | null | undefined): string | null {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return "items is required";
  }
  return null;
}

// src/calculators.ts
import type { CustomerType, OrderItem } from "./orderTypes";

export function calculateSubtotal(items: OrderItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0);
}

export function calculateDiscount(subtotal: number, customerType: CustomerType): number {
  return customerType === "vip" ? subtotal * 0.1 : 0;
}

// src/orderProcessor.ts
import { calculateDiscount, calculateSubtotal } from "./calculators";
import type { OrderInput, OrderResult } from "./orderTypes";
import { validateOrder } from "./validators";

export function processOrder(order: OrderInput): OrderResult {
  const validationMessage = validateOrder(order);
  if (validationMessage) {
    return { status: "error", message: validationMessage };
  }

  const subtotal = calculateSubtotal(order.items);
  const discount = calculateDiscount(subtotal, order.customer.type);

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

Passe den Testimport auf ../src/orderProcessor an und führe npm test sowie npm run typecheck erneut aus. Ein Diff dieser Größe lässt sich noch sauber prüfen. Wenn derselbe PR auch Verzeichnisse verschiebt, Dependencies aktualisiert, Formatierung ändert und Fachbegriffe umbenennt, sinkt die Review-Qualität.

Dependency-Updates getrennt behandeln

Ein häufiger Fehler ist, Refactoring und Major-Upgrades zu mischen. Wenn Tests brechen, ist unklar, ob TypeScript, eine API-Änderung, der Bundler oder die Geschäftslogik schuld ist.

Lass Claude Code zuerst nur inventarisieren.

Lies package.json und den Lockfile.
Aktualisiere noch nichts.

Gib eine Tabelle aus mit:
- package
- aktuelle Version
- empfohlene Zielversion
- ob es ein Major-Upgrade ist
- URL des offiziellen Migration Guides
- wahrscheinlich betroffene Dateien
- Tests, die vor dem Update ergänzt werden sollten

Für destruktive oder breite Änderungen sollte das Berechtigungsmodell konservativ bleiben. Die Dokumentation zu Claude Code permissions ist Pflichtlektüre, bevor Migrationen, Löschungen oder Deployments automatisiert werden. Agentengeschwindigkeit hilft nicht, wenn sie nötige Zustimmungspunkte überspringt.

Konkrete Fallen

Die erste Falle ist Refactoring ohne Tests. Saubererer Code ist trotzdem eine Regression, wenn Rundung, Rabattlogik oder Fehlermeldungen verändert werden.

Die zweite Falle ist, Vorschläge von Claude Code als Geschäftsregel zu akzeptieren. Eine idiomatischere Rückgabe ist nicht automatisch kompatibel mit bestehenden Clients.

Die dritte Falle ist der riesige PR. Typmigration, Logiktrennung, Dependency-Updates, Dateiverschiebungen und Formatierung sollten möglichst getrennt werden.

Die vierte Falle ist zu schnelle Verbesserung der Fehlerbehandlung. In alten Systemen können null, bestimmte Strings oder ungewöhnliche HTTP-Status Teil eines Vertrags sein.

Die fünfte Falle ist fehlende Dokumentation. Lass Claude Code Kompatibilitätsnotizen, manuelle Prüfschritte und Rollback-Plan in die PR-Beschreibung schreiben.

Kombiniere diesen Ablauf mit dem Guide zur Refactoring-Automatisierung, TDD mit Claude Code und automatischer Dokumentationsgenerierung. Für Teams gehören verbotene Bereiche, Testbefehle, Domänenbegriffe und Review-Regeln in CLAUDE.md Best Practices.

ClaudeCodeLab unterstützt Teams mit Claude Code Training, CLAUDE.md Templates und Beratung zur Modernisierung bestehender Produkte. Das Ziel ist keine KI-Neuschreibung, sondern ein wiederholbarer Prozess, in dem Tests, Berechtigungen und menschliche Reviews zusammenarbeiten.

Geprüftes Ergebnis

Ich habe den Ablauf als legacy-modernization-demo durchgespielt: alten JavaScript Order Processor erstellen, drei Verhalten mit Vitest sichern, nach TypeScript migrieren und anschließend npm test sowie npm run typecheck ausführen. Der größte Gewinn kam durch die Trennung von Read-only-Audit und Bearbeitungsprompt. Die Diffs waren kleiner, und Gesamtbetrag, VIP-Rabatt sowie Fehler bei leerer Bestellung waren leichter zu prüfen.

#Claude Code #Legacy-Code #Refactoring #technische Schulden #Modernisierung
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.