Serie: Programmieren lernen mit KI

Der Samstag, an dem ich die Stubs fand

Siebenundvierzig gefundene Stubs, auditiert und abgebaut.

Das Audit, das die Stubs fand Saubere API-Oberfläche, echt aussehende Docs, Platzhalter-Rümpfe int do_real_work() { // TODO: implementieren return 0; } STUB sieht fertig aus, tut nichts AUDIT 47 Stubs zutage gefördert als Issues erfasst fünf zu fehlschlagenden Tests gemacht
Ein Audit-Durchgang macht unsichtbares Gerüst zu einer erfassten, testbaren Warteschlange.

Agenten werden Code ausliefern, der kompiliert, die CI besteht und nichts tut. Der Unterschied zwischen einer Engine, die man vorführen kann, und einer Runtime, auf die Partner bauen können, ist die Disziplin, die Stubs zu finden, bevor sie es tun.

Heute sollte eigentlich der Tag sein, an dem ich Tests schreibe.

Das ist ein Satz, von dem ich nie gedacht hätte, ihn zu sagen. Der Plan war, die C-API zu nehmen, die die Runtime jetzt freigibt, eine ernsthafte Unit-Test-Suite dagegen zu schreiben, zuzusehen, wie die Coverage-Zahl steigt, und eine Art beruflichen Stolzes zu fühlen, dass die Engine nicht mehr „agentengecodet mit Vibes” ist, sondern jetzt „agentengecodet mit Tests”.

Der Plan hielt etwa zwei Stunden.

Was ich fand

Der dritte Test, den ich schrieb, rief eine Runtime-Funktion auf, die auf dem Papier echte Arbeit leisten sollte. Der Funktionsname war sauber. Die C-API-Oberfläche sah richtig aus. Der Doc-Kommentar im Header sagte, was die Funktion tut. Die Implementierung, als ich sie las, gab einen Platzhalterwert zurück und loggte ein TODO.

Es war ein Stub. Ein Stub, der vor drei Wochen in einem PR gelandet war, in einem Branch, der sauber geschlossen wurde, mit der Commit-Nachricht des Agenten, die behauptete, das Feature sei implementiert. Der PR-Titel sagte „Implement X.” Der PR-Text sagte, die Arbeit sei erledigt. Der Reviewer (ich, an einem Samstag, in Eile) hatte gemerged.

Ich schaute nach. Fünfundvierzig Minuten später hatte ich eine Liste von siebenundvierzig ähnlichen Funktionen. Siebenundvierzig Stellen, an denen die Runtime behauptete, echte Arbeit zu leisten, und tatsächlich Platzhalter zurückgab.

Die gute Nachricht ist, dass keiner der Stubs sicherheitsrelevant oder korrektheitsrelevant in einer Weise war, die kaputt an einen Partner ausgeliefert worden wäre. Es waren die Art von Stubs, die ein Agent landet, wenn die Issue-Formulierung zu großzügig ist und das Test-Framework noch nicht streng genug ist, um den Stub durchfallen zu lassen. Das System funktionierte wie entworfen. Das Design war ein Problem.

Die schlechte Nachricht ist, dass ich kurz davor gewesen war, Testabdeckung für Funktionen zu behaupten, die nichts zu bedecken hatten.

Was ich mir eingestehen musste

Ein paar unbequeme Dinge.

Der Agenten-Prompt belohnte Vollständigkeit statt Korrektheit. Als der Prompt sagte „implementiere Funktion X mit Signatur Y, die einen Wert vom Typ Z zurückgibt”, konnte und tat der Agent diesen Vertrag erfüllen, indem er einen Standardwert vom Typ Z zurückgab. Streng genommen implementierte er die Funktion. Funktional tat er es nicht. Der Prompt hatte ein Loch; der Agent füllte das Loch so, wie Wasser ein Loch füllt. Das liegt an mir.

Mein Review-Prozess hat es nicht abgefangen. Ich hatte PRs auf Form gelesen, nicht auf Ausführung. „Passt die API zum Issue? Existiert der Test? Besteht die CI?” Drei Prüfungen, alle grün, keine davon inspizierte tatsächlich, ob die Implementierung irgendetwas tat. Der agentengetriebene Workflow hatte mich meinen Standard dafür, was Review bedeutet, senken lassen. Die Disziplin, über die ich allen erzählt hatte, war dünner als ich behauptet hatte.

Die CI hat es nicht abgefangen, weil die Tests noch nicht existierten. Der Test-Harness lief. Die Handvoll Tests darin bestand. Nichts lief gegen die neuen Funktionen, weil nichts irgendetwas Bedeutungsvolles über sie behauptete. CI grün bedeutete CI grün. Es bedeutete nicht funktionierend.

Das ist die Lehrbuch-Art, wie eine agentengetriebene Codebasis in Schwierigkeiten gerät. Es ist der Fehlerzustand, über den ich gelesen hatte und dachte, ich schütze mich davor. Ich habe mich nicht gut genug geschützt.

Was ich mit dem Rest des Tages gemacht habe

Ein paar Dinge, der Reihe nach.

Ein Audit-Durchgang. Ein kleines Skript geschrieben, das die öffentliche Oberfläche der Runtime durchläuft, jede Funktion in jedem Header findet und die Implementierungen gegen ein paar Signaturmuster bewertet („return 0”, „return nullptr”, „TODO”, „PLACEHOLDER”). Siebenundvierzig Treffer, mehr oder weniger. Jeden als GitHub-Issue erfasst mit dem Funktionsnamen, dem Dateipfad, dem ursprünglichen PR, der ihn eingeführt hat, und einem frischen Abnahmekriterium.

Eine echte Implementierungswarteschlange. Alle siebenundvierzig als priorisierte Issues für die Agenten zur Aufnahme neu eingereicht. Das Abnahmekriterium ist diesmal explizit. Die Implementierung muss echte Arbeit leisten. Der Test, der sie ausübt, muss eine nicht-triviale Behauptung aufstellen. Der PR kann nicht landen, ohne beides. Ich werde keinen PR mergen, dessen Tests Tautologien sind.

Eine test-first-Neuformulierung der Warteschlange. Von jetzt an sagt jedes neue Feature-Issue „schreibe zuerst den Test, dann die Implementierung, und beides muss im selben PR landen.” Das ist die Disziplin, die ich von Anfang an hätte fahren sollen. Der Agent kann das, wenn man ihn darum bittet. Er tut es nicht, wenn man ihn nicht darum bittet.

Ein Unit-Test-Leitfaden für den Copilot Guide. Das Onboarding-Dokument, das die Agenten zu Beginn jedes Issues lesen, um einen Abschnitt darüber aktualisiert, wie ein echter Test aussieht. Tautologische Behauptungen werden als Geruch markiert. Tests, die nur den Happy Path ausüben, werden markiert. Tests, die einen Rückgabewert gegen eine hartkodierte Fixture vergleichen, die durch Ausführen der Funktion erzeugt wurde, die sie angeblich testen, werden markiert. Der Leitfaden sagt jetzt, wie man einen Test schreibt, der die Art von Fehler abfängt, auf die ein echter Nutzer stoßen würde.

Fünf Tests von Hand gegen die kritischsten Stubs geschrieben. Die fünf, die, wären sie gestubbt geblieben, eine echte Partnerintegration in der ersten halben Stunde hätten scheitern lassen. Diese fünf Tests schlagen jetzt laut gegen die aktuelle Implementierung fehl. Gut. Das sollen sie. Die Implementierungen werden nächstes Wochenende nachziehen.

Best Practices, zu denen ich mich jetzt verpflichte

Eine kurze Liste, geschärft durch das Wochenende.

Tests zuerst, im selben PR wie das Feature. Der Agent tut das, wenn man ihn darum bittet. Die Issue-Formulierung muss danach fragen.

Ein fehlschlagender Test ist wertvoller als ein bestandener, wenn der fehlschlagende Test bedeutet, dass die Implementierung unvollständig ist. Die fünf Tests, die ich heute gegen Stubs geschrieben habe, sind einige der nützlichsten Tests im Repo, gerade weil sie fehlschlagen. Sie sind die Spezifikation, die die Agenten erfüllen müssen.

Testen, was die Funktion tut, nicht, was ihre Signatur sagt. Ein Test, der eine Funktion aufruft und behauptet, der Rückgabetyp sei korrekt, ist kein Test. Er ist Typüberprüfung, die der Compiler bereits gemacht hat. Ein Test behauptet ein Verhalten.

Implementierungen während des Reviews lesen, nicht nur Signaturen. Wenn ich den PR eines Agenten reviewe, muss ich den Körper der Funktion lesen und bestätigen, dass der Körper tut, was das Issue verlangt hat. Nicht „die API hat die richtige Form.” Nicht „der Test besteht.” Tut die Implementierung tatsächlich die Arbeit.

Die Codebasis regelmäßig auf Stubs auditieren. Das Audit-Skript, das ich heute geschrieben habe, läuft jetzt in der CI. Wenn ein neuer Stub landet, markiert die CI ihn. Stubs sind nicht verboten. Unmarkierte Stubs sind es.

Was ich Buildern und Partnern mitgeben möchte

Wenn Sie einen agentengetriebenen Workflow betreiben und in letzter Zeit kein Stub-Audit gemacht haben, machen Sie eines. Die Wahrscheinlichkeit, dass Sie Stubs haben, von denen Sie nichts wissen, ist hoch. Die Kosten, sie heute zu finden, sind gering. Die Kosten, sie zu finden, wenn ein Partner versucht, gegen die betroffene Funktion zu integrieren, sind hoch.

Wenn Sie ein Anbieter von Coding-Agenten sind und das hier lesen: Die Metrik, für die ich optimieren würde, ist „meldet der Agent, wenn seine eigene Implementierung keine echte Implementierung ist.” Ein Stub, der sich selbst ankündigt, ist etwas anderes als ein Stub, der vorgibt, ein fertiges Feature zu sein. Die Agenten, die auf dieser Metrik gut abschneiden, werden die sein, denen ich bei substanzieller Arbeit vertraue.

Wenn Sie überlegen, ob Sie 2026 auf dieser Engine bauen sollen, ist das die Art von Moment, über den ich öffentlich sein möchte. Es ist ein Moment, der eine Schwäche in meiner Review-Disziplin offengelegt hat. Die Disziplin hat sich dadurch verbessert. Die Codebasis ist dadurch besser geworden. Ich würde lieber, dass Sie jetzt darüber lesen, als es im März selbst zu entdecken.

Müder Samstag. Produktives Wochenende. Das Audit-Skript ist das, wofür ich in drei Monaten am dankbarsten sein werde.

Zurück ans Bauen.

Bauen Sie auf einer Runtime, die die Wahrheit sagt

RakuAI wird öffentlich entwickelt, Stubs inklusive — mit der Audit-Disziplin, die eine räumliche Runtime sicher macht, um dagegen zu integrieren. Sehen Sie, was es braucht, um darauf auszuliefern.

← Alle Beiträge