Clean Code #6: Fehlerbehandlung — Exceptions richtig einsetzen
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
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.