„Ist unser Code zu alt für Coding-Agenten?“
Die Frage höre ich seit dem Frühjahr in fast jedem Erstgespräch, und sie kommt immer mit demselben Unterton: als wäre die Antwort bekannt und nur noch das Ausmaß offen.
Sie ist falsch gestellt.
Ich arbeite gerade in beiden Welten. In einem gewachsenen System aus PHP und Go, das älter ist als die Berufserfahrung mancher seiner Entwickler, und in Repositories, die es seit wenigen Wochen gibt und in denen Agenten von der ersten Zeile an schreiben. Wenn ich zähle, wo ein Agent in diesem Jahr etwas Falsches durchgebracht hat, verliert nicht der alte Code. Es verliert der Code, bei dem kein Befehl Nein sagen konnte.
Das ist die Größe, um die es geht. Nicht das Alter, nicht die Sprache, nicht der Stil. Dieser Artikel ersetzt die eine Frage durch vier, die sich in einer Woche beantworten lassen, und sagt, in welcher Reihenfolge ich sie in einem alten System angehe.
Was ein Agent im Bestand kann und woran er scheitert, steht in Coding-Agenten im Altsystem. Hier geht es um das, was um ihn herum stehen muss.
Das grüne Häkchen ist die Summe der gestellten Fragen
Ein Agent lieferte mir vor einiger Zeit Go-Code. Kompiliert, Review unauffällig, Tests grün. In einer Schleife öffnete er in jedem Durchlauf eine Datei und schloss sie erst am Ende der Funktion statt direkt danach. Im Test mit zehn Dateien fällt das nicht auf. In Produktion stapeln sich die offenen Handles, bis das Betriebssystem keine mehr hergibt und der Dienst steht.
Der Code war nicht falsch geschrieben. Er war falsch gedacht, und keine der Prüfungen hatte danach gefragt.
Seitdem lese ich ein grünes Häkchen anders. Es ist keine Aussage über den Code. Es ist die Summe der Fragen, die vorher jemand in Tests, Linter und Typprüfung gegossen hat. Wer nie nach offenen Handles gefragt hat, bekommt darauf keine Antwort. Der passende Linter fängt diesen Fall übrigens, sobald er aktiv ist. Nur wusste ich erst nach dem Ausfall, dass er es ist, den ich brauche.
Das ist der Kern des ganzen Themas. Ein Agent arbeitet so sicher, wie die Prüfungen fragen, die um ihn herum laufen. Er selbst stellt keine Fragen, die ihm niemand gestellt hat. Prüfbarkeit ist die Größe, nicht das Alter.
Warum alter Code dabei oft im Vorteil ist
Und hier wird die Ausgangsfrage interessant, denn zwölf Jahre Betrieb sind zwölf Jahre gestellte Fragen.
Jeder Vorfall, der damals eine Nacht gekostet hat, hat mit etwas Glück einen Test hinterlassen, einen Monitor, einen Kommentar mit einer Vorgangsnummer. Die Sonderbehandlung für den einen Großkunden steht mit Datum im Code. Die Datenbank hat Einschränkungen, die aus echten Datenfehlern entstanden sind. Ein System, das lange läuft, hat seine Grenzfälle gesehen. Ein Repository, das seit drei Monaten existiert, hat noch keinen gesehen.
Dazu kommt, was alter Code nicht hat: Er wächst nicht mehr um ein Drittel pro Woche. Bei Agenten ist das kein Nachteil. Eine Codebasis, die sich langsam ändert, lässt sich beschreiben, und die Beschreibung stimmt am nächsten Tag noch.
Was dem alten Code fehlt, ist meist der Befehl, der all das zusammen ausführt. Die Tests gibt es, aber drei Verzeichnisse davon laufen seit 2019 nicht mehr. Die statische Analyse gibt es, aber nur auf dem Rechner des Kollegen, der sie eingerichtet hat. Genau da beginnt die Arbeit, und sie ist kleiner als eine Modernisierung.
Wie ein Sicherheitsnetz in Code kommt, der nie Tests hatte, steht in Legacy-Code testen, wenn es keine Tests gibt.
Frage 1: Gibt es einen Befehl, der Nein sagt?
Nicht „gibt es Tests“. Gibt es einen einzigen Befehl, der Tests, statische Analyse und Linter ausführt und mit einem Exit-Code ungleich null endet, wenn etwas nicht stimmt?
Dieser Befehl ist die Schnittstelle zwischen Agent und Wahrheit. Ein Agent hält sich für fertig, wenn er fertig ist. Der Befehl hält ihn für fertig, wenn er schweigt. Der zweite Maßstab ist der, der zählt, und in meinem Aufbau ist er der einzige: Abgenommen wird über Exit-Codes, nicht darüber, ob ein Agent sich selbst für fertig hält.
check:
vendor/bin/phpstan analyse --no-progress
vendor/bin/php-cs-fixer check
vendor/bin/phpunit --testsuite=unit
go vet ./... && staticcheck ./...Der Befehl läuft lokal und in der CI, und zwar derselbe. Ich habe Projekte gesehen, in denen vendor/bin/phpunit direkt aufgerufen grün war und make test rot, weil nur das Makefile die richtige Konfiguration kannte. Ein Agent nimmt den Weg, der grün ist.
Was der Befehl prüft, wächst aus Vorfällen. Auf meiner eigenen Website haben Agenten im September fünfzig Artikel geschrieben. Zwei davon gingen mit einer Meta-Description von 21 und von 5 Zeichen live: Ein gerades Anführungszeichen im Text hatte das Attribut vorzeitig beendet. Geprüft wurde bis dahin nur, dass eine Description da ist. Seitdem bricht der Build unter fünfzig Zeichen ab, und der Fall ist nie wieder aufgetreten. Nicht, weil die Agenten besser wurden. Weil die Frage jetzt gestellt wird.
Wie die statische Analyse stufenweise in Code kommt, der sie nie kannte, steht in PHPStan Stufe für Stufe in Altcode einführen.
Frage 2: Fällt die Prüfung laut aus?
Der gefährlichste Zustand ist nicht die fehlende Prüfung. Es ist die Prüfung, die aufgehört hat zu prüfen und weiter Grün meldet.
Drei Fälle aus einem einzigen Monat. In meinem Wissensrepository verlangte eine Regel, vor dem Commit zu belegen, dass ein rein ergänzender Beitrag nichts löscht, per Grep auf Zeilen, die mit einem Minus beginnen und nicht mit zwei. Eine gelöschte Aufzählungszeile steht im Diff aber genau so: ein Minus vom Diff, eines von der Liste. Das Muster schloss genau die aus. In einem Repository, das aus Aufzählungen besteht, war die Prüfung bei den wahrscheinlichsten Löschungen blind, und zwar seit dem Tag, an dem sie aufgeschrieben wurde. Gefunden wurden vier verschluckte Zeilen in einem Commit, der sieben angezeigt bekam.
Auf einer meiner Websites standen Speculation Rules als Inline-Skript im Head, und die Content Security Policy erlaubte kein Inline-Skript. Der Browser verwirft so einen Block ohne Meldung: Seite normal, Code im Quelltext, Wirkung null. Build grün, Tests grün, und meine eigene Prüfung über alle 32 Seiten auch, weil sie das Vorhandensein im HTML gemessen hatte und nicht die Wirkung.
Und ein Skript, das vor jedem Commit Links und Metadaten prüft, hatte vier Tage lang die halbe Datei übersprungen und in dieser Zeit zuverlässig „0 Befunde“ gemeldet.
Diesen dritten Fall und den Kanarienvogel, der seitdem dagegen steht, habe ich in Ein Agent ist so gut wie sein Kontext beschrieben.
Drei Ausfallarten, ein Muster. Keine der Prüfungen war kaputt im Sinne eines Fehlers. Die eine hatte ein zu enges Suchmuster, die zweite maß die falsche Ebene, die dritte hatte still aufgehört. Alle drei sahen dabei aus wie Erfolg.
Daraus zwei Gewohnheiten. Jede automatische Prüfung bekommt einen Kanarienvogel: einen absichtlich eingebauten Fehler, den jeder Lauf finden muss, sonst bricht er ab, statt grün zu melden. Und jedes Suchmuster wird an einem echten Treffer erprobt, bevor es in eine Regel kommt. Ungetestet ist ein Suchmuster eine Behauptung, keine Prüfung.
Die Frage, die ich beim Bau jeder Prüfung stelle: Wie sieht ihr Ausfall aus? Lautet die Antwort „wie Erfolg“, ist sie nicht fertig.
Frage 3: Stimmt, was der Agent liest?
Jedes Werkzeug liest beim Start eine Datei aus dem Repository, je nach Hersteller AGENTS.md oder CLAUDE.md. Was dort steht, glaubt der Agent ohne Rückfrage. Das ist der Zweck der Datei, und es ist ihre Gefahr.
Im September habe ich in zwei gewachsenen Repositories diese Dateien geprüft. Beide enthielten tragende Falschangaben: die falsche PHP-Version, die falsche Go-Version, eine Autoloader-Konvention, die so nie galt, Verweise auf ein GitLab, das längst abgeschaltet war, und Befehle, die auf keinem Rechner installiert waren. Niemand hatte gelogen. Die Dateien waren einmal aus dem Wissen eines Kollegen entstanden und danach nie wieder gegen die Wirklichkeit gehalten worden. Ein Mensch hätte beim dritten falschen Befehl aufgehört, ihnen zu trauen. Ein Agent hat es nicht.
Daraus drei Regeln, die ich seitdem anwende:
- Die erste Fassung schreibt ein Mensch, und zwar aus den letzten drei Überraschungen, nicht aus der Architektur. Ein Agent kann den Code beschreiben. Was nicht im Code steht, kann er nicht wissen, und genau das gehört in die Datei.
- Jeder Satz muss ausführbar sein. „Tests laufen mit
make test“ lässt sich prüfen. „Wir achten auf eine saubere Trennung der Schichten“ nicht. Der zweite Satz steht in fast jeder dieser Dateien und hat noch nie eine Entscheidung geändert. - Kurz halten, den Rest verlinken. Die Datei behält, was für jede Änderung gilt. Das Fachwissen wandert in ein eigenes Verzeichnis, das der Agent bei Bedarf liest. Eine Datei mit vierhundert Zeilen wird nicht gelesen, sie wird überflogen, von Menschen wie von Modellen.
Und die Datei wächst aus demselben Anlass wie der Prüfbefehl: Immer wenn ein Agent etwas falsch verstanden hat, das ein Mensch gewusst hätte, kommt der fehlende Satz dazu. Nach ein paar Monaten steht dort, was vorher in drei Köpfen lag.
Was in die Wissensschicht darunter gehört und wie sie sich von Regeln unterscheidet, steht in Second Brain für KI-Agenten.
Frage 4: Ist die Regel ein Satz oder ein Mechanismus?
Die unangenehmste Erkenntnis dieses Jahres: Regeln werden gebrochen. Von Sessions, die sie kennen.
In meinem Wissensrepository gibt es eine feste Liste erlaubter Metadatenwerte. Als vier erfundene darin auftauchten, habe ich sie zurückgebaut und die Regel in jede Anweisungsdatei geschrieben. In den zwei Tagen danach entstanden drei neue. Dann kam die Prüfung dazu, und der vierte wurde sofort gemeldet. Mehr Nachdruck in der Formulierung wäre der falsche Hebel gewesen. Wer eine Konvention nicht anwendet, wendet auch eine schärfer formulierte nicht an. Wo sich ein Satz als Prüfung formulieren lässt, ist die Prüfung die Regel und der Satz nur die Begründung dazu.
Das gilt bis in die Werkzeugebene. In einem der beiden Repositories von oben war das Verzeichnis mit den Agentenregeln vollständig von der Versionskontrolle ausgeschlossen. Die Regeln konnten also nur persönlich sein; jeder Entwickler hatte seine eigenen, oder keine. Jetzt liegt es im Repository, und statt des Satzes „PHP läuft nur im Container“ gibt es einen Hook, der jeden Aufruf von php und composer auf dem Host abweist, bevor er ausgeführt wird. Der Satz wurde ignoriert. Der Hook kann es nicht.
Und es gilt für die Isolation. Anfang September liefen zwei meiner Sessions im selben Verzeichnis. Zweimal am selben Tag hat ein git add -A die halbfertige Arbeit der jeweils anderen mitgenommen und unter fremder Commit-Message abgelegt. Ein ganzer Abschnitt mit ausgewerteten Dokumenten wäre verloren gegangen; der Diff zeigte 57 Löschungen, wo der Edit rein ergänzend war, und nur deshalb fiel es auf. Nach dem ersten Vorfall habe ich die Regel ergänzt. Die zweite Session hat sie trotzdem gebrochen, weil sie vor der Ergänzung gestartet war und ihre Anweisungen aus der Zeit davor stammten. Seitdem arbeitet jede Session in einem eigenen Git-Worktree. Das ist keine Regel. Das ist ein Dateisystem.
Wer baut, nimmt nicht ab
Die vier Fragen beschreiben, was um den Agenten herum steht. Bleibt die Frage, wer am Ende sagt, dass die Änderung stimmt.
Nicht der Agent, der sie geschrieben hat. Wer Code schreibt, prüft ihn mit denselben Annahmen, mit denen er den Fehler eingebaut hat. Das gilt für Menschen, und es gilt für Modelle, sogar innerhalb einer Modellfamilie: ähnliches Training, ähnliche blinde Flecken.
Bei mir ist das deshalb aufgeteilt wie auf einer Baustelle. Ein starkes Modell plant und zerlegt die Aufgabe. Mehrere günstige Instanzen setzen um, jede in einem eigenen Worktree, und keine darf committen. Zusammengeführt wird zentral, Aufgabe für Aufgabe, und abgenommen wird über den Befehl aus Frage 1. Das Review macht danach ein Modell aus einer anderen Familie, und für alles, wofür es keine Regel gibt, zum Schluss ein Mensch.
Zwei Dinge halten das wirtschaftlich. Die deterministischen Prüfungen bleiben Pflicht und laufen um jede Aufgabe; das KI-Review ist die Ergänzung, nicht der Ersatz. Und nicht jede Änderung braucht das große Review. Ich prüfe an Meilensteinen, mit dem kleinsten Modell, das die relevanten Fehlerklassen noch zuverlässig findet. Sonst kostet die Kontrolle mehr als die Arbeit.
Wie aus der Aufgabe davor eine Spec wird, an der sich Umsetzung und Abnahme messen lassen, steht in Agentic Coding mit Spec-Driven Development.
Wo ich in einem alten System anfange
Nichts davon setzt eine Modernisierung voraus. Die Reihenfolge, die sich bewährt hat, passt in eine Woche, und der Agent selbst ist dabei schon nützlich.
- Tag 1: der Befehl. Alles, was es an Tests, Analyse und Lintern schon gibt, hinter einem Ziel. Was rot ist, wird nicht repariert, sondern ausgeschlossen und aufgeschrieben. Am Abend gibt es einen Befehl, der Nein sagen kann.
- Tag 2: derselbe Befehl in der CI, auf jedem Pull Request, ohne Ausnahme. Erst jetzt gilt er.
- Tag 3: der Kanarienvogel. Ein absichtlich kaputter Testfall, ein absichtlich falscher Typ. Meldet die CI ihn nicht, prüft sie nicht, was Sie glauben.
- Tag 4: die Anleitung, von Hand, aus den letzten drei Überraschungen. Jeder Satz wird einmal ausgeführt, bevor er stehen bleibt.
- Tag 5: die erste Aufgabe für den Agenten, und zwar eine, deren Ergebnis der Befehl beurteilen kann: Charakterisierungstests um die Stelle, die als Nächstes angefasst wird. Ein Test, der gegen den unveränderten Code nicht läuft, ist falsch. Das ist ein Schiedsrichter, den man nicht überreden kann.
Ab dann wächst das Netz aus Vorfällen. Jedes Mal, wenn ein Mensch etwas findet, das kein Befehl gefunden hat, wird daraus eine Prüfung. Das ist auch die Zahl, an der ich den Fortschritt messe: wie oft im Monat ein Mensch etwas gefunden hat, das die Prüfung nicht sah. Sie soll fallen. Sie wird nie null.
Häufige Fragen
Müssen wir erst eine Testabdeckung nachholen? Nein. Eine Abdeckung von 80 Prozent sagt nichts darüber, ob die Tests Nein sagen können. Ein Netz um die Stelle, die angefasst wird, reicht, und es ist die erste Aufgabe für den Agenten.
Reicht ein Linter? Für den Anfang ja, wenn er in der CI läuft und rot wird. Er fängt weniger, als er verspricht, und mehr als nichts. Welche Prüfungen fehlen, zeigt der nächste Vorfall.
Unser System ist zu groß für das Kontextfenster. Der Agent braucht nicht das System. Er braucht die Stelle, die er ändert, und einen Befehl, der über das ganze System sagt, ob die Änderung etwas kaputt gemacht hat. Die Größe ist ein Problem des Zuschnitts, nicht des Alters; warum ein größeres Kontextfenster das nicht löst, steht im Glossar.
Und wenn der Befehl Nein sagt, obwohl die Änderung richtig ist? Dann ist die Prüfung falsch, und das ist ein Befund, der vor dem Commit auffällt statt in Produktion. Der Agent darf sie nicht anpassen. Er meldet es.
Was ich diese Woche tun würde
Öffnen Sie das älteste Repository, das bei Ihnen noch Geld verdient, und führen Sie den einen Befehl aus, der alles prüft. Gibt es ihn nicht, ist das Ihre Woche. Gibt es ihn und er ist grün: Bauen Sie absichtlich einen Fehler ein und sehen Sie nach, ob er rot wird.
Erst danach lohnt sich die Frage, welchen Agenten Sie einsetzen.
Alter Code ist für einen Agenten kein Hindernis. Ungeprüfter Code ist eines.
Wie so ein Aufbau in einem Team entsteht, steht unter Claude Code einführen.
Dieser Artikel gehört zu einer Reihe über Systeme, die es schon gibt. Der Rückblick ordnet alle Artikel der Reihe nach Anlass.

