Kolumne

Knigge für Softwarearchitekten: Der Kammerjäger

Gernot Starke, Peter Hruschka

© Shutterstock.com/bilha golan

Zum Umbauen und Ändern von Software gehört nach unserer Erfahrung auch die Suche nach Fehlern – daher erwarten Sie diesmal Tipps zum zielgerichteten Debuggen.

Die Situation sollten Sie kennen: Kurz vor Auslieferung tritt ein kritischer Fehler auf. Die spontane Reaktion lautet „kann gar nicht sein“. Weder ein „Clean Build“ noch ein Reboot helfen. Sie haben nur noch wenig Zeit, bis die Software ausgeliefert werden soll – Ihr Chef sitzt Ihnen im Nacken. Die ganze Situation ist Ihnen unangenehm, leider fehlt Ihnen (noch) jegliche Idee, woher dieses Fehlverhalten kommen könnte. Es schlägt die große Stunde des Kammerjägers – die Bugs dieser Welt erzittern vor Furcht.

Selbstverständlich?

Selbstverständlich sollten unsere Ratschläge selbstverständlich sein. Wir erleben aber ständig, dass vermeintliche Selbstverständlichkeiten rundweg ignoriert werden – nicht nur bei der Fehlersuche. Wir möchten Sie hier vorsichtig an einige (vermeintlich) selbstverständliche Verhaltensweisen erinnern – denn eine kurze Auffrischung der wichtigen Verhaltensweisen könnte Lady Entwicklerin und Gentleman Entwickler die nächste Debugging-Session verkürzen.

Fundamental!

Wir gehen davon aus, dass Sie eine klare, präzise Fehlerbeschreibung haben. Sie wissen genau, in welcher Situation der Fehler aufgetreten ist, mit welcher Version der Software, auf welcher Hardware, mit welchem Betriebssystem und welcher Netztopologie. Sie kennen den genauen Wortlaut der Fehlermeldung, besitzen Zugriff auf eventuelle Logfiles, ebenso auf die Eingabe- oder Eingangsdaten, die den Fehler verursacht haben. Nein? Dann wird es Zeit, diese Informationen zu beschaffen. Wir wollen einen konkreten Bug jagen, nicht ein unbestimmtes Gerücht.

Chill’ mal

Können Sie sich einen echten Jäger vorstellen, der hektisch durch sein Jagdrevier keucht, laut flucht und alle paar Sekunden die Richtung ändert? Dessen Jagderfolg wird nahe Null bleiben. Daher empfehlen wir Ihnen ruhige Chillout-Musik und entspanntes Zurücklehnen – zumindest wir können dabei besser denken. Falls Ihnen nette Kollegen jetzt noch einen ordentlichen Espresso (oder grünen Tee) spendieren – umso besser. Schicken Sie Ihren hektischen Chef auf irgendeine wichtige Managementmission, stellen Sie Telefon, Chat- und Twitter-Client aus. Am besten suchen Sie sich eine gut gelaunte Kollegin zur Unterstützung.

Fehler nachvollziehen

Stellen Sie als Nächstes sicher, dass Sie den gesuchten Bug zuverlässig reproduzieren können. Beschaffen Sie sich die betroffene Version des Quellcodes, passende Testdaten und mögliche Hardware- und Betriebssystemkonfigurationen. Starten Sie das System und vollziehen sämtliche Schritte der Fehlersituation nach – ohne dass Sie irgendeine Änderung an Konfiguration oder Umgebung vornehmen. Sie müssen den Fehler in Ihrer Entwicklungsumgebung selbst erleben können – sonst werden Sie ihn niemals finden! Dadurch stellen Sie sicher, dass Sie den gleichen Bug jagen, den Ihre Anwender gemeldet haben.

In der idealen Welt checken Sie eine fertig konfigurierte virtuelle Maschine aus, in der sowohl Entwicklungs- wie Testumgebung inklusive Testdaten für die jeweilige Version enthalten sind. Aber wir möchten Sie ja nicht neidisch machen.

Vorsicht – (falsche) Annahme

Das Anti-Pattern der Fehlersuche lautet „falsche Annahme“: Sie nehmen an, der Fehler lauert in Baustein X. Ihre Suche und Gedanken kreisen um dieses X, weil Sie andere Ursachen kategorisch ausschließen. Viele Fehler sind in Wirklichkeit aber Konsequenzen (auch genannt „Folgefehler“). Die lenken geschickt vom eigentlichen Problem ab, das ursprünglich an einer völlig anderen Stelle in völlig anderer Form auftrat. Stellen Sie daher Ihre Annahmen ausdrücklich in Frage. Erklären Sie Ihre Annahmen beispielsweise einem Kollegen – dieses „vier-Augen-Prinzip“ hilft oftmals, irrige Annahmen als solche zu identifizieren.

Das bringt uns zum nächsten Tipp: nämlich ausreichendes explizites Wissen über das System.

Szenario kennen

Sie müssen im System den Kontext des Fehlers kennen, genauer: Sie müssen die Funktionen aller betroffenen Bausteine im System kennen, von Beginn der letztlich fehlerhaften Aufgabe bis zum Auftreten des Fehlers selbst.

Skizzieren Sie diesen Weg von Daten durch die Bausteine des Systems auf Papier. Machen Sie darin erwartete Zwischenergebnisse explizit – bevor Sie im Debugger diese Ergebnisse überprüfen (in arc42-Sprechweise: Analysieren Sie das Laufzeitszenario, in dem der Fehler auftrat). Das kann eine grobe Skizze sein – gibt Ihnen aber eine plausible Richtlinie, anhand derer Sie in die Tiefe von Quellcode abtauchen können – und dort die aktuellen Daten mit den von Ihnen erwarteten vergleichen.

Versuchen Sie also, Ihre Reiseroute im Debugger zumindest in groben Zügen vorab zu planen – anstatt sich auf eine Last-Minute-Überraschungsreise ohne festes Ziel zu begeben. Wenn Sie wissen, dass Sie nach Pisa reisen, halten Sie den schiefen Turm auch nicht fälschlicherweise für einen Fehler.

Gucken statt Denken

Sherlock Holmes hat in einem seiner Romane eine Grundregel erfolgreicher Kammerjäger formuliert: „It’s a capital mistake to theorize before one has data“. Schauen Sie genau hin. Noch genauer, lesen Sie das, was der Stacktrace oder der Debugger anzeigt, und nicht das, was Sie gerne dort finden möchten. Das menschliche Unterbewusstsein spielt uns in dieser Hinsicht gerne Streiche: Wir glauben fest, Dinge zu sehen – obwohl sie in Wirklichkeit ganz anders sind. Eine sehr überzeugende Vorstellung dieser Kategorie von Täuschung gibt Apollo Robbins – absolut sehenswert.

Im konkreten Debugging von Quellcode müssen Sie vor dem „Gucken“ oftmals instrumentieren, entweder über Logging, Tracing oder banales System.out. Wenn Sie Code nicht instrumentieren können, hilft Single-Stepping im Debugger.

Zum Thema „genau hingucken“ gefällt uns ein Sprichwort aus Sizilien, das wir für Sie ausgegraben haben: „Nur der Kochlöffel weiß genau, wie es unten im Kochtopf aussieht!“ (Agans, David J.: „Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems“, Amacom, 2002).

Intervallschachtelung: Divide and Conquer

Betrachten Sie Abbildung 1: Dort tritt in dem rot markierten Baustein 1 ein Fehler auf. Ihrer Einschätzung und Erwartung nach erstreckt sich die Verarbeitung des gesamten Ablaufs innerhalb des Systems auf den blau markierten Datenfluss, an dem die Bausteine 2 bis 6 beteiligt sind. Betreiben Sie jetzt gezieltes Halbieren: Überprüfen Sie Ihre Annahmen am Übergang einer Hälfte zur nächsten (hier: zwischen Baustein 4 und 3): Sofern dort Ihrer sachkundigen Einschätzung nach alles stimmt, muss der Fehler nach diesem Übergang entstehen – ansonsten vorher.

Durch solche Schachtelung können Sie auch in großen Systemen gezielt zur eigentlichen Fehlerursache vorstoßen – vorausgesetzt Sie kennen das Szenario (siehe oben!).

Abb. 1: Auf dem Weg zum Fehler

Nur EINE Sache ändern

Nehmen wir an, Sie haben den leidigen Bug auf wenige Zeilen Code eingegrenzt. Nun setzen Sie zur finalen Entsorgung an. Zentraler Tipp dazu: Ändern Sie immer nur eine Sache auf einmal – danach testen Sie erneut. Falls Sie mehrere Dinge gleichzeitig modifizieren, wissen Sie später nicht mehr, welche Änderung denn nun wirklich das Problem behoben hat.

David Agans nennt das typisch amerikanisch: „Schießen Sie mit einer Kugel auf Bugs, nicht mit einer Schrotflinte.“

Das Wunder der vier Augen

So manches Mal haben wir im Projekt Kollegen bei der Fehlersuche helfen dürfen. Das Vier-Augen-Wunder wurde hier auch als „Ich-erkläre-es-meiner-Frau“-Taktik bezeichnet: Erklären Sie das Problem einer völlig unbeteiligten Person – allein dadurch ergeben sich für Sie selbst neue Lösungsideen oder Gedankenanstöße. Der Grund dafür ist einfach – Außenstehende gehen oftmals mit weniger oder anderen Annahmen in eine Situation. Pair Debugging funktioniert ebenfalls großartig – allerdings müssen Sie sich dann die Jagdtrophäen teilen.

Fazit

Lokalisieren und Entfernen von Fehlern gehört zu unserem Handwerkszeug. Der rein mechanische Umgang mit Debuggern ist, den IDEs sei Dank, einfach geworden. Ein paar Grundregeln beherzigt, und schon wird das Kammerjägern zur reinen Freude. Wir alle wissen ja – nach der erfolgreichen Jagd sieht jeder Fehler banal und klein aus – auch wenn wir zwischendurch alle schon mal an Zufälle, Außerirdische oder dunkle Mächte geglaubt haben – weil Bugs sich manchmal sehr kreativ verstecken. In diesem Sinne, Happy Hunting!

Noch mehr Knigge gefällig? Dann werfen Sie einen Blick auf die letzte Folge „Der Saubermann“.

Aufmacherbild: Exterminator – A cartoon image of an exterminator forcing a flea to leave. Eps10 von Shutterstock / Urheberrecht: bilha golan

Geschrieben von
Gernot Starke
Gernot Starke
    Informatikstudium an der RWTH Aachen, Dissertation über Software-Engineering an der J. Kepler Universität Linz. Langjährige Tätigkeit bei mehreren Software- und Beratungsunternehmen als Softwareentwickler, -architekt und technischer Projektleiter. 1996 Mitgründer und technischer Direktor des „Object Reality Center“, einer Kooperation mit Sun Microsystems. Dort Entwickler und technischer Leiter des ersten offizielle Java-Projekts von Sun in Deutschland. Seit 2011 Fellow der innoQ GmbH.  
Peter Hruschka
Peter Hruschka
Informatikstudium an der TU Wien, Promotion über Echtzeit-Programmiersprachen. 18 Jahre im Rahmen eines großen deutschen Softwarehauses verantwortlich für Software-Engineering. Initiator, Programmierer und weltweiter Prediger und Vermarkter eines der ersten Modellierungstools. Seit 1994 selbstständig als Trainer und Berater mit den Schwerpunkten Software-/Systemarchitekturen und Requirements Engineering, bevorzugt im technischen Umfeld.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: