Zurück zum Blog
Clean Code März 2026 7 Min Lesezeit

Clean Code #6: Fehlerbehandlung — Exceptions richtig einsetzen

MN

Michael Nikolaus

Vorstand & Softwarearchitekt, Minicon eG

Fehlerbehandlung ist kein Nebenthema — sie ist Teil der Kernlogik. Wie Sie mit Fehlern umgehen, bestimmt die Robustheit und Wartbarkeit Ihres gesamten Systems.

Exceptions statt Return-Codes

In modernen Sprachen gibt es Exceptions. Nutzen Sie sie. Return-Codes vermischen die Fehlerbehandlung mit der normalen Logik und machen den Code schwer lesbar.

// ❌ Return-Codes vermischen alles
int result = SaveToDatabase(order);
if (result == -1) { /* DB error */ }
else if (result == -2) { /* validation error */ }
else if (result == 0) { /* success */ }

// ✅ Exceptions trennen Fehler von Logik
try {
    await SaveToDatabase(order);
    await SendConfirmation(order);
} catch (DatabaseException ex) {
    logger.Error("DB save failed", ex);
    throw;
}

Spezifische Exceptions verwenden

throw new Exception("Something went wrong") ist nutzlos. Was ging schief? Was kann der Aufrufer tun?

// ❌ Generisch und nichtssagend
throw new Exception("Error processing order");

// ✅ Spezifisch und handlungsfähig
throw new InsufficientStockException(productId, requested, available);
throw new PaymentDeclinedException(orderId, reason);
throw new CustomerNotFoundException(customerId);

Null ist kein Fehlerbehandlungsmechanismus

null zurückzugeben bedeutet, die Fehlerbehandlung dem Aufrufer aufzubürden — der dann entweder eine NullReferenceException bekommt oder überall null-Checks einbauen muss.

// ❌ Null-Rückgabe
Customer? GetCustomer(int id) {
    var customer = _db.Customers.Find(id);
    return customer; // null wenn nicht gefunden
}
// Jeder Aufrufer muss prüfen:
var c = GetCustomer(42);
if (c == null) { /* was jetzt? */ }

// ✅ Besser: Exception oder spezieller Rückgabetyp
Customer GetCustomer(int id) {
    return _db.Customers.Find(id) 
        ?? throw new CustomerNotFoundException(id);
}

Catch auf dem richtigen Level

Fangen Sie Exceptions dort, wo Sie sie sinnvoll behandeln können. Ein Repository sollte keine HttpRequestException fangen — ein API-Client schon.

  • Controller-Level: Fehlercodes an den Client senden (400, 404, 500)
  • Service-Level: Business-Fehler abfangen und transformieren
  • Infrastructure-Level: Retries, Circuit Breakers

Fail Fast

Wenn etwas nicht stimmt, scheitern Sie sofort. Prüfen Sie Vorbedingungen am Anfang der Methode. Ein fehlgeschlagener Aufruf mit klarer Exception ist besser als ein korrumpierter Zustand, der drei Schichten weiter zu einem mysteriösen Bug führt.

void ProcessPayment(Order order, PaymentMethod method) {
    ArgumentNullException.ThrowIfNull(order);
    ArgumentNullException.ThrowIfNull(method);
    if (order.Total <= 0)
        throw new ArgumentException("Order total must be positive");
    
    // Ab hier: sicherer Zustand
}

Logging ≠ Handling

Ein häufiger Fehler: Exception fangen, loggen und dann verschlucken. Das ist keine Fehlerbehandlung — das ist Beweisvernichtung.

// ❌ Exception verschluckt
try { await ProcessOrder(order); }
catch (Exception ex) {
    _logger.Error(ex.Message); // geloggt...
    // ...aber der Aufrufer denkt, alles ist OK!
}

// ✅ Loggen UND weiterwerfen
try { await ProcessOrder(order); }
catch (Exception ex) {
    _logger.Error(ex, "Order processing failed");
    throw; // Aufrufer wird informiert
}

Fazit

Gute Fehlerbehandlung macht den Unterschied zwischen einem System, das "irgendwie läuft", und einem, das robust und debuggbar ist. Verwenden Sie spezifische Exceptions, scheitern Sie früh, und verschlucken Sie niemals Fehler.

Nächster Artikel: Clean Code #7: Testing — Tests als Qualitätsgarantie

Interesse geweckt?

Lassen Sie uns über Ihr Projekt sprechen

Das erste Gespräch ist kostenlos. Wir hören Ihnen zu und finden die beste Lösung für Sie.

Kontakt aufnehmen