Skip to main content

Vier Bugs, die mich je einen Tag kosteten — und jedes Mal logen meine Werkzeuge

Ein Crawler, der Fehler erfand, schwarze Screenshots, von einem leeren Ordner verdrängte Middleware und eine Render-Pipeline ohne Renderer. Vier verlorene Tage, eine Gewohnheit, die sie gerettet hätte.

Die Bugs, die mich am meisten Zeit gekostet haben, waren nie schwere Bugs. Schwere Bugs sind ehrlich — sie melden sich, sie liefern einen Stacktrace, man behebt sie. Teuer sind die, bei denen ein Werkzeug einen selbstbewusst anlügt und man den Tag damit verbringt, das Falsche zu debuggen.

Hier sind vier aus den letzten Monaten. Jeder hat ungefähr einen Tag gefressen. Inhaltlich haben sie nichts miteinander zu tun, in der Form sind sie identisch.

1. Der Crawler, der Fehler erfunden hat

Zur Kontrolle eigener Projekte lasse ich einen Website-Crawler laufen: kaputte Links, Redirect-Ketten, fehlende Header, solche Dinge. Bei einer Seite kam er mit einer Wand aus Rot zurück — fehlende Security-Header, strukturelle Probleme, Scores durchgehend nach unten gezogen.

Ich habe Stunden damit verbracht, Header zu prüfen, die nachweislich gesendet wurden. curl zeigte sie. Der Browser zeigte sie. Der Crawler bestand darauf, dass sie fehlen.

Die eigentliche Ursache: Der Crawler forderte die Seiten mit akzeptierter Brotli-Komprimierung an und konnte Brotli-Antworten nicht korrekt dekodieren. Alles, was er von diesen Seiten „las“, war Müll, also fiel alles durch, was er prüfte. Die Scores waren auf jeder einzelnen Seite zu niedrig.

Gzip zu erzwingen behob es sofort.

Was ich gelernt habe: Wenn ein Werkzeug ein Problem meldet, überprüfen Sie mit einem anderen Werkzeug, ob das Problem existiert, bevor Sie mit dem Reparieren anfangen. Ein einziges curl -I am Anfang hätte den Tag gerettet, weil es den Widerspruch sofort gezeigt hätte.

2. Die Screenshots, die schwarz waren

Ich baute ein Spiel in Unity und wollte, dass sich der Build im Rahmen einer automatischen Prüfung selbst fotografiert — Screenshot machen, anschauen, bestätigen, dass die Szene wirklich gerendert wird.

Zwei Fallen stapelten sich. Erstens: Die Kamera in eine Textur zu rendern erfasste die 3D-Szene, ließ aber die im Immediate Mode gezeichnete Debug-Oberfläche stillschweigend weg — genau die Information, die ich prüfen wollte, war also nie im Bild. Als ich dann auf Bildschirmaufnahme über das Betriebssystem umstieg, lieferte macOS ein vollkommen gültiges, vollkommen schwarzes PNG. Kein Fehler. Keine Berechtigungsabfrage. Nur Schwarz.

Ein grüner Haken über einer Pipeline, die nichts fotografierte.

Was ich gelernt habe: Lassen Sie das Testobjekt den Nachweis selbst erzeugen, aus seiner eigenen Renderschleife heraus, und lassen Sie es laut scheitern, wenn das nicht geht. Eine automatische Prüfung, die „alles in Ordnung“ nicht von „ich habe nichts gesehen“ unterscheiden kann, ist schlechter als gar keine Prüfung, weil sie Zuversicht herstellt.

3. Die Middleware, die in Produktion lief und lokal nicht

Eine Next.js-Seite hatte Middleware für Weiterleitungen. In Produktion funktionierte sie. Lokal tat sie überhaupt nichts — keine Logs, keine Wirkung, als gäbe es die Datei nicht.

Ich prüfte die Matcher-Konfiguration, den Dateipfad, die Runtime, die Next-Version. Alles in Ordnung.

Die Ursache war ein leeres Verzeichnis src/pages, übrig geblieben aus einer alten Migration. Allein seine Existenz änderte, wie die Projektstruktur aufgelöst wurde, und die Middleware im Wurzelverzeichnis wurde aus dem Build verdrängt. Einen leeren Ordner zu löschen behob es.

Der Hinweis stand im Build-Manifest — die Middleware war dort schlicht nicht aufgeführt. Ich fand das nach Stunden des Nachdenkens über meinen Code, obwohl ich es in einer Minute hätte finden können, indem ich frage, was das Framework glaubt, gebaut zu haben.

Was ich gelernt habe: Wenn sich Verhalten zwischen zwei Umgebungen mit demselben Code unterscheidet, hören Sie auf, den Code zu lesen. Vergleichen Sie die beiden Build-Ausgaben. Der Unterschied liegt in dem, was gebaut wurde, nicht in dem, was Sie geschrieben haben.

4. Der Renderer, der keinen Renderer hatte

Beim Automatisieren von Blender im Headless-Betrieb zur Videoerzeugung hatte ich eine Pipeline, die von Anfang bis Ende fehlerfrei durchlief und keine Videodatei produzierte. Zwei getrennte Ursachen, nacheinander entdeckt: Dem Build, den ich ansteuerte, stand überhaupt kein FFmpeg-Writer zur Verfügung, und eine API, auf die ich mich für Animationskurven verlassen hatte, war in der Version, auf die ich aktualisiert hatte, entfernt worden. Und als dann Bilder herauskamen, machte das voreingestellte Farbmanagement aus flachen Cartoon-Farben ein ausgewaschenes Grau — was gar kein Bug ist, sondern eine Voreinstellung, die genau das tut, wofür sie gedacht ist, in einem Kontext, in dem sie falsch ist.

Was ich gelernt habe: Prüfen Sie bei jedem Headless-Werkzeug, ob die Fähigkeit überhaupt vorhanden ist, bevor Sie den Workflow bauen, der darauf beruht. Eine Zeile, die vorab fragt „kannst du dieses Format schreiben?“, statt es aus einer am Ende fehlenden Datei zu erschließen.

Die gemeinsame Form

Alle vier haben dieselbe Struktur:

  1. Ein Werkzeug meldete etwas — einen Fehler, einen Erfolg, ein leeres Ergebnis.
  2. Ich vertraute der Meldung.
  3. Die Meldung war ein Artefakt des Werkzeugs, keine Tatsache über mein System.

Die Lösung ist nicht, allem zu misstrauen. Sie ist billiger: Bevor Sie auf eine Meldung hin handeln, bestätigen Sie dieselbe Tatsache aus einer unabhängigen Richtung. Ein zweites Werkzeug, eine rohe Anfrage, die Build-Ausgabe, eine direkte Fähigkeitsabfrage. Es kostet eine Minute und bestätigt entweder Ihre Realität oder entlarvt die Lüge sofort.

Die zweite Gewohnheit ist strenger: Behandeln Sie einen stillen Erfolg als Fehlschlag, bis das Gegenteil bewiesen ist. Der schwarze Screenshot ging durch. Die leere Video-Pipeline beendete sich sauber. Die Middleware scheiterte, indem sie nichts tat. In jedem Fall war die Art des Systems, mir zu sagen, dass etwas nicht stimmt, das Schweigen — und den Tag verlor ich nur, weil ich Schweigen als Zustimmung gelesen habe.

Nichts davon ist raffiniert. Es ist einfach der Unterschied zwischen dem Debuggen des eigenen Codes und dem Debuggen der eigenen Instrumente — und dem Wissen, was von beidem man gerade tut.