- 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
Fehlertypen: Syntaxfehler, Laufzeitfehler, Logikfehler
Wer programmiert, macht Fehler. Das ist kein Versagen, sondern Naturgesetz – selbst die besten Entwickler erzeugen täglich Bugs. Der Unterschied zwischen guten und schlechten Programmierern ist nicht ob sie Fehler machen, sondern wie schnell sie diese finden und beheben. Genau darum geht es in diesem Kurs: Debugging – die systematische Kunst der Fehlersuche.
Bevor du Fehler effizient suchen kannst, musst du verstehen welche Arten von Fehlern es überhaupt gibt. Denn jede Sorte verhält sich anders, wird zu unterschiedlichen Zeitpunkten entdeckt und braucht eine andere Such-Strategie. In dieser Lektion lernst du die drei klassischen Fehlerkategorien: Syntaxfehler, Laufzeitfehler und Logikfehler. Plus zwei spezielle Sub-Typen die im Alltag wichtig sind.
1) Die drei Fehler-Hauptklassen
Alle Programmierfehler lassen sich grob in drei Kategorien einteilen – das ist seit den 1960er Jahren so und gilt für jede Programmiersprache: Java, Python, JavaScript, C++, alles. Die Unterscheidung hilft dir, sofort die richtige Such-Strategie zu wählen:
2) Syntaxfehler – die offensichtlichen
Ein Syntaxfehler ist ein Verstoß gegen die Grammatik der Programmiersprache. Du tippst etwas das in dieser Sprache keinen Sinn ergibt: vergessene Klammer, falsche Schlüsselworte, Tippfehler bei Variablennamen. Der gute Punkt: solche Fehler findet der Compiler oder Interpreter sofort. In modernen IDEs siehst du sie sogar schon während des Tippens unterstrichen.
Schau dir Beispiele aus drei Sprachen an – jedes hat einen Syntaxfehler, den die rote Wellenlinie markiert:
Klassiker in Java: das Semikolon fehlt am Zeilenende. Der Compiler weigert sich überhaupt erst zu kompilieren – kein .class-File entsteht. In Java ist das Semikolon Pflicht, in Python dagegen optional – jede Sprache hat ihre eigenen Regeln.
Hier fehlt die schließende Klammer. Python merkt das beim Parsen und gibt einen klaren Fehler – mit Zeilennummer und einem hilfreichen ^-Marker. Tools wie VS Code mit Python-Extension zeigen das schon beim Tippen mit roter Wellenlinie.
JavaScript: Tippfehler beim Keyword return (statt return steht retrun). JavaScript versucht retrun als Funktion zu interpretieren – kann das aber nicht – und liefert einen verwirrenden Fehler. Tippfehler in Schlüsselworten sind gemein, weil die Fehlermeldung oft nicht auf die echte Ursache zeigt.
3) Laufzeitfehler – die mit Drama
Laufzeitfehler (oder „Runtime Errors") sind fies: dein Code kompiliert ohne Beschwerden, läuft brav los, und stürzt dann mitten im Betrieb ab. Häufig erst nach Sekunden oder Minuten – wenn ein bestimmter Code-Pfad erreicht wird. Typische Beispiele:
- NullPointerException: du greifst auf ein Objekt zu das es nicht gibt (Java, C#)
- IndexOutOfBoundsException: Array-Zugriff jenseits der Grenze
- Division durch Null: mathematisch undefiniert
- FileNotFoundException: die Datei die du öffnen willst, gibt's nicht
- OutOfMemoryError: das Programm wollte mehr RAM als verfügbar
- StackOverflowError: zu tiefe Rekursion (zu viele verschachtelte Funktionsaufrufe – mehr in K39 Rekursion)
Wenn findUser niemanden findet, gibt es null zurück. Der Aufruf user.getName() stürzt dann ab. Der wichtige Hinweis: die Stacktrace verrät dir die genaue Zeile UND wie du dort hingekommen bist. MainApp.run hat getName aufgerufen, der wiederum stürzte ab. Diese Information ist gold wert für die Fehlersuche – mehr dazu wenn wir den Call-Stack in L3 anschauen.
4) Logikfehler – die heimtückischsten
Diese Sorte ist die gefährlichste. Dein Code läuft ohne jeden Fehler durch, aber das Ergebnis ist falsch. Kein Crash, keine Exception, keine rote Wellenlinie – nichts. Nur dass die berechnete Steuer um 3 Cent abweicht. Oder die Sortierung verkehrt herum ist. Oder der Login einen Nutzer hereinlässt der eigentlich keine Berechtigung hat (Sicherheitsrelevant!).
Der Programmierer hat + statt * geschrieben. Aus 100€ + 19% MwSt sollten 119€ werden – tatsächlich wird 100.19€ herauskommen. Python beschwert sich nicht. Das ist der Albtraum jedes Buchhalters: Software die plausibel aussieht, aber falsch rechnet.
Logikfehler entdeckt man nur durch:
- Tests die das erwartete Ergebnis prüfen (Unit-Tests, Integrationstests, mehr in K54)
- Code-Reviews wo Kollegen den Code durchgehen
- Beobachtung in Produktion: Beschwerden von Nutzern
- Logging kritischer Werte – mehr in L7
5) Klassifizierungs-Spiel
Jetzt bist du dran. Schau dir die Code-Snippets an und entscheide: welche Fehler-Art liegt vor? Damit übst du das schnelle Erkennen – eine Schlüsselkompetenz beim Debugging:
6) Wann werden welche Fehler entdeckt?
Die Erkennungszeit ist entscheidend für den Aufwand der Behebung. Je früher ein Bug gefunden wird, desto günstiger ist er. Eine Faustregel aus der Software-Industrie (geht auf den IBM-Engineer Barry Boehm zurück): jeder Schritt später kostet etwa das 10-fache. Ein im IDE gefangener Bug kostet vielleicht 1€, derselbe in Produktion 1000€ oder mehr.
7) Weitere wichtige Fehler-Sub-Typen
Die drei Hauptkategorien sind ein gutes Grundgerüst, aber im Alltag gibt es noch ein paar Spezialformen die du kennen solltest:
Warnungen sind keine echten Fehler – der Code läuft, aber der Compiler sagt „Ich habe Bedenken". Beispiel: ungenutzte Variable, deprecated API. Warnungen einfach zu ignorieren ist ein Anfänger-Fehler. Profis behandeln sie oft als Fehler (Compiler-Flag „warnings as errors") weil sich darin häufig Logikfehler verbergen.
Heisenbugs sind Bugs die verschwinden sobald du sie suchst. Klassiker: ein Bug der nur in Production auftritt, aber nicht in Dev. Oder einer der nur dann da ist wenn du KEINEN Debugger anhängst. Meist haben sie mit Threading, Timing oder Umgebungs-Variablen zu tun.
Off-by-One-Fehler: ein Klassiker bei Schleifen. for (int i=0; i<=n; i++) statt i<n – führt zu Index-Überschreitung. Statistisch eine der häufigsten Bug-Sorten überhaupt. „Es gibt nur zwei schwere Probleme in der Informatik: Cache-Invalidierung und Off-by-One-Fehler" – ein bekannter Programmierer-Witz.
Race Conditions: tritt nur unter bestimmten Timing-Bedingungen auf, etwa wenn zwei Threads gleichzeitig auf eine Variable zugreifen. Schwer zu reproduzieren, weil das Timing oft zufällig ist.
Memory Leaks: das Programm gibt Speicher nicht mehr frei und wächst langsam an. Stürzt vielleicht erst nach Tagen ab. In Sprachen ohne Garbage Collection wie C oder C++ häufiger, aber auch in Java/Python möglich.
8) Die Fehler-Meldung lesen lernen
Eine Sache die Anfänger oft falsch machen: sie lesen die Fehlermeldung nicht. Stattdessen scrollen sie sofort zu Stack Overflow oder fragen den Kollegen. Dabei steht die Antwort in 80% der Fälle schon in der Fehlermeldung – man muss sie nur lesen.
Was steht da drin?
- Exception-Typ: NullPointerException – also: irgendwo war ein
nullwo keiner sein sollte - Message: „Cannot invoke String.length() because user.getName() is null" – sehr explizit: der Rückgabewert von
user.getName()war null - Stacktrace: in
UserService.java, Zeile 42 in der MethodeprocessUser. Aufgerufen vonMainApp.run, Zeile 17. Gestartet inmain, Zeile 8.
Mit diesen drei Informationen kannst du den Bug in Sekunden lokalisieren: gehe zu UserService.java Zeile 42, schau dir user.getName() an, und überleg warum dort manchmal null rauskommt. Die Fehlermeldung ist dein bester Freund – nicht dein Feind. Mehr zum Stacktrace-Lesen in L3 Debugger-Konzepte.
9) Was bringt mir die Klassifizierung?
Sobald du den Fehler-Typ kennst, ist auch die Such-Strategie klar:
| Fehler-Typ | Hauptwerkzeug | Such-Aufwand |
|---|---|---|
| Syntax | IDE-Marker lesen, Fehlermeldung interpretieren | Sekunden |
| Laufzeit | Stacktrace lesen, Debugger anwerfen | Minuten bis Stunden |
| Logik | Tests schreiben, Werte loggen, Bisection | Stunden bis Tage |
| Off-by-One | Werte an Schleifen-Grenzen mit print oder Watch prüfen | Minuten |
| Heisenbug | Logging maximal hoch drehen, Production-Replica bauen | Tage bis Wochen |
| Race Condition | Thread-Dumps analysieren, Synchronisation prüfen | Tage |
| Memory Leak | Profiler einsetzen (VisualVM, dotMemory, ...) | Stunden bis Tage |
Die nächsten Lektionen vertiefen die Such-Strategien für jeden Fehler-Typ. Die nächste Lektion zeigt die systematische Vorgehensweise die unabhängig von Sprache und IDE funktioniert. Danach die Werkzeuge: Debugger-Grundlagen in L3, dann sprachspezifisch für Java, Python und JavaScript.
Zusammenfassung
Drei Hauptfehler-Klassen: Syntaxfehler (Compiler verweigert, sofort erkannt), Laufzeitfehler (Code läuft los, crashed dann – NullPointer, OutOfBounds, FileNotFound, etc.), Logikfehler (Code läuft fehlerfrei, Ergebnis ist falsch – die gemeinste Sorte). Spezialformen: Warnungen, Heisenbugs, Off-by-One, Race Conditions, Memory Leaks. Erkennungszeit nach Shift-Left-Prinzip: Compile (billig) → Dev-Run → QA/Test → Production (sehr teuer). Pro Schritt später ca. 10x teurer. Wichtigste Anfänger-Regel: Fehlermeldungen LESEN. Exception-Typ + Message + Stacktrace gibt dir den Bug in 80% der Fälle direkt. Such-Strategie pro Typ: Syntax → IDE-Marker; Laufzeit → Stacktrace + Debugger; Logik → Tests + Logs + Bisection. Diese Kategorisierung gilt sprachübergreifend für Java, Python, JavaScript und alle anderen.
