- 1 Section
- 10 Lessons
- unbegrenzt
- Debugging & Fehleranalyse10
- 1.1Fehlertypen: Syntaxfehler, Laufzeitfehler, Logikfehler
- 1.2Systematische Fehlersuche: Duck-Debugging, Divide & Conquer
- 1.3Debugger-Grundkonzepte: Breakpoints, Watches, Call Stack
- 1.4Debugging in Java mit IntelliJ / Eclipse
- 1.5Debugging in Python mit VS Code und pdb
- 1.6Debugging in JavaScript: Browser DevTools
- 1.7Logging: Fehler protokollieren und auswerten
- 1.8Fehler in Datenbankabfragen finden
- 1.9Fehler in Netzwerkverbindungen und APIs debuggen
- 1.10Praxisaufgaben: Fehlerhafte Programme korrigieren
Systematische Fehlersuche: Duck-Debugging, Divide & Conquer
Nachdem du in L1 die Fehler-Typen kennengelernt hast, geht's jetzt um die Such-Methodik. Denn das größte Problem beim Debuggen ist selten der Bug selbst – sondern der Mensch davor, der planlos im Code rumstochert. Erfahrene Entwickler unterscheiden sich von Anfängern nicht durch tieferes Wissen, sondern durch strukturiertes Vorgehen.
In dieser Lektion lernst du zwei Klassiker: Rubber Duck Debugging (klingt absurd, funktioniert verblüffend gut) und Divide & Conquer bzw. Bisection – die mathematisch effizienteste Methode überhaupt um einen Fehler zu lokalisieren. Beide sind sprachunabhängig und funktionieren in Java, Python, JavaScript und überall sonst.
1) Warum überhaupt Methodik?
Der häufigste Fehler beim Debuggen heißt „guessing and changing": man rät was schief sein könnte, ändert eine Stelle, schaut ob's hilft, ändert die nächste, und so weiter. Das ist extrem ineffizient und führt oft dazu dass man neue Bugs einbaut während man den alten sucht. Schlechte Entwickler sind 10x langsamer als gute – nicht weil sie weniger Wissen haben, sondern weil sie planlos arbeiten.
2. Eine Zeile zufällig ändern
3. Programm laufen lassen
4. Geht immer noch nicht? Andere Zeile ändern
5. „Jetzt geht's. Aber warum?" — keine Ahnung
6. Bug taucht später woanders wieder auf
Resultat: Stunden verschwendet, der echte Bug ist nicht gefixt, dazu vermutlich neue Bugs eingebaut.
2. Hypothese aufstellen: „Vermutlich X."
3. Hypothese gezielt prüfen (Wert ansehen)
4. Bestätigt? → Fix gezielt einbauen
5. Widerlegt? → Nächste Hypothese
6. Nach Fix: Regression-Test schreiben
Resultat: Du verstehst was schief war, der Bug ist wirklich weg, du hast einen Test der ihn nie wieder zulässt.
2) Schritt 1: Problem präzise beschreiben
Klingt banal, ist aber der wichtigste Schritt. Ein präzise beschriebenes Problem ist halb gelöst. Statt „die App geht nicht" beschreibe:
- Was tust du? „Ich klicke auf den Login-Button..."
- Was erwartest du? „...erwarte dass das Dashboard erscheint..."
- Was passiert wirklich? „...stattdessen bleibt die Seite weiß und in der Konsole steht TypeError."
- Wann reproduzierbar? „Bei jedem Login, auf allen Browsern."
- Was hat sich geändert? „Vor dem letzten Deploy ging es noch."
Diese 5 Fragen liefern dir oft schon 50% der Lösung. Das ist auch das Format wie du gute Bug-Tickets schreibst und gute Stack-Overflow-Fragen. Wer sich darum drückt, beschäftigt sich nicht ernsthaft mit dem Problem.
3) Schritt 2: Reproduzieren
Ein Bug den du nicht zuverlässig reproduzieren kannst, kannst du nicht beheben. Vor allen Such-Schritten brauchst du einen Weg, den Fehler auf Knopfdruck auszulösen. Das nennt man Minimal Reproducible Example oder MRE.
Wenn der Fehler nur „manchmal" auftritt: finde die exakte Bedingung. Tritt er nur bei bestimmten Eingaben auf? Nur ab einem bestimmten Datum? Nur bei vielen Nutzern parallel? Mehr zu solchen schwer-reproduzierbaren Heisenbugs in der vorigen Lektion.
Ein MRE ist die kleinstmögliche Code-Variante die den Bug noch auslöst. Wenn dein ganzer Webshop crashes – kannst du das auf 10 Zeilen reduzieren? Genau diese 10 Zeilen sind dann das MRE. Drei Vorteile:
- Das MRE selbst ist meistens schon eine starke Diagnose
- Du kannst es im Bug-Ticket teilen, Kollegen können es nachstellen
- Daraus wird oft direkt der Regression-Test
4) Rubber Duck Debugging
Eine der ältesten und besten Debugging-Methoden klingt komisch: erklär den Code Schritt für Schritt einer Gummiente auf deinem Schreibtisch. Stell dir vor du sprichst zu jemandem der keine Ahnung hat. Sag laut was jede Zeile macht, was die Variablen enthalten sollten, was die Funktion zurückgibt.
calculateTotal bekommt einen Preis und eine Steuer rein. Dann addiere ich price und tax_rate... hmm, Moment. Ich addiere die Steuer als absolute Zahl, aber die ist als Prozent gemeint! Da liegt der Bug!"
Studien aus der Software-Forschung zeigen: Programmierer lösen ~40% ihrer schwierigen Bugs einfach durchs Erklären – noch bevor das Gegenüber etwas sagt. Daher steht in vielen Entwickler-Büros eine Gummiente.
5) Divide & Conquer / Bisection
Jetzt das mächtigste Werkzeug überhaupt: Bisection, auch binäre Suche. Die Idee ist mathematisch elegant: wenn du nicht weißt wo der Bug sitzt, halbiere den Suchraum. Prüfe die Mitte. Bug in oberer Hälfte? Halbiere die obere Hälfte. Und so weiter. Bei 1000 Zeilen Code findest du den Fehler in maximal 10 Schritten (log₂ 1000 ≈ 10).
Wie das funktioniert: an strategisch gewählten Stellen print-Statements einfügen (oder im Debugger Breakpoints setzen) und prüfen ob die Werte dort noch korrekt sind. Damit grenzt du den Fehler-Bereich systematisch ein:
6) Bisection mit git bisect
Eine besonders elegante Anwendung der Bisection-Idee: wenn du nicht weißt wann ein Bug eingeführt wurde. Du hast 500 Commits in den letzten Wochen, irgendwann ist ein Bug reingerutscht. Welcher Commit war's? git bisect macht binäre Suche auf der Commit-History:
Bei 500 Commits brauchst du nur log₂(500) ≈ 9 Tests. Und mit einem automatisierten Test (Skript das 0 bei OK / 1 bei Fehler zurückgibt) macht git bisect run skript.sh das sogar vollautomatisch. Eine der besten Erfindungen der Software-Entwicklung.
7) Die 5-Whys-Technik
Wenn du den Bug gefunden hast – stopp. Frage fünfmal „Warum?". Das ist eine Technik aus dem Toyota-Production-System, aber auch in der Software hilfreich:
- Bug: Steuer wird falsch berechnet
- Warum? Weil
+statt*in Zeile 42 steht - Warum? Weil der Programmierer das ohne Test geändert hat
- Warum? Weil es keinen Unit-Test für die Steuer-Funktion gab
- Warum? Weil unsere Test-Coverage-Regeln zu lasch sind
- Warum? Weil wir die CI-Pipeline nie auf 80% Coverage konfiguriert haben
Das echte Problem ist nicht der Operator – sondern die fehlende Testabdeckung. Wenn du nur den Operator fixt, tritt das Muster nächstes Mal woanders auf. Root-Cause-Analysis verhindert Wiederholung.
8) Die 7-Schritte-Methode
Damit du eine kompakte Checkliste hast: das ist die Methodik der Profis. Alle 7 Schritte sollten bei jedem ernsthaften Bug durchlaufen werden:
9) Häufige Anti-Muster
Was du nicht tun solltest beim Debuggen – Verhalten das jeder schon mal hatte, das aber nichts bringt:
- Cargo-Cult-Debugging: irgendwas ändern weil es „letztes Mal geholfen" hat. Ohne den Bug zu verstehen.
- Shotgun-Debugging: zehn Stellen gleichzeitig ändern, dann schauen ob's geht. Wenn's geht, weißt du nicht welcher Change war's. Wenn's nicht geht, hast du 10x das Risiko neuer Bugs.
- Aufgeben und Workaround: „Wenn ich diese Funktion immer mit dem Spezialwert aufrufe, klappt's." Den Bug nie verstanden – sitzt jetzt latent im Code.
- Code rauskommentieren: macht aus dem Programm ein anderes. Wenn dann etwas geht, sagt das nichts über den Original-Bug aus.
- Kein git commit vor Debug-Sessions: nach 2 Stunden Stochern bist du nicht mehr in dem Code in dem du angefangen hast. Vor Bug-Hunting: alles commiten / wegstashen.
10) Wenn du nicht weiterkommst
Manchmal bist du blockiert. Tipps:
- Pause machen. 20 Minuten Spaziergang. Das Gehirn arbeitet im Hintergrund weiter. Sehr oft kommt die Erleuchtung beim Kaffee.
- Schlafen. Ein Bug der dich um 2 Uhr nachts nervt, ist um 9 Uhr morgens oft offensichtlich.
- Kollegen fragen. Nicht „löse mein Problem", sondern „kannst du dir das mit mir anschauen?". Selbst durch das Erklären-Müssen (Duck-Effekt!) fällt dir's oft auf.
- Stack Overflow / Issue-Tracker. Fehlermeldung googeln. In 80% der Fälle hat jemand das schon erlebt. ABER: erst eigene Hypothesen aufstellen, dann fremde Lösungen kopieren.
- Dokumentation lesen. Wirklich. Profis lesen Dokus. Anfänger fragen Stack Overflow als erstes.
Was du nicht tun solltest: einen ganzen Tag verbissen am gleichen Bug bleiben, ohne Methodik. Wenn du nach 2 Stunden nichts hast, ist die Methodik falsch – nicht der Bug schwer.
11) Werkzeuge die diese Methodik unterstützen
Die Methodik ist Sprach- und Tool-unabhängig. Aber gute Werkzeuge erleichtern jeden Schritt – die nächsten Lektionen zeigen sie im Detail:
| Schritt | Werkzeug |
|---|---|
| Reproduzieren | Test-Frameworks (JUnit, pytest, Jest – siehe K54) |
| Hypothese prüfen | Debugger (Breakpoints, Watches) |
| Bisection im Code | print(), System.out.println(), console.log(), Breakpoints |
| Bisection in History | git bisect |
| Variable beobachten | IDE-Watches (siehe Java, Python, JS) |
| Späte Bugs | Logging + Log-Analyse |
| Regression-Schutz | Unit-Tests + CI-Pipeline |
Zusammenfassung
Debugging ist Methodik, nicht Magie. Profis sind schneller weil sie systematisch arbeiten, nicht weil sie mehr wissen. 7-Schritte-Methode: Beschreiben → Reproduzieren → Hypothese → Prüfen → Fix → Test → 5x Warum? Rubber Duck Debugging: Code laut einem Zuhörer erklären; ~40% der schweren Bugs lösen sich beim reinen Versprachlichen. Divide & Conquer / Bisection: Suchraum halbieren bei jedem Schritt – bei 1000 Zeilen Code findest du den Bug in 10 Tests. Werkzeuge: Print-Statements, Breakpoints, git bisect für Commit-History. 5-Whys: Root-Cause-Analyse – nicht nur Symptom, sondern strukturelles Problem finden. Anti-Muster: Cargo-Cult-Debugging, Shotgun-Debugging, Workarounds ohne Verstehen, Code rauskommentieren. Bei Blockade: Pause, Schlaf, Duck-Erklärung an Kollegen, Doku lesen. Diese Methodik ist sprachunabhängig – funktioniert in Java, Python, JavaScript, allem. Die nächste Lektion zeigt die Debugger-Grundkonzepte die diese Methodik elektronisch unterstützen.
