- David J. Agans’ Debugging behandelt die Grundlagen des Debuggings: wie man nach dem Auffinden eines Bugs die Ursache findet und ihn behebt, und bietet Prinzipien, zu denen nicht nur Einsteiger und Entwickler mit mittlerer Erfahrung, sondern auch Profis immer wieder zurückkehren können
- Das Buch ist in 9 Regeln gegliedert und verbindet Systemverständnis, Reproduktion von Fehlern, Beobachtung, Divide-and-Conquer, Kontrolle von Änderungen, Audit Trail, Überprüfung von Annahmen, externe Perspektiven und Verifikation von Fixes mit Praxisbeispielen
- Auch wenn ältere Technologien oder Beispiele außerhalb der Computerwelt vorkommen, liegt der Kern nicht bei einem bestimmten Tool, sondern in der Denkweise zum Eingrenzen von Problemen und ist daher auf Hardware- und Software-Debugging allgemein anwendbar
- Bei schwer zu behandelnden Bugs wie intermittierenden Problemen ist der Rat Make it Fail besonders nützlich, auch wenn es schade ist, dass der Begriff Heisenbug nicht direkt behandelt wird
- Anders als Bücher, die die Nutzung von GDB oder das Schreiben von Tests erklären, konzentriert sich dieses Werk auf das große Bild des Debuggings, sodass Tool-Bedienung und Regressionstests durch andere Materialien ergänzt werden müssen
Welches Problem das Buch adressiert
- David J. Agans’ Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems behandelt den Prozess, nach dem Entdecken eines Bugs die Ursache zu finden und ihn tatsächlich zu beheben
- Statt auf eine bestimmte Technologie oder ein bestimmtes Tool fokussiert es Debugging-Prinzipien, die für Software- und Computerhardware-Entwickler nötig sind
- Es eignet sich besonders für Einsteiger und Entwickler mit mittlerer Erfahrung, hilft aber auch Erfahrenen dabei, in dringenden Situationen die leicht vergessenen Grundlagen wiederzubeleben
- Eine Stärke des Buchs ist, dass es Debugging, das man oft nur durch Erfahrung lernt, in Prinzipien und Fallbeispiele verdichtet vermittelt
Die 9 Debugging-Regeln
-
Verstehe das System
- Lies das Handbuch, erfasse die Gesamtstruktur und verstehe sowohl die Grundprinzipien als auch die Details des Verhaltens
- Prüfe zugleich, was die verwendeten Tools zeigen und was sie verbergen
-
Bring es zum Scheitern
- Führe das Problem erneut aus und beginne von vorn, um die Fehlerbedingungen direkt auszulösen
- Simuliere den Fehler nicht nur, sondern provoziere den echten Fehler, und finde die unkontrollierten Bedingungen, die intermittierende Bugs hervorrufen
- Halte alles fest, vertraue Statistiken nicht blind und akzeptiere, dass auch seltene Ereignisse tatsächlich eintreten können
- Lege Debugging-Tools nicht beiseite, sondern nutze sie, um das Problem sichtbar zu machen
-
Hör auf zu denken und sieh hin
- Bevor du mit Vermutungen eine komplexe Reparatur startest, beschaffe dir zuerst beobachtbare Daten
- Untersuche den Fehler und seine Details und baue interne Instrumentierung ein oder ergänze externe Messmittel
- Scheue dich nicht, tief einzusteigen, aber beachte den Heisenberg-Effekt, bei dem die Beobachtung selbst das Verhalten verändern kann
- Nutze Vermutungen nicht als Schlussfolgerung, sondern nur als Werkzeug, um den Suchraum einzugrenzen
-
Teile und herrsche
- Grenze den Suchbereich durch sukzessive Approximation ein und beurteile, auf welcher Seite der Bug liegt
- Nutze gut sichtbare Testmuster und beginne von einem schlechten Zustand aus, um die Ursache einzugrenzen
- Entferne zuerst bereits bekannte Bugs und Rauschen, um das Untersuchungsobjekt zu vereinfachen
-
Ändere immer nur eine Sache gleichzeitig
- Isoliere die entscheidenden Faktoren und verstehe vor dem Beheben, was falsch ist
- Ändere auch Tests nur einzeln und vergleiche sie mit dem Normalfall
- Prüfe schließlich, was sich seit dem letzten funktionierenden Zustand verändert hat
-
Pflege einen Audit Trail
- Halte als Audit Trail fest, was du in welcher Reihenfolge getan hast und welche Ergebnisse dabei herauskamen
- Auch scheinbar nebensächliche Details können die Ursache sein; dokumentiere Ereignisse so, dass ihre Zusammenhänge sichtbar werden
- Auch der Audit Trail des Designprozesses hilft beim Testen und sollte unbedingt festgehalten werden
-
Prüfe den Stecker
- Hinterfrage Annahmen, die du für selbstverständlich hältst, und prüfe alles von Anfang an erneut
- Beziehe auch die Tools, mit denen du das Problem suchst, selbst in den Testumfang ein
-
Gewinne eine neue Perspektive
- Wenn du allein nicht weiterkommst, gewinne durch andere Menschen oder andere Erklärungsweisen neue Einsichten
- Schon das Erklären des Problems gegenüber einer Schaufensterpuppe kann helfen, die Gedanken zu ordnen
- Nutze Expertise, höre auf Erfahrende und stelle das Teilen von Symptomen und das Einholen von Hilfe über den eigenen Stolz
-
Wenn es nicht behoben ist, ist es nicht behoben
- Verifiziere nach dem Fix, ob das Problem wirklich gelöst ist und ob deine Änderung die tatsächliche Ursache entfernt hat
- Probleme verschwinden nicht von selbst; behoben werden müssen sowohl die Ursache als auch der Prozess
Wie Beispiele die Prinzipien lebendig machen
- Eine bloße Regelliste wirkt trocken, aber die ausführlichen Erklärungen und Anekdoten aus der Praxis holen die Prinzipien in reale Situationen
- Viele Beispiele gehen bis in technische Details, was für manche Leser belastend sein kann
- Einige Beispiele behandeln ältere Technologien, doch da es im Kern um Prinzipien statt um eine bestimmte Technik geht, ist das kein großes Problem
- Nicht alle Beispiele drehen sich um Computing; auch ein unterhaltsamer Fall zu Hausverkabelung ist enthalten
- Wer von Computerhardware und Software gar nichts versteht, wird vielen Beispielen nur schwer folgen können
- Auf die Regelerklärungen folgen Geschichten, in denen mehrere Regeln zusammen angewendet werden, einfache Übungsaufgaben für Leser, Helpdesk-Tipps und abschließende Bemerkungen
Besonders starke Punkte und Grenzen
- Das Prinzip „Hör auf zu denken und sieh hin“ ist besonders wichtig
- Denn viele versuchen, Probleme anhand von Vermutungen zu beheben, bevor sie Daten gesammelt haben, die eine Hypothese stützen oder widerlegen könnten
- Auch „Wenn es nicht behoben ist, ist es nicht behoben“ bleibt stark im Gedächtnis
- Denn man muss nicht nur prüfen, ob etwas geändert wurde, sondern auch, was die Ursache war und warum es jetzt behoben ist
- Die Diskussion zu „Provoziere den Fehler, simuliere ihn nicht“ ist nicht ganz so klar wie andere Teile des Buchs, aber dennoch lohnend
- Intermittierende Probleme sind meist am schwierigsten zu behandeln, und das Buch gibt in Make it Fail direkte Ratschläge dazu
- Heisenberg wird erwähnt, aber der im Softwarebereich geläufige Begriff Heisenbug wird nicht behandelt
- Ein Heisenbug ist ein Bug, der verschwindet oder sein Verhalten ändert, sobald man versucht, ihn zu beobachten oder zu isolieren
Unterschiede zu anderen Materialien
- Das Buch unterscheidet sich von Tool-Handbüchern oder Testbüchern dadurch, dass es grundlegende Debugging-Prinzipien ins Zentrum stellt
- Richard Stallmans Debugging with GDB: The GNU Source-Level Debugger erklärt vor allem bestimmte Techniken oder Tool-Befehle
- Es gibt auch Materialien mit allgemeinen Ratschlägen wie Norman Matloffs Guide to Faster, Less Frustrating Debugging, doch sie sind nicht so breit angelegt wie Agans’ Buch
- Testbücher wie Boris Beizers Software Testing Techniques konzentrieren sich auf das Schreiben von Tests zum Auffinden von Bugs und behandeln die Behebung gefundener Bugs vergleichsweise weniger
- Nachdem ein Bug gefunden wurde, sollte ein Test dafür in die Regression Test Suite aufgenommen werden, aber Tests und Regressionstests liegen außerhalb des Rahmens dieses Buchs
Ergänzende Materialien und Kritikpunkte
- Auf der begleitenden Website debuggingrules.com gibt es Links zu verwandten Informationen sowie ein Poster mit den 9 Regeln zum Herunterladen und Ausdrucken
- Es ist schade, dass die für das Verständnis der Regeln wichtigen vollständigen Listen der Unterregeln weder im Buch noch auf einer einzelnen Seite der Website gesammelt sind
- Konkretere Ratschläge und Beispiele zu häufigen Tools und Problemtypen wie Symbolic Debugger, Digital Logic Probe oder ddd on gdb wären noch hilfreicher gewesen
- Ein separates Buch, das dieselben Regeln auf allgemeine Problemlösung außerhalb des Computings ausdehnt, wäre ebenfalls sinnvoll, aber die Beispiele in diesem Buch sind für nicht technische Leser zu spezialisiert
- Die Grundprinzipien wirken oberflächlich betrachtet selbstverständlich, doch Einsteiger müssen sie lernen und Erfahrene sich immer wieder in Erinnerung rufen, und dieses Buch eignet sich gut für beides
1 Kommentare
Hacker-News-Kommentare
Ich halte die Versuchung, vorhandenen kaputten Code durch zusätzliche „Fixes“ zum Laufen bringen zu wollen, für am schädlichsten.
Kaputter Code ist schwer zu reparieren, weil es zu viele mögliche Änderungsstellen gibt, und es ist viel einfacher, funktionierenden Code kaputtzumachen.
Wenn eine ganze Lichterkette zu Weihnachten nicht angeht, scheitert die Methode, eine Birne nach der anderen auszutauschen, sobald mehrere Birnen defekt sind.
Stattdessen sollte man mit einem minimal funktionsfähigen Beispiel beginnen und nach und nach Dinge hinzufügen, bis der Fehler auftritt; in der Praxis spart es oft Zeit, einfach von vorn anzufangen.
Bei einem schwer greifbaren Produktionsproblem hat ein Teil des Teams die problematische Routine neu geschrieben, und während der Rest weiter debuggt hat, wurde die Neufassung zuerst ausgerollt.
Mindestens einmal wurde der ursprüngliche Bug am Ende nie gefunden, weil man nicht unbegrenzt Zeit in ein „behobenes“ Problem stecken konnte.
Regel 0 lautet: Nicht in Panik geraten.
Deadlines und wütende Kunden behindern klares Denken; ein guter, vertrauenswürdiger Manager sollte Engineers vor diesem Druck abschirmen, damit sie sich auf die Problemlösung konzentrieren können.
Ein guter Manager hat uns in einen anderen Call verschoben und gesagt: „Ignoriert, was die da sagen, und konzentriert euch. Ich kümmere mich darum.“ Danach ist mein Respekt für diesen Manager deutlich gestiegen.
Wenn du keine Zeit hast, es richtig zu machen, warum glaubst du dann, Zeit zu haben, es zweimal zu machen?
Gemeint war, Dinge abzufangen, die von oben herunterkommen, damit Engineers sich auf ihre eigentliche Arbeit konzentrieren können.
Es ist viel besser, auf eine funktionierende Version zurückzugehen und dann ohne Krisendruck debuggen zu können.
Bei Nr. 4, „Teile und herrsche“, ist
git bisectsehr hilfreich.Wenn es einen funktionierenden Commit gibt und danach unter Dutzenden bis Hunderten Commits ein schlechter Commit steckt, kann man den problematischen Commit oder Code in wenigen Schritten eingrenzen.
Ein Nutzungsbeispiel findet sich unter https://nickjanetakis.com/blog/using-git-bisect-to-help-find....
Bei Live-Consulting in einer unbekannten großen Codebase habe ich es so schnell eingegrenzt; sonst wäre der Bereich, in dem etwas kaputt sein konnte, viel zu groß gewesen.
git bisecthalte ich mich an die Disziplin, dass jeder Commit, der in einen „echten“ Branch kommt, einzeln buildbar sein, die damals bekannten Tests bestehen und nach bestem Wissen deploybar sein muss.Das ist mir wichtiger als Prinzipien wie jeden Tastendruck aufzubewahren oder den letzten „Fixes.“-Commit stehen zu lassen. Solche Prinzipien machen die binäre Suche nämlich nutzlos.
Ich nutze es nicht oft, aber wenn es bei einem der größten, mysteriösesten Bugs nur einmal richtig trifft, liefert es auf einen Schlag Hinweise im Wert von Tagen; das reicht, damit es sich lohnt.
git bisect:ein kaputtes System mit einem funktionierenden System vergleichen und Unterschiede systematisch eliminieren, um den Defekt zu finden.
Das lässt sich auch außerhalb von Software oder Hardware anwenden; früher war es großartig, zwei gleiche Jetskis zu haben und beim Reparieren des einen mit dem anderen vergleichen zu können.
git bisectselbst als das allgemeinere Prinzip der binären Suche.Man kann es nicht nur auf Commit-Bereiche anwenden, sondern auch, wenn man den Systemraum aufteilt.
Wenn zum Beispiel ein 10-stufiger Workflow kaputt ist, kann man prüfen, ob Schritt 5 noch funktioniert, oder eingrenzen, ob es ein Hardwareproblem ist oder nicht.
Das ist besonders wichtig, wenn die Ursache des Problems möglicherweise gar kein Code-Commit in dem Repository ist, in dem man gerade bisectet.
bisectist großartig, aber man sollte die Philosophie und Denkweise dieses Buchs, also die „Regeln“, von praktischen Ratschlägen zu „Tools“ unterscheiden.Wer bei „Welches Tool soll ich verwenden?“ anfängt, ist im Nachteil gegenüber jemandem, der bei „Hat das früher nicht funktioniert?“ anfängt.
Die Welt ist voller Tools, und wenn man versucht, alle im Kopf zu speichern, wird man verrückt; besser ist es, zuerst die Philosophie zu verinnerlichen.
git bisect run. Ein wirklich erstaunliches kleines Tool.https://andrewrepp.com/git_bisect_run
Man sollte sicherstellen, dass man die richtige Datei auf der richtigen Maschine bearbeitet.
Heutzutage füge ich immer vorübergehend eine Zeile ein, die einen fatalen Fehler auslöst, um zu prüfen, ob es die richtige Datei ist und je nach Situation auch die richtige Zeile.
So stellt man sicher, dass die eigene Änderung tatsächlich eine Wirkung hat.
Es gibt weitere Regeln
„Es ist alles meine Schuld“: Es könnte zwar ein Compiler-Bug oder ein Hardwarefehler sein, aber das ist sehr selten; daher sollte man zuallererst die eigene Code-Änderung verdächtigen
„Wenn du einen Bug findest, suche auch seine Familie und Freunde“: Man sollte überlegen und prüfen, wo dasselbe Muster sonst noch aufgetreten sein könnte
„Optimiere zuerst für die Nutzer, an zweiter Stelle für die Wartungsprogrammierer und zuletzt für den Computer“
Eine Zusammenfassung gibt es unter https://blog.codinghorror.com/the-first-rule-of-programming-...
Meist findet man den Bug in der eigenen Logik, während man den fehlerauslösenden Code auf einen einfacheren Fall reduziert
Ein- oder zweimal blieb tatsächlich etwas übrig, das man den Entwicklern melden konnte; meist waren es Libraries mit höchstens ein paar Hundert Nutzern und entsprechend wenigen Tests
Am Ende war ich sehr erleichtert, als klar wurde, dass es kein unmöglich wirkender Codefehler war, sondern ein CPU-Problem
Am besten ist es trotzdem, die Ursache-Wirkungs-Kette noch ein paar Mal per binärer Suche einzugrenzen, um sicherzugehen
Der Rat „Verstehe das System: Lies das Handbuch, lies alles gründlich, kenne die Grundlagen, kenne die Roadmap, verstehe die Tools und schlage die Details nach“ klingt etwas seltsam
Es wirkt so, als solle man bei einem Bug im Code zuerst das komplette 700-seitige Handbuch der verwendeten Library lesen, dazu sieben einschlägige Bücher, und erst nach ein oder zwei Monaten den Bug ansehen
Ich frage mich, ob es auch nur einen Programmierer gibt, der diesen Rat tatsächlich befolgt
Atwood und Spolsky gründeten Stack Overflow erst 2008; es war eine Zeit, in der Leute Bücher unter Namen wie „Camel book“ kannten und Wissen einfach im Kopf hatten
0. https://stackoverflow.blog/2021/12/14/podcast-400-an-oral-hi...
„Alles gründlich lesen“ heißt nicht unbedingt: „Lies zuerst das komplette 700-seitige Handbuch der verwendeten Library“
Wenn es ein Problem mit
git bisectgibt, kann man statt ein paar Stack-Overflow-Schnipsel zusammenzukleben unter https://git-scm.com/docs/git-bisect das Thema etwas tiefer verstehenDer Fehler ist nur, zu denken, das Ergebnis von mehreren Monaten Arbeit sei, einen einzelnen Bug irgendwie zu flicken
Das Ziel ist, möglichst alle Bugs dieser Art wirklich zu beheben und zu erreichen, dass sie gar nicht erst geschrieben werden
Die Alternative ist, in ein unbekanntes System wie mit dem Fallschirm abzuspringen, ohne Verständnis an verschiedenen Stellen herumzufummeln, bei grünen Tests einen PR zu öffnen und zu hoffen, dass man nichts weiter kaputt gemacht hat; so etwas regelmäßig zu tun, ist nahezu ein Albtraum
Nebenbei: Wenn das Handbuch der verwendeten Library 700 Seiten hat, verwendet man vermutlich die falsche Library
Als Schritt 10 sollte man den Bug vielleicht in die CI-Tests aufnehmen, um Regressionen zu verhindern
Man sollte prüfen, dass CI vor dem Fix fehlschlägt und nach dem Fix durchläuft
Vor allem, weil es Commits gab, die über fünf Jahre alt waren, und weil es als Komponente/Library etliche seltsame Hacks für IE enthielt
Manche Tests dauern lange oder sind kompliziert zu schreiben und müssen gewartet werden; außerdem muss man akzeptieren, dass eine Testsuite nicht alle Randbedingungen prüft
Das kann bedeuten, dass ein Bug, der es bis in die Produktion geschafft hat, erneut auftreten kann; wenn es aber ein einfacher Fehler war, ist seine Wiederholungswahrscheinlichkeit möglicherweise nicht höher als die von Hunderten anderer potenzieller Fehler
Am Ende hängt es von der Situation ab, und Tests zu schreiben ist nicht kostenlos
Ich habe unzählige Male gesehen, dass eine tiefere Ursache wieder aktiv wurde und dasselbe Problem zurückkehrte, oder dass niemand wusste, dass ich etwas behoben hatte, sodass alle weiterhin Workarounds nutzten, als gäbe es den Bug noch
Wenn man nach „behoben!“ noch eine kurze Notiz und eine Root-Cause-Analyse hinterlässt, hilft das anderen
Wie schnell kann CI noch laufen, nachdem sich Tests über lange Zeit angesammelt haben, und ist es langfristig weiterhin sinnvoll, sie beizubehalten?
Wenn man diese Denkweise Kindern, sich selbst oder anderen vermitteln möchte, empfehle ich mindestens Folgendes:
The Martian by Andy Weir https://en.wikipedia.org/wiki/The_Martian_(Weir_novel)
https://en.wikipedia.org/wiki/Zen_and_the_Art_of_Motorcycle_...
https://en.wikipedia.org/wiki/The_Three-Body_Problem_(novel)
To Engineer Is Human - The Role of Failure in Successful Design By Henry Petroski
https://pressbooks.bccampus.ca/engineeringinsociety/front-ma...
https://en.wikipedia.org/wiki/Surely_You%27re_Joking,_Mr._Fe...!
Besonders gut ist das Konzept der „gumption traps“.
Wenn man in die Falle der Wertstarrheit geraten ist, wird man ohnehin langsamer; also sollte man das Tempo bewusst herausnehmen, noch einmal auf bereits durchlaufene Stellen schauen und prüfen, ob die Dinge, die man für wichtig hielt, wirklich wichtig waren.
Auch einfach eine Weile auf die Maschine zu schauen, ist nicht falsch; die Passage, dass irgendwann ein kleiner Fakt vorsichtig anfragt, ob man interessiert sei, wenn man sie wie eine Angelschnur beobachtet, ist fast schon eine Lebensregel.
Die Ereignisse passieren einfach über grobe logische Sprünge hinweg, und im Grunde wirkt es eher wie Fantasy mit einer sehr dünnen Schicht wissenschaftlicher Wortspiele darüber.
Vor ein paar Jahren habe ich einen ähnlichen Text geschrieben. Das hier erwähnte ursprüngliche Buch hatte ich damals nicht gelesen.
https://explog.in/notes/debugging.html
Julia Evans’ Debugging-Zine ist ebenfalls sehr gut: https://wizardzines.com/zines/debugging-guide/
Auch nach erfolgreichem Debugging ist die Arbeit nicht vorbei.
Der Kern von „Three Questions About Each Bug You Find“ <http://www.multicians.org/thvv/threeq.html> besteht aus drei Fragen:
Gibt es diesen Fehler auch an anderer Stelle, welcher nächste Bug verbirgt sich hinter diesem Bug, und was muss man tun, um solche Bugs zu verhindern?