Legacy-Code ist die stille Bremse jedes Projekts. Gewachsene Strukturen, undokumentierte Logik, Magic Numbers und Funktionen die alles auf einmal tun — das kennt jedes Team. Refactoring ist die Antwort. Aber Refactoring ohne Strategie ist gefährlich. Claude Code hilft dabei, systematisch, sicher und nachvollziehbar zu modernisieren.
Refactoring-Philosophie: Verhalten erhalten, Struktur verbessern
Die goldene Regel des Refactorings lautet: Das externe Verhalten darf sich nicht ändern. Was vorher funktioniert hat, muss danach genauso funktionieren — nur der interne Aufbau wird besser. Martin Fowlers Definition ist heute noch gültig: Refactoring ist eine Abfolge kleiner, kontrollierter Transformationen, die den Code lesbarer, wartbarer und erweiterbarer machen.
PrinzipDie drei Säulen sicheren Refactorings
- Kleine Schritte: Jede Änderung ist ein atomarer Schritt. Kein Big Bang Rewrite.
- Tests als Netz: Vor jedem Refactoring müssen bestehende Tests grün sein.
- Kontinuierliche Verifikation: Nach jedem Schritt testen — nicht erst am Ende.
Claude Code unterstützt diesen Ansatz durch seine Fähigkeit, den gesamten Kontext einer Codebasis zu lesen und schrittweise Transformationen vorzuschlagen, die das Verhalten korrekt erhalten. Statt eines manuellen Diff-Marathons führt Claude Code durch jeden Schritt und erklärt, was sich ändert und warum.
Analysiere diese Datei und liste alle Refactoring-Kandidaten auf. Ordne sie nach Risiko (niedrig/mittel/hoch) und beschreibe, was sich am externen Verhalten ändert und was gleich bleibt.
Häufige Refactoring-Patterns
PatternExtract Function
Lange Funktionen sind der häufigste Code-Smell. Wenn eine Funktion mehr als 20–30 Zeilen hat, macht sie vermutlich zu viel. Extract Function teilt sie in benannte, wiederverwendbare Einheiten auf.
Vorher: Monolithische Funktion
JavaScript (vorher)
function processOrder(order) {
// Validierung
if (!order.items || order.items.length === 0) throw new Error('Keine Items');
if (!order.customerId) throw new Error('Kein Kunde');
// Rabatt berechnen
let discount = 0;
if (order.items.length > 10) discount = 0.1;
if (order.isPremium) discount = 0.2;
// Summe
const total = order.items.reduce((s, i) => s + i.price, 0);
return { total: total * (1 - discount), discount };
}
JavaScript (nachher)
function validateOrder(order) {
if (!order.items?.length) throw new Error('Keine Items');
if (!order.customerId) throw new Error('Kein Kunde');
}
function calculateDiscount(order) {
if (order.isPremium) return 0.2;
if (order.items.length > 10) return 0.1;
return 0;
}
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function processOrder(order) {
validateOrder(order);
const discount = calculateDiscount(order);
const total = calculateTotal(order.items);
return { total: total * (1 - discount), discount };
}
PatternReplace Magic Numbers
Magic Numbers sind numerische Konstanten ohne Erklärung im Code. Sie machen den Code schwer lesbar und noch schwerer wartbar. Das Gegenmittel: benannte Konstanten.
JavaScript
// Vorher — was bedeuten 86400 und 0.15?
if (Date.now() - user.lastLogin > 86400000) sendReminder(user);
const fee = amount * 0.15;
// Nachher — selbstdokumentierend
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const PLATFORM_FEE_RATE = 0.15;
if (Date.now() - user.lastLogin > ONE_DAY_MS) sendReminder(user);
const fee = amount * PLATFORM_FEE_RATE;
Finde alle Magic Numbers in dieser Datei. Schlage sprechende Konstantennamen vor, gruppiere zusammengehörige Konstanten und erstelle eine eigene constants.ts Datei.
PatternRename für Klarheit
Variablennamen wie x, temp, data oder val sind technische Schulden in Reinform. Gutes Umbenennen kostet nichts und spart stundenlange Debugging-Sessions.
Tipp: Claude Code erkennt semantischen Kontext. Prompt: "Benenne alle Variablen und Funktionen in dieser Datei nach ihrem tatsächlichen Zweck um. Halte dabei die Namenskonventionen des Projekts ein."
Legacy JavaScript zu TypeScript migrieren mit Claude Code
Die Migration von JavaScript zu TypeScript ist eine der häufigsten Modernisierungsaufgaben in 2026. TypeScript gibt statische Typsicherheit, bessere IDE-Unterstützung und macht Bugs zur Compile-Zeit statt zur Laufzeit sichtbar.
LegacyMigrations-Strategie: Inkrementell, nicht Big Bang
Niemals die gesamte Codebasis auf einmal migrieren. Stattdessen:
- TypeScript-Compiler mit
allowJs: true und strict: false starten
- Kritischste Module zuerst (Datenmodelle, API-Layer)
- Schrittweise
strict: true aktivieren
- Externe Libraries mit
@types/* Paketen typisieren
TypeScript (migriert)
// Vorher (JavaScript)
function fetchUser(id) {
return fetch(`/api/users/${id}`).then(r => r.json());
}
// Nachher (TypeScript)
interface User {
id: string;
email: string;
name: string;
createdAt: Date;
}
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json() as Promise<User>;
}
Migriere diese JavaScript-Datei zu TypeScript. Leite Typen aus dem vorhandenen Code ab, erstelle Interfaces für alle Datenobjekte und füge strenge Return-Types hinzu. Behalte alle vorhandenen Kommentare.
Achtung: TypeScript-Migration ändert das Laufzeitverhalten nicht — aber any-Typen sind eine Falle. Claude Code neigt dazu, any als schnellen Ausweg zu nutzen. Prompt explizit: "Kein any. Falls der Typ unklar ist, erstelle ein Interface mit unknown-Feldern die dokumentiert sind."
Große Klassen aufteilen: Single Responsibility Principle
Das Single Responsibility Principle (SRP) besagt: Eine Klasse sollte genau einen Grund haben, sich zu ändern. In Legacy-Code finden sich oft "God Classes" die Datenhaltung, Geschäftslogik und Datenbankzugriffe gleichzeitig verwalten.
SRPBeispiel: UserManager aufteilen
Eine typische God Class mit zu vielen Verantwortlichkeiten:
- UserManager → aufgeteilt in:
- →
UserRepository (Datenbankoperationen)
- →
UserAuthService (Login, Tokens, Permissions)
- →
UserNotificationService (Emails, Push-Notificationen)
- →
UserValidator (Validierungslogik)
TypeScript
// Nach der SRP-Aufteilung: saubere Separation
class UserRepository {
constructor(private db: Database) {}
async findById(id: string): Promise<User | null> {
return this.db.query(`SELECT * FROM users WHERE id = $1`, [id]);
}
}
class UserAuthService {
constructor(private repo: UserRepository, private jwt: JwtService) {}
async login(email: string, password: string): Promise<string> {
const user = await this.repo.findByEmail(email);
if (!user || !await verifyHash(password, user.passwordHash))
throw new Error('Ungültige Zugangsdaten');
return this.jwt.sign({ userId: user.id });
}
}
Analysiere diese Klasse auf SRP-Verletzungen. Identifiziere alle Verantwortlichkeiten, schlage eine Aufteilungsstrategie vor und zeige, wie Dependency Injection die neue Struktur verbindet.
Tests vor dem Refactoring: Sicherheitsnetz erstellen
Ohne Tests ist Refactoring Glücksspiel. Bevor du auch nur eine Zeile änderst, braucht der Code ein Test-Sicherheitsnetz. Claude Code kann dabei helfen, charakterisierende Tests (auch "Golden Master Tests" genannt) zu erstellen — Tests die das aktuelle Verhalten einfrieren.
TestsDrei Schichten des Sicherheitsnetzes
- Unit Tests: Individuelle Funktionen isoliert testen
- Integration Tests: Zusammenspiel von Modulen prüfen
- Charakterisierende Tests: Aktuelles (auch fehlerhaftes!) Verhalten dokumentieren
TypeScript / Vitest
import { describe, it, expect } from 'vitest';
import { processOrder } from './order';
describe('processOrder — charakterisierende Tests', () => {
it('berechnet Standardpreis ohne Rabatt', () => {
const order = { customerId: 'c1', isPremium: false, items: [{ price: 100 }] };
expect(processOrder(order).total).toBe(100);
});
it('wendet 20% Rabatt für Premium-Kunden an', () => {
const order = { customerId: 'c2', isPremium: true, items: [{ price: 100 }] };
expect(processOrder(order).total).toBe(80);
});
it('wirft bei fehlendem customerId', () => {
expect(() => processOrder({ items: [{ price: 10 }] }))
.toThrow('Kein Kunde');
});
});
Erstelle charakterisierende Tests für diese Funktion. Decke alle erkennbaren Code-Pfade ab, inklusive Edge Cases und Fehlerfälle. Nutze Vitest. Kommentiere jeden Test mit dem Verhalten das er einfriert.
Workflow: Tests schreiben → Tests grün → Refactoring Schritt 1 → Tests grün → Commit → Refactoring Schritt 2 → und so weiter. Jeder grüne Zwischenstand ist ein sicherer Rollback-Punkt.
Claude Code Prompts für spezifische Refactoring-Tasks
Die Qualität des Refactorings hängt stark von der Qualität der Prompts ab. Diese praxiserprobten Formulierungen liefern konsistent gute Ergebnisse:
Prompt-BibliothekExtract Function
Extrahiere die Logik zwischen Zeile X und Y in eine eigene Funktion. Der Name soll aus dem Verhalten ableitbar sein. Prüfe, welche Parameter wirklich benötigt werden, und minimiere die Schnittstelle.
Prompt-BibliothekTypeScript Migration
Konvertiere diese Datei zu TypeScript. Strenge Typen, kein any. Erstelle Interfaces für alle Objekte. Nutze Union Types statt boolean-Flags. Exportiere alle Typen als eigene Interface-Datei.
Prompt-BibliothekTest-Coverage-Report
Analysiere diese Datei und erstelle eine Liste aller nicht-getesteten Code-Pfade. Schreibe dann Tests für die drei riskantesten Pfade zuerst und erkläre warum.
Prompt-BibliothekGod Class aufteilen
Diese Klasse verletzt das Single Responsibility Principle. Erstelle einen Refactoring-Plan: (1) Liste alle Verantwortlichkeiten, (2) schlage neue Klassennamen vor, (3) zeige das finale Dependency-Diagram, (4) implementiere schrittweise beginnend mit der Datenzugriffsschicht.
Refactoring-Workflow mit Claude Code im Alltag
- Review-first: Claude Code zuerst analysieren lassen, bevor man selber ändert
- Incremental commits: Nach jedem Refactoring-Schritt committen — nie große Batches
- Pair-Refactoring: Claude Code schlägt vor, Entwickler entscheidet und versteht
- Dokumentation mitpflegen: JSDoc / TSDoc direkt beim Refactoring aktualisieren
- Performance-neutral: Refactoring darf die Performance nicht verschlechtern — Benchmarks messen
Warnung: Claude Code kann Refactorings vorschlagen die semantisch korrekt wirken, aber subtile Verhaltensänderungen einführen — besonders bei asynchronem Code, closures und Nebeneffekten. Immer die Tests laufen lassen und bei kritischen Pfaden den Diff manuell prüfen.
Claude Code im Team einsetzen?
Starte jetzt kostenlos und erfahre, wie Claude Code dein Refactoring-Tempo verdoppelt — ohne Qualitätsverlust.
Kostenlos testen →