IT & Engineering
Software-Bugs und wie man sie schneller behebt
Die Kosten für das Debugging sind nicht für alle gleich. Die Kosten hängen nicht nur von Betriebs- und Servicegebühren ab, sondern auch davon, wie viele technische Schulden Sie haben.
Wenn wir von technischen Schulden sprechen, meinen wir die Kosten, die entstehen, wenn Unternehmen Probleme nicht beheben, die sie in der Zukunft beeinträchtigen werden. Das kann teuer werden. Aber was wäre, wenn es Muster oder Strategien gäbe, die das Entwicklungsteam nutzen könnte, um Bugs schneller zu erkennen und zu beheben?
In diesem Artikel lassen wir Taten sprechen und teilen eine Strategie, die sich für uns als am effektivsten erwiesen hat, um die Zeit zur Fehlerbehebung zu verkürzen.
Was ist ein Software-Bug?
Ein Software-Bug ist lediglich ein Coding-Fehler in einem Programm. Bugs kommen in einem einzelnen Programm oder einer Anwendung häufig genug vor, aber wenn wir das Ganze aus der Vogelperspektive betrachten und auf eine gesamte Plattform aus integrierten Diensten und vernetztem Code schauen, gleicht die Aussicht auf die Fehlersuche der sprichwörtlichen Nadel im Heuhaufen.
Einen Bug zu beheben, dauert länger, als eine Zeile Code zu schreiben. Es ist also sinnvoll, von Anfang an in Ihre Softwareentwicklung zu investieren. Richtig?
Die Kosten des Erfolgs: Softwareentwicklungsprozesse
Die Weichen für den Erfolg zu stellen, ist mit hohen Vorabkosten an Zeit und Ressourcen verbunden. Die Entwicklung individueller Software hat oft einen durchschnittlichen Preis von bis zu 250.000 $ , abhängig von Ihrer Infrastruktur und Ihren Anforderungen. Daher ist es für die Kosten entscheidend, Ihre Wartungsstrategie zu planen, anstatt ständig die Kosten für das Erstellen von neuem Code zu tragen.
Modellierung Ihrer Produktionsumgebung
Die Wahrscheinlichkeit, einen Bug in einer lokalen oder kontrollierten Umgebung zu reproduzieren, ist in etwa so hoch wie die Überlebenschance einer Maus auf einem Bauernhof voller Katzen – es sei denn, Sie haben Ihre Produktionsumgebung so genau wie möglich modelliert.
Diese Lösung kann viele Formen annehmen, einschließlich der gängigen Praxis, eine Staging- oder Entwicklungsumgebung zu erstellen. Aber diese sind teuer, und langlebige Umgebungen neigen dazu, mit der Zeit abzuweichen. In Derricks 20 Jahren Erfahrung als Teil des Entwicklungsteams kommt nichts an die Möglichkeit heran, ein Problem auf dem lokalen Rechner zu reproduzieren. Das bedeutet, dass Sie die Möglichkeit brauchen, die Produktionsumgebung lokal so realistisch und reproduzierbar wie möglich zu modellieren.
Die erfolgreichsten Implementierungen davon haben wir in Architekturen gesehen, die Microservices oder, wie wir sie gerne nennen, „auf eine Domain beschränkte Dienste“ verwenden. In diesen Umgebungen ist es das Ziel, jeden abhängigen Dienst so bereitzustellen, dass der Dienst, an dem Sie arbeiten, nicht zwischen der Ausführung auf Ihrem lokalen Rechner und der in der Produktion unterscheiden kann.
Wie lässt sich das umsetzen? Der einfachste Weg ist, von den Service-Verantwortlichen zu verlangen, eine benutzerfreundliche Fake- oder Mock-Implementierung der öffentlichen Schnittstelle ihres Dienstes bereitzustellen. Dies ist ein perfektes Beispiel dafür, wie Sie die Weichen für den Erfolg stellen. Das Erstellen dieser Mockups kostet Zeit und Ressourcen. Wenn es in Ihrem Entwicklungsteam üblich ist, diese Tools bereitzustellen, wird das Ziel, Ihre Produktionsumgebung im Code zu modellieren, viel einfacher, da Ihnen die meiste harte Arbeit bei der Implementierung eines Fake- oder Mock-Dienstes abgenommen wird. Wenn nicht, müssen Sie den Preis dafür zahlen, dies selbst zu erstellen.
Beim Mocking Ihrer Dienste können Sie nicht einfach externe Schnittstellen nachbilden, Sie müssen auch die Transportprotokolle einbeziehen. Sie fragen sich vielleicht, warum? Wäre es nicht besser, kostspielige Remote-Aufrufe komplett zu vermeiden? Wäre das günstiger? Ja, aber es wäre nicht effektiv. Das Ziel ist es, die Produktionsumgebung so genau wie möglich zu modellieren; dies schließt den Transport ein, den Ihre App zur Kommunikation mit anderen Diensten verwendet. Egal ob HTTP, gRPC oder gutes altes TCP, kleine Abweichungen im Transport-Stack oder in den Marshalling-Bibliotheken können sehr subtile Auswirkungen darauf haben, wie Ihr Code in der Produktion funktioniert.
Erfahrungsbericht: Wie wir das bei Mailgun gelernt haben
Wir hatten einen Dienst, der die JSON-Marshalling-Bibliotheken wechselte, um die Leistung zu steigern, was jedoch unbeabsichtigt ein Problem beim Unicode-Parsing verursachte. Die Schnittstelle zu mocken, anstatt tatsächlich den Transport und die Marshalling-Funktion aufzurufen, hätte dieses Problem vor uns verborgen.
Ein weiteres unerwartetes Problem betraf eine Änderung an der Golang-DNS-Bibliothek, die unsere Produktionsumgebung komplett lahmgelegt hätte, wenn unsere funktionale Test-Suite nicht gewesen wäre – und ja, wir führen eine DNS-Mock-Implementierung für unsere Tests aus.
Erstellen von funktionalen Tests zur Suche nach Edge-Cases und zur Fehlerdiagnose
Okay, sobald wir also unsere Umgebung modelliert haben, sollten Sie als Erstes eine funktionale Test-Suite erstellen. Eine funktionale Test-Suite ist ein Container, der eine Reihe von Tests enthält, die bei der Ausführung und Berichterstattung von Testausführungsstatus helfen sollen. Sie können Ihren Suites Testfälle und Testpläne hinzufügen, um eine Vielzahl von Szenarien und Edge-Cases abzudecken. Je besser Ihre Suite ist, desto einfacher wird es, wenn Sie versuchen, ein Problem zu diagnostizieren.
Wenn das Problem, das Sie lösen möchten, als funktionaler Test existiert, können Sie die Werte im Test einfach (und schnell) durch die genauen Werte aus der Produktion ersetzen. Allein die lokale Reproduktion des Problems durch die Wiederherstellung der exakten Daten aus der Produktion kann zur Lösung führen. Wenn Sie es einfach gemacht haben, Daten aus der Produktion zu importieren oder zu simulieren, kann dies ein unschätzbar wertvolles Werkzeug in Ihrem Arsenal sein.
Wenn Sie keinen bestehenden funktionalen Test haben, der das Szenario abdeckt (was häufig vorkommt, da Nutzer oft nervige, ähm … einzigartige und unvorhergesehene Wege finden, Ihr System zu nutzen), müssen Sie diesen erstellen.

Ein Teil davon, die Weichen auf Erfolg zu stellen, besteht darin, funktionale Tests durchzuführen oder die Fähigkeit zu besitzen, schnell einen funktionalen Test zu erstellen. Unit-Tests (das Testen des kleinsten Code-Teils, der in einem System logisch isoliert werden kann) können nützlich sein, sobald Sie das Problem eingegrenzt haben. Ein funktionaler Test ermöglicht es Ihnen, ein grobes Bild Ihres Dienstes zu zeichnen und aus der Perspektive des Kunden zu verstehen, wie der Dienst in einem bestimmten Szenario funktioniert. Beim Debugging gilt: Tests des Produkts sind immer wichtiger als das Testen des Codes.
Warum Sie manuelle Tests vermeiden sollten und was stattdessen zu tun ist
Das Gegenteil von funktionalen Tests sind manuelle Tests. Manuelles Testen ist eine Art von Softwaretest, bei dem Testfälle manuell von einer testenden Person ausgeführt werden, ohne automatisierte Tools zu verwenden. Sie sollten sich immer auf funktionale Tests statt auf manuelle Tests konzentrieren, da ein manueller Test fehleranfällig und nicht reproduzierbar ist.
Viele in der Programmierung machen den Fehler, einen Dienst lokal auszuführen und Endpunkte manuell zu testen, um Probleme zu diagnostizieren. Da wir dies viele Jahre lang getan haben, können wir Ihnen aus erster Hand gar nicht sagen, wie oft wir ein Problem reproduziert haben, ohne genau zu wissen, wie wir das gemacht haben. Das hat bei dem Versuch, unsere Schritte zurückzuverfolgen, schon oft dazu geführt, dass wir am liebsten den Tisch umgeworfen hätten.
Debugging-Sitzungen dauern oft Stunden bis Tage, und bei manuellen Tests bleiben alle ausprobierten Szenarien undokumentiert und verschwimmen miteinander. Wenn jedoch all diese versuchten Szenarien als funktionale Tests geschrieben werden, kann jedes Szenario Teil der Historie der Debugging-Sitzung werden.
Es gibt ein Geheimnis bei funktionalen Tests. Das Schreiben funktionaler Tests zur Diagnose von Problemen ist eine Schlüsselstrategie, um die Zeit bis zur Fehlerbehebung zu minimieren. Diese Diagnosemethode funktioniert jedoch nicht, wenn das Schreiben des funktionalen Tests schwierig ist. Die Lösung: Vereinfachen Sie die Dinge vom ersten Schritt an.
Erstellen Sie eine Suite von Hilfsfunktionen und einfachen Assertions. Das Ziel ist es, Aktionen wie Retry-Schleifen und das Importieren von Daten-Suites SEHR einfach zu machen. Diese Hilfsfunktionen sollten zu den ersten Tools gehören, die beim initialen Schreiben der Software erstellt werden, um später als Support für neue funktionale Tests zu dienen.
Ein Beispiel: Wie wir das bei Mailgun gemacht haben
Die primäre /messages-API bei Mailgun wird von einem Dienst verarbeitet, den wir Influx nennen. Dieser Dienst verarbeitet sowohl HTTP- als auch SMTP-Nachrichten in unserem System und kommuniziert mit fast jedem anderen Dienst in unserer Suite für den Nachrichtenversand.
Infolgedessen haben wir stark in unsere funktionale Test-Suite investiert und unsere Produktionsumgebung für diesen sehr öffentlichen Teil unseres Dienstkatalogs im Code modelliert. Wir haben über 700 Tests erstellt. Die meisten davon sind funktional und auf einem lokalen Rechner in etwa sechs Minuten abgeschlossen.
Viele dieser Tests kamen während Debugging- oder Diagnosesitzungen hinzu und wurden dann in unsere Test-Suite aufgenommen. Wenn ein neues Szenario benötigt wird, wird es der funktionalen Suite hinzugefügt und bildet einen Schutz vor zukünftigen Regressionen.
Abschließende Gedanken
Die wahren Kosten der Softwareentwicklung liegen in der Wartung und Diagnose von Code. Deshalb ist es die zusätzliche Zeit und das Geld wert, die Weichen für den Erfolg zu stellen, indem Sie eine Suite von Tools erstellen, mit denen Sie die Lösungszeit verkürzen können.
Diese frühe Investition wird sich umso mehr auszahlen, je länger das Projekt andauert.
Hat Ihnen unser Ansatz zum Finden und Diagnostizieren von Software-Bugs gefallen? Wenn Sie mehr Inhalte wie diesen möchten, vergessen Sie nicht, unseren Newsletter zu abonnieren.