blog / refactoring-strategien

Refactoring Strategien: Wann, wie und warum Code umstrukturieren

Refactoring ist kein Luxus

Code muss sich ändern. Anforderungen entwickeln sich, Teams wachsen, neue Patterns entstehen. Refactoring ist die systematische Verbesserung von Code ohne Verhaltensänderung. Die zentrale Frage: Wann lohnt sich der Aufwand?

Wann refactoren?

Die 3-Strikes-Regel

  1. Erstes Mal: Implementiere direkt
  2. Zweites Mal: Dupliziere widerwillig
  3. Drittes Mal: Refactore

Code-Duplikation ist der stärkste Indikator. Drei ähnliche Implementierungen signalisieren ein fehlendes Abstraktionslevel.

Technische Indikatoren

Refactoring ist fällig bei:

  • Methoden über 50 Zeilen
  • Klassen mit mehr als 5 Verantwortlichkeiten
  • Verschachtelte if-else über 3 Ebenen
  • Duplikation identischer Logik
  • Kommentare, die Code “erklären müssen”
  • Schwierige Unit-Tests (Code ist nicht testbar)

Business-Trigger

Refactoring sollte Budget bekommen wenn:

  • Features dauern überproportional lange
  • Bug-Rate steigt kontinuierlich
  • Onboarding neuer Entwickler stockt
  • Code-Reviews eskalieren regelmäßig

Wie refactoren?

1. Sicherheitsnetz aufbauen

Vor jedem Refactoring:

# Tests müssen existieren und grün sein
npm test
# Coverage prüfen
npm run coverage

Ohne Tests ist Refactoring Glücksspiel. Der Code muss auf seine Funktionalität getestet werden.

2. Kleine Schritte

Schlecht:

// Komplettes Rewrite in einem Commit
function processOrder(order) {
  // 500 Zeilen neue Implementierung
}

Gut:

// Schritt 1: Extract Method
function validateOrder(order) { /* ... */ }
function calculateTotal(order) { /* ... */ }
function processPayment(order) { /* ... */ }

// Schritt 2: Refactor validateOrder
// Schritt 3: Refactor calculateTotal
// ...

Jeder Schritt muss einzeln commitbar sein. Tests bleiben grün.

3. Bewährte Refactoring-Patterns

Extract Method

// Vorher
function getUserData(userId) {
  const user = db.query(`SELECT * FROM users WHERE id = ${userId}`);
  if (!user) throw new Error('Not found');
  const posts = db.query(`SELECT * FROM posts WHERE user_id = ${userId}`);
  return { user, posts, postCount: posts.length };
}

// Nachher
function getUserData(userId) {
  const user = findUser(userId);
  const posts = getUserPosts(userId);
  return buildUserResponse(user, posts);
}

Replace Conditional with Polymorphism

// Vorher
function calculatePrice(product) {
  if (product.type === 'book') {
    return product.basePrice * 0.9;
  } else if (product.type === 'electronics') {
    return product.basePrice * 1.2;
  } else if (product.type === 'food') {
    return product.basePrice * 1.05;
  }
}

// Nachher
class Book {
  calculatePrice() { return this.basePrice * 0.9; }
}
class Electronics {
  calculatePrice() { return this.basePrice * 1.2; }
}
class Food {
  calculatePrice() { return this.basePrice * 1.05; }
}

Introduce Parameter Object

// Vorher
function createUser(firstName, lastName, email, age, city, country) {
  // ...
}

// Nachher
function createUser(userData) {
  const { firstName, lastName, email, age, address } = userData;
  // ...
}

4. Strangler Fig Pattern

Für große Legacy-Systeme: Neuer Code ersetzt Alten schrittweise.

// Phase 1: Routing-Layer einfügen
function processOrder(order) {
  if (shouldUseNewImplementation(order)) {
    return newProcessOrder(order);
  }
  return legacyProcessOrder(order);
}

// Phase 2: Feature Flag ausrollen
// Phase 3: Legacy-Code entfernen

Warum refactoren?

Messbare Vorteile

Entwicklungsgeschwindigkeit:

  • 30-50% schnellere Feature-Implementation nach Refactoring
  • 40% weniger Zeit in Debugging
  • 60% schnelleres Onboarding

Code-Qualität:

  • Zyklomatische Komplexität sinkt
  • Test-Coverage steigt
  • Bug-Density reduziert sich

Team-Moral:

  • Entwickler arbeiten lieber in sauberem Code
  • Weniger Frustration bei Code-Reviews
  • Höhere Retention-Rate

ROI-Berechnung

Refactoring-Kosten = Entwicklerzeit × Stundensatz
Einsparungen = (alte Feature-Zeit - neue Feature-Zeit) × kommende Features

Break-Even wenn: Einsparungen > Kosten

Bei aktiv entwickelten Systemen amortisiert sich Refactoring innerhalb von 2-4 Sprints.

Anti-Patterns vermeiden

1. Big Bang Refactoring

Komplettes Rewrite ist fast immer falsch. Inkrementell arbeiten.

2. Refactoring ohne Tests

Ohne Sicherheitsnetz führt Refactoring zu Bugs.

3. Gold Plating

Nur refactoren was nötig ist. Nicht “schöner machen” für ungenutzte Bereiche.

4. Refactoring während Features

Refactoring und neue Features trennen. Separate Commits, separate PRs.

Praktische Checkliste

Vor dem Refactoring:

  • Tests existieren und sind grün
  • Scope ist klar definiert
  • Team ist informiert
  • Branch erstellt

Während des Refactorings:

  • Kleine Commits (< 200 Zeilen)
  • Tests bleiben grün
  • Keine Verhaltensänderung
  • Code-Reviews früh einbinden

Nach dem Refactoring:

  • Alle Tests grün
  • Performance-Regression ausschließen
  • Dokumentation aktualisiert
  • Team-Wissen geteilt

Fazit

Refactoring ist systematische Arbeit, kein Perfektionismus. Die Regel: Code sollte besser sein als vor dem Anfassen. Nicht perfekt - besser.

Kleine, kontinuierliche Verbesserungen schlagen große Rewrites. Tests sind nicht optional. ROI lässt sich messen.

Erfolgreiche Teams reservieren 15-20% ihrer Sprintkapazität für technische Schulden. Refactoring ist Investment, kein Overhead.