Kommentar von André Hoendgen, MoIn Group Wie ein Coding Agent eine komplexe Codebase dauerhaft versteht

Von André Hoendgen 6 min Lesedauer

Ein erfahrener Entwickler kennt seine Codebase nicht auswendig. Er weiß nicht, was in jeder Funktion steht. Aber wenn etwas kaputt geht, weiß er innerhalb von Sekunden, wo er nachschauen muss. Damit ein Entwickler so gut durch seinen eigenen Code navigieren kann, benötigt er einen Überblick über das ganze System: Frontend, Backend, Datenbank und Infrastruktur, zumindest in ihren Umrissen, dazu das Wissen darüber, an welchen Stellen diese Teile ineinander greifen.

Der Autor: André Hoendgen ist CTO der MoIn Group(Bild:  MoIn Group)
Der Autor: André Hoendgen ist CTO der MoIn Group
(Bild: MoIn Group)

Dafür ist einerseits eine saubere Architektur nötig, aus der sich ableiten lässt, wo ein Verhalten fachlich hingehört, und andererseits Tooling. Beim Menschen ergänzen sich beide. Beim KI-Agenten trägt das Tooling die Hauptlast, weil ihm fehlt, was ein Mensch über Jahre aufbaut. Er hat über Sitzungen hinweg kein Gedächtnis und sein Kontextfenster ist zu klein, um sich das Bild jedes Mal neu zu erlesen. Das stellt ein Problem dar, wenn Entwickler mit KI bei gleichbleibender Qualität entwickeln wollen. Gelöst wird das durch eine generierte Codemap, durch Annotationen, die den Zweck von Code festhalten, und durch eine klare Grenze zwischen dem, was sich automatisch ableiten lässt, und dem, was weiterhin Menschen entscheiden müssen.

Ein KI-Agent braucht eine Codemap

Ein Agent muss wissen, was existiert, wo es liegt und wie es zusammenhängt. So eine Karte wird bei jedem Build neu, aus dem Code selbst, generiert.

Die erste Stufe beim Aufbau dieser Karte ist die statische Analyse: Ein Parser wie Tree-sitter zerlegt den Quelltext in seinen Syntaxbaum und liefert Dateien, Symbole, Signaturen, Importe und Aufrufe – schnell, inkrementell und über viele Sprachen hinweg, auch ohne installierte Projekt-Toolchain.

Aus Namen werden im zweiten Schritt Identitäten. Ein Alias wie @/services/user wird zu einem echten Pfad, aus einem Namen wird eine Kante zwischen zwei konkreten Stellen. Was offenbleibt – etwa welche Implementierung hinter einem Aufruf über ein Interface steckt – bekommt eine Konfidenz und wird als unsicher geführt. Ein Language Server, ein Werkzeug, das Typen und Modulauflösung kennt, hebt genau diese Kanten von „vermutet“ auf „bestätigt“.

Die dritte Stufe wird meist übersehen: die Grenzen zwischen den Teilsystemen. Jeder Parser endet an der Prozessgrenze, ein Aufruf verschwindet im Netzwerk und taucht auf der anderen Seite als Handler wieder auf. Genau dort verlaufen aber die fachlichen Wege. Ein umbenanntes Feld im Frontend trifft einen Endpunkt und am Ende eine Spalte in der Datenbank. Typisierte Aufrufe zwischen Frontend und Backend gehören deshalb ebenso in die Karte wie das Datenbankschema.

Der Code zeigt das Verhalten, nicht den Zweck

Damit steht, was existiert und wie es zusammenhängt. Was fehlt, ist der Zweck, also warum ein Codestück in dieser Form existiert. Übersetzt in Code sind das zwei Tags im Docblock, dem Kommentarblock über der Funktion. @summary beschreibt, was eine Funktion tut. Das lässt sich aus dem Code selbst ablesen. @intent beschreibt, warum sie das tut – und genau das steht nirgendwo im Code.

Ein Beispiel: Ein Prüfskript baut die Anwendung in ein Wegwerf-Verzeichnis, statt das reguläre zu nutzen. Ohne Begründung sieht das nach unnötigem Umweg aus. Der Intent liefert den Grund: Der Guard darf den bestehenden Build nicht überschreiben, sonst zerstört eine grün meldende Prüfung die Grundlage der nächsten. Wer den Umweg entfernt, ohne den Grund zu kennen, bricht nichts sofort und der Schaden zeigt sich erst später.

Ohne diese Annotation bleibt im Code oft nur ein Warnschild wie „nicht anfassen“. Solche Kommentare altern schlecht, weil den Anlass irgendwann niemand mehr kennt. Ein Intent lässt sich dagegen prüfen: Gilt der Grund noch? Ein Agent kann eine Begründung abwägen, ein Verbot kann er nur befolgen oder überlesen.

Entscheidend ist der Zeitpunkt: Der Intent muss entstehen, während der Code geschrieben wird, denn danach ist er nur noch Rekonstruktion. Eine Konvention hält das nicht durch, ein Linter im Pre-Commit-Hook schon. Für Legacy-Code gilt deshalb die Pfadfinderregel: Jede neue Funktion bekommt ihren Intent beim Schreiben, jede angefasste wird nachgerüstet. Nach wenigen Wochen ist der aktive Teil der Codebase abgedeckt.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Big Data, Analytics & AI

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung. Die Einwilligungserklärung bezieht sich u. a. auf die Zusendung von redaktionellen Newslettern per E-Mail und auf den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern (z. B. LinkedIn, Google, Meta).

Aufklappen für Details zu Ihrer Einwilligung

DRY meint Wissen, nicht identischen Code

Der Zweck verändert auch ein bekanntes Prinzip. DRY wird meist so gelesen: derselbe Code darf nicht zweimal existieren. Die ursprüngliche Formulierung spricht aber von Wissen und verlangt für jedes Wissenselement eine eindeutige, autoritative Repräsentation.

Daraus folgt beides: Zwei gleich aussehende Blöcke mit unterschiedlichem Zweck bleiben getrennt. Eine fachliche Regel, die an drei Stellen umgesetzt ist, bildet die echte Dopplung, auch wenn der Code dort verschieden aussieht.

Das Problem ist älter als die Agenten. Gängige Analysewerkzeuge melden einen duplizierten Block, sobald sich rund hundert aufeinanderfolgende Token in ihrer Form hinreichend ähneln. Der Zweck der Fundstellen spielt dabei keine Rolle. Wer dem Befund folgt, konsolidiert nach Form. Ein Agent macht dasselbe, nur schneller und über mehr Dateien hinweg.

Erst der Graph macht das Erfahrungswissen nutzbar

Struktur, Identitäten, Systemgrenzen und Zweck ergeben zusammen einen Graphen der Codebase. Damit lässt sich abbilden, was erfahrene Entwickler im Kopf tun: Der Blast Radius einer Änderung – wer alles von einer Funktion, einer Tabelle, einem Endpunkt abhängt – ergibt sich aus den Konsumenten eines Symbols, statt geschätzt zu werden. Hotspots zeigen sich dort, wo hohe Verflechtung auf hohe Komplexität trifft. Abhängigkeitspfade lassen sich über Prozessgrenzen hinweg verfolgen, Verstöße gegen die Architekturschichten werden prüfbar. Ein erfahrener Entwickler beantwortet diese Fragen ohnehin, allerdings nur für den Teil des Systems, den er kennt.

Der Unterschied im Verhalten ist deutlich messbar: Mit Karte liest ein Modell erheblich weniger Dateien auf Verdacht und steuert die zuständigen Stellen gezielt an. Ohne Karte sucht dasselbe Modell und baut sich sein Bild bei jeder Aufgabe neu.

Die Karte ersetzt keine deterministische Prüfung

Wichtig ist: Die Karte macht das System zwar sichtbar, verhindert aber dadurch nicht automatisch Fehler. Dafür braucht es eine zweite Ebene: Prüfungen, die nach jeder Dateiänderung sofort laufen und vor jedem Commit vollständig. Diese Kontrolle läuft nicht über ein weiteres Modell, sondern über klassischen, deterministischen Code – denselben Linter, denselben Compiler, dieselben Tests, die auch bei menschlich geschriebenem Code greifen.

Der Unterschied liegt in der Durchsetzung. Eine Regel, die nur in einer Anweisungsdatei steht, ist ein Vorschlag, den ein Agent befolgen oder überlesen kann. Eine Regel im Linter ist eine Bedingung, an der kein Weg vorbeiführt. Genau hier verläuft die Grenze, die für den Produktionsbetrieb zählt: Wo ein System zuverlässig laufen muss, entscheidet nicht die Inferenz des Modells, sondern die deterministische Prüfung dahinter.

Der Graph erklärt nicht, wofür das System existiert

Eine Ebene bleibt offen. Der Graph zeigt, wie das System gebaut ist. Wofür es existiert, steht dort nicht.

Diese Ebene liefert eine Architekturdokumentation, etwa nach arc42 – vor allem die ersten Kapitel: Einführung und Ziele, Randbedingungen, Kontextabgrenzung und Lösungsstrategie. Sie beantworten, welches Problem das System löst, welche Zwänge gesetzt sind und welche grundsätzlichen Entscheidungen getroffen wurden. Diese Kapitel ändern sich selten und lassen sich aus keinem Quelltext ableiten. [1.1]Diese Dokumentation sollte im selben Repository liegen wie der Code, damit sie nicht neben dem System altert, sondern sich mit ihm weiterentwickelt – idealerweise im selben Review- und Commit-Prozess.

Bei den Kapiteln darunter ergänzt der Graph: Die Bausteinsicht und ihre Diagramme beschreiben das, was er ohnehin aus dem Code ableitet, und sie ändern sich mit jedem Refactoring. Solche Darstellungen gehören deshalb in die Generierung, die Handarbeit bleibt bei den Kapiteln darüber.

Verständnis hält, weil es jederzeit neu ableitbar ist

Ein Agent hat über Sitzungen hinweg kein Gedächtnis, und trainiertes Wissen über ein Projekt veraltet mit dem nächsten Commit. Jeder Versuch, das Verständnis im Modell zu halten, scheitert daran.

Die Antwort liegt in der Quelle. Struktur und Zweck liegen beide im Code, also ist das Bild des Systems generierbar und deterministisch – gleicher Stand, gleiche Karte, unabhängig von Modellversion und Tagesform. Sie altert mit dem Code, weil ein Refactoring beides im selben Commit verändert. Das Verständnis liegt damit im Repository und überdauert das Sitzungsende, den Modellwechsel und den Weggang eines Kollegen.

Ein Allheilmittel ist das nicht. Ein schlecht geschnittenes System bleibt schlecht geschnitten, die Karte macht den Schnitt nur sichtbar. Eine falsche Anforderung wird sauber umgesetzt und bleibt falsch. Architektur, Reviews und Tests verlieren dadurch nicht an Bedeutung, sie verschieben sich nur: von der Kontrolle über jede einzelne Zeile hin zur Kontrolle über Regeln, Schnitt und Schichtung, an die sich jede Zeile halten muss. Der Gewinn liegt woanders: Der Agent rät nicht, sondern sucht sich die zuständige Stelle – genau die Fähigkeit, die eingangs den erfahrenen Entwickler ausmacht, der nicht jede Zeile kennt, aber immer weiß, wo er nachschauen muss.

Artikelfiles und Artikellinks

(ID:50952854)