EMZETT.
Login

Debugging

Kurz: Der systematische Prozess, einen Fehler (Bug) in einem Programm zu finden, seine Ursache zu verstehen und ihn zu beheben.

Genauer: Gängige Techniken reichen von einfachen Ausgaben zur Kontrolle von Variablenwerten bis zu spezialisierten Debugger-Tools, mit denen sich die Ausführung Schritt für Schritt anhalten und der aktuelle Zustand aller Variablen inspizieren lässt (Breakpoints). Guter Testcode und aussagekräftige Fehlermeldungen verkürzen die Zeit, die Debugging kostet, erheblich.

Im Detail

Der Begriff “Bug” (Käfer) für einen Programmfehler geht auf eine Anekdote aus den 1940er-Jahren zurück, als in einem frühen Computer buchstäblich eine Motte einen Fehler verursachte — seitdem hat sich “debuggen” als Begriff fürs Fehlerbeheben durchgesetzt.

Die gängigsten Debugging-Techniken, von einfach bis fortgeschritten:

  • Print-Debugging: Ausgaben (print(), console.log()) an strategischen Stellen im Code einfügen, um zu sehen, welchen Wert eine Variable an einem bestimmten Punkt tatsächlich hat. Einfach und universell einsetzbar, aber mühsam bei komplexen Abläufen und muss hinterher wieder entfernt werden.
  • Debugger mit Breakpoints: Ein spezialisiertes Werkzeug (meist in die IDE integriert) hält die Programmausführung an einer markierten Stelle an, ohne den Code zu verändern. Man kann dann Schritt für Schritt weiterlaufen lassen und dabei live sehen, welchen Wert jede Variable gerade hat.
  • Logging: Wie Print-Debugging, aber dauerhaft im Code verankert und mit Schweregraden (DEBUG, INFO, WARNING, ERROR) — sinnvoll, um auch in Produktion nachvollziehen zu können, was ein Programm getan hat, ohne bei jedem Problem extra Debug-Ausgaben einbauen zu müssen.
  • Stack-Trace lesen: Bei einer Exception zeigt der Stack-Trace genau, an welcher Codezeile der Fehler auftrat UND über welche Kette von Funktionsaufrufen das Programm dorthin gelangt ist — oft der schnellste Weg zur Fehlerursache, wenn man lernt, ihn richtig zu lesen (meist von oben nach unten, die eigentliche Fehlerstelle im eigenen Code steht oft weiter oben als Bibliotheks-interne Aufrufe).

Ein bewährter systematischer Ansatz ist das schrittweise Eingrenzen des Problems: Statt wahllos an vielen Stellen gleichzeitig nach dem Fehler zu suchen, wird der verdächtige Codebereich per Zweiteilung eingegrenzt (ähnlich einer binären Suche) — funktioniert die erste Hälfte des Programms korrekt? Wenn ja, liegt der Fehler in der zweiten Hälfte, und man wiederholt das Vorgehen dort.

Guter, aussagekräftiger Code beugt vor: sprechende Variablennamen, kleine, testbare Funktionen mit klarer Verantwortung, und präzise Fehlermeldungen (die genau sagen, WAS erwartet und WAS tatsächlich erhalten wurde) reduzieren die Zeit, die für Debugging überhaupt nötig ist, drastisch — die beste Debugging-Strategie ist letztlich, möglichst wenig zu debuggen zu haben.

Siehe auch: Errors, Exceptions