Blog | Employees, technology, business, news, events | Mimacom

KI-Coding-Agenten: Muss man Code noch lesen?

Geschrieben von Daniel Willig | 20.10.2026, 02:47:40

KI-Agenten haben für ein Kundenprojekt eine Admin-Oberfläche gebaut, und Review-Agenten haben dabei Fehler gefunden, die Senior Software Engineer Daniel Willig übersehen hatte. Müssen Entwicklerinnen und Entwickler also überhaupt noch Code lesen? Daniel zeigt, welche Prüfungen man getrost Agenten und automatischen Regeln überlassen kann und welchen Code weiterhin ein verantwortlicher Mensch lesen sollte.

Das Wichtigste in Kürze

  • KI-Agenten können heute den Grossteil des Codes in einem Projekt schreiben. Spannend ist vor allem die Frage, wer diesen Code prüft und wie.
  • In einem aktuellen Kundenprojekt hat Daniel die Prüfung dreigeteilt: Automatische Regeln prüften die Struktur, Review-Agenten die Details. Er selbst kümmerte sich darum, was der Kunde wirklich braucht und wer worauf zugreifen darf.
  • So entsteht Software im Tempo von Agenten. Die Teile, die Daten und Zugriffe schützen, prüft trotzdem ein Mensch, der dafür verantwortlich ist.

Vor Kurzem habe ich für ein Kundenprojekt eine Admin-Oberfläche gebaut, ohne selbst eine einzige Zeile Code zu schreiben. Eine Gruppe von KI-Agenten hat den Code geschrieben. Eine zweite Gruppe, die Review-Agenten, hat jede Änderung geprüft. Dazu haben ein statischer Code-Analyzer und ein Linter darauf geachtet, dass die Regeln eingehalten werden, die ich festgelegt hatte. Ich habe mir die Änderungen so gründlich angesehen, wie ich konnte. Trotzdem haben die Review-Agenten mehr gefunden als ich, und das in einem Bruchteil der Zeit.

Immer wieder haben sie mich auf Sonderfälle hingewiesen, an die ich gar nicht gedacht hatte. Einmal fanden sie sogar einen Fehler in meiner eigenen Anforderung. Ganz am Anfang hatte ich festgelegt, dass jeder fehlgeschlagene Login einen Audit-Eintrag erzeugen soll. Gross darüber nachgedacht hatte ich dabei nicht. Ein Audit-Eintrag hält im Protokoll fest, wer wann was gemacht hat. Bei einem fehlgeschlagenen Login ist aber niemand angemeldet. Die Einträge hatten also keinen Akteur und waren ziemlich nutzlos. Schlimmer noch: Jeder konnte die Audit-Datenbank volllaufen lassen, indem er einfach Login-Versuche mit falschem Passwort abschickte. Bemerkt hat das ein Review-Agent. Ich selbst hatte meine Anforderung nie infrage gestellt.

Solche Erlebnisse haben mich ins Grübeln gebracht. Gehört es noch zu meinem Job, Code zu lesen? Oder besteht er inzwischen eher darin zu prüfen, ob das Ergebnis meiner Spezifikation entspricht? Ich halte KI im Grunde für eine weitere Abstraktionsebene. Als Compiler zuverlässig genug waren, hörten Entwicklerinnen und Entwickler auf, Assembler zu lesen. Die Korrektheit war ihnen trotzdem wichtig. Sie prüften sie nur auf einer höheren Ebene, nämlich im Quellcode. Wenn Agenten zuverlässig funktionierenden Code aus unseren Absichten machen, könnte sich der Blick noch einmal nach oben verschieben. Wir würden dann vor allem auf die Spezifikation schauen und darauf, wie sich das System tatsächlich verhält.

Ich finde, für den Grossteil des Codes passt der Vergleich. An ein paar Stellen hinkt er allerdings. Gerade diese Stellen verraten einiges darüber, warum wir überhaupt abstrahieren. Das ist nicht nur für Entwicklungsteams interessant. Wenn Agenten den Code schreiben, wollen auch die Kunden wissen, was noch geprüft wird und von wem. Und sie wollen wissen, worauf sie sich verlassen können.

 

Wo der Vergleich passt und wo er hinkt

Einem Compiler kann man vertrauen, ohne sich seine Ausgabe anzusehen. Er übersetzt immer gleich, und der Quellcode legt das Verhalten vollständig fest. Wir rechnen dieses Vertrauen gern dem Werkzeug an. Dabei liegt es zu einem grossen Teil daran, wie genau die Eingabe ist.

Ein Ticket oder ein Prompt ist längst nicht so genau. Natürliche Sprache lässt Lücken. Ein Agent füllt sie mit Entscheidungen, die plausibel aussehen, und oft fallen sie mir erst im Code auf. Der Agent baut das, worum man ihn bittet, Lücken inklusive. Wie gut die Software wird, hängt deshalb heute stark davon ab, wie gut die Anforderungen sind. Ein Vergleich von Eingabe und Ausgabe ersetzt das Lesen von Code also nur, wenn die Eingabe präzise genug ist. In meinem Projekt sorgten detaillierte Akzeptanzkriterien in den Tickets für diese Präzision. Aus ihnen haben die Agenten auch die Tests abgeleitet. Die Kriterien übernehmen damit die Rolle, die früher der Quellcode hatte. Deshalb verdienen sie jetzt die Aufmerksamkeit, die früher der Implementierung galt. Auch die Tests sollte man sich noch einmal ansehen. Ein Test, den ein Agent aus einem Kriterium ableitet, prüft schliesslich nur, wie der Agent das Kriterium verstanden hat. Jede Lücke im Kriterium steckt also auch im Test.

Das Audit-Beispiel ist hier aufschlussreich. Der Fehler lag in meiner Anforderung, und der Implementierungs-Agent hat sie brav umgesetzt. Der Review-Agent ist darauf gestossen, weil er die Folgen durchgespielt hat: Wer kann einen fehlgeschlagenen Login auslösen, und was steht dann im Eintrag? Review-Agenten können also durchaus eine Absicht hinterfragen, solange sie irgendwo im Code Spuren hinterlässt. Was nie im Repository landet, sehen sie aber nicht. Das gilt etwa für Erwartungen des Kunden, die niemand aufgeschrieben hat. Und wenn die prüfenden und die schreibenden Agenten auf ähnlichen Modellen basieren, übersehen sie womöglich dieselben Dinge. Ihre Einigkeit ist dann weniger wert als die von zwei Menschen, die unabhängig voneinander zum selben Ergebnis kommen.

Die zweite Schwachstelle sind Berechtigungen. Das wiegt schwer, denn eine Admin-Oberfläche besteht zum grossen Teil daraus. Ein Test kann zeigen, dass ein Admin einen Datensatz bearbeiten darf. Dass kein Konto mit weniger Rechten dasselbe kann, lässt sich von aussen viel schwerer nachweisen. Dafür müsste man jeden möglichen Weg durch den Code abdecken, und das schafft keine Testsuite.

 

Warum wir abstrahieren: drei Gründe

Fred Brooks hat 1986 in seinem Essay „No Silver Bullet“ zwischen essenzieller und akzidenteller Komplexität unterschieden. Die eine steckt im Problem selbst, die andere kommt von unseren Werkzeugen und Methoden. Moseley und Marks haben 2006 in „Out of the Tar Pit“ daran angeknüpft. Mit dieser Unterscheidung kann man gut beurteilen, ob eine Abstraktion Komplexität wirklich beseitigt oder nur woandershin schiebt. Sie sagt aber nicht, für wen oder wofür eine Abstraktion da ist. Genau darum geht es jedoch, wenn Agenten den Code schreiben. Die bessere Frage ist hier, wovor eine Abstraktion uns schützt. Dabei komme ich auf drei verschiedene Gründe.

Der erste ist unser Arbeitsgedächtnis. Wir können nur eine Handvoll zusammenhängender Gedanken gleichzeitig im Kopf behalten. Also verstecken wir Details hinter Namen und bekannten Mustern und zerlegen Logik in kleine Funktionen. Warum das hilft, erklärt etwa John Swellers Cognitive Load Theory. Green und Petre haben mit ihrem Cognitive-Dimensions-Framework ein Raster entwickelt, mit dem man Notationen bewerten kann, zum Beispiel danach, wie leicht sie sich ändern lassen und wie viele Abhängigkeiten sie verstecken. Auch Felienne Hermans beschreibt diese Zusammenhänge in ihrem Buch „The Programmer’s Brain“. Der Preis dafür ist Indirektion: Wer wissen will, was wirklich passiert, muss sich durch Schnittstellen und Konfiguration hangeln.

Der zweite Grund ist der Kontext des Modells. Ein Modell überblickt viel mehr Code auf einmal als wir. Manche dieser Verdichtungen braucht es deshalb gar nicht. Trotzdem ist sein Kontext begrenzt, und jedes Token kostet Geld. Tokens sind die Einheiten, in denen Modelle Text lesen und nach denen die Nutzung abgerechnet wird. In einer grossen Codebasis muss ein Modell ausserdem erst einmal die richtigen Dateien finden. Dabei helfen klare Namen und gut geschnittene Module. Viele Abstraktionen, die eigentlich für Menschen gedacht sind, helfen deshalb auch Modellen. Bei Modellen werden sie nur erst bei grösseren Codemengen wichtig.

Der dritte Grund hat nichts damit zu tun, wer den Code liest. David Parnas hat 1972 vorgeschlagen, dass Module genau die Designentscheidungen verstecken sollten, die sich wahrscheinlich ändern. So bleibt jede Änderung auf ein Modul begrenzt. Melvin Conway hatte ein paar Jahre vorher beobachtet, dass Systeme die Kommunikationsstruktur ihrer Organisation widerspiegeln. Viele Grenzen in Software sollen deshalb dafür sorgen, dass verschiedene Teams arbeiten können, ohne sich ständig abzustimmen. Dazu kommen Sicherheitsgrenzen, die festlegen, worauf eine Komponente zugreifen darf. Auch die Stellen, an denen Tests echte Abhängigkeiten wie eine Datenbank ersetzen, gehören dazu. Diese Abstraktionen schützen das laufende System und die Art, wie es entwickelt wird. Ob ein Mensch oder ein Agent den Code schreibt, spielt für sie keine Rolle.

Viele Abstraktionen haben mehrere dieser Gründe zugleich. Entscheidend ist, welcher Grund eine bestimmte Abstraktion rechtfertigt. Erleichtert sie nur uns Menschen das Lesen, darf man sie ruhig hinterfragen, wenn Agenten den Grossteil des Lesens und Schreibens übernehmen.

 

 

Formatierung hilft nur Menschen beim Lesen. Namen und kleine Funktionen helfen auch Modellen, sich in einer Codebasis zurechtzufinden. Die Abstraktionen, die bis in die rechte Spalte reichen, braucht man weiterhin, egal wer den Code schreibt.

 

Was die Forschung bisher sagt

Viel Forschung, die diese Gründe direkt untersucht, gibt es noch nicht. In einer Studie von 2023 hat ein Team um Jain an der UC Berkeley die Trainingsdaten für Code-Generatoren aufgeräumt, also strukturierter und lesbarer gemacht. Die damit trainierten Modelle haben anschliessend besseren Code geschrieben. Modelle profitieren offenbar von derselben Struktur wie wir. Das ist nicht weiter überraschend, schliesslich haben sie an Code gelernt, den Menschen für Menschen geschrieben haben.

Das richtige Mass an Abstraktion finden Modelle allerdings nicht zuverlässig von allein. Die Macher von KISS Sorcar, einem neuen Coding-Assistenten, beschreiben, wie Modelle Hilfsklassen und zusätzliche Zwischenschichten einbauen, die das Debugging erschweren. Als Gegenmittel weisen sie das Modell ausdrücklich an, einfachen Code zu bevorzugen. Manche Modelle übernehmen Muster aus ihren Trainingsdaten, ohne abzuwägen, ob sie im konkreten Fall passen. Dabei entstehen leicht Abstraktionen, für die keiner der drei Gründe spricht.

Am deutlichsten unterscheiden sich Mensch und Modell bei der Formatierung. Eine Studie hat gemessen, wie viel vom Token-Budget eines Modells für Leerzeichen und Einrückungen draufgeht. Beides ist fast nur für Menschen da. Die Autoren schlagen Werkzeuge vor, die Menschen den formatierten Code zeigen und dem Modell eine kompakte Version geben. Ein klareres Beispiel kenne ich nicht: Diese Abstraktion dient nur unserem Kopf und lässt sich vom Code trennen.

Die Entwicklerinnen und Entwickler selbst haben Lesbarkeit noch lange nicht abgeschrieben. Atlassian hat 2025 die eigenen Entwicklerinnen und Entwickler befragt. Die Mehrheit sagte, dass lesbarer Code für Wartung und Zusammenarbeit unverzichtbar bleibt, auch wenn KI immer mehr davon schreibt. Solange Menschen den Code verantworten und pflegen, ist das eine vernünftige Haltung.

 

Ein Praxisbeispiel: hexagonale Architektur

Zu Beginn des Projekts habe ich entschieden, dass es einer hexagonalen Architektur folgen soll. Während der Umsetzung habe ich diese Entscheidung immer wieder angezweifelt. Vor allem konnte ich nicht ausschliessen, dass ich sie einfach aus Gewohnheit getroffen hatte.

Die hexagonale Architektur heisst auch Ports and Adapters. Die Fachlogik beschreibt dabei über Schnittstellen, was sie von aussen braucht. Diese Schnittstellen heissen Ports. Adapter setzen sie für eine konkrete Technik um, etwa für eine Datenbank oder ein Web-Framework. Meist werden zwei Vorteile genannt: Man kann die Infrastruktur austauschen, ohne die Fachlogik anzufassen, und man kann die Fachlogik ohne laufendes System testen. Beides lohnt sich aber nur, wenn es viel Fachlogik gibt und die Adapter sich voraussichtlich ändern. Eine Admin-Oberfläche legt dagegen vor allem Datensätze an, zeigt sie an, ändert und löscht sie. Vorher prüft sie jeweils, ob der Zugriff erlaubt ist. Bei mir zog also keines der beiden Argumente so richtig.

Nicht gerechnet hatte ich damit, wie nützlich die Regeln dieser Architektur werden, sobald Agenten den Code schreiben. Im Kern besteht die hexagonale Architektur aus ein paar Abhängigkeitsregeln. Die wichtigsten sind, dass die Fachlogik nie direkt auf Adapter zugreift und dass Adapter nur von Ports abhängen. Werkzeuge wie ArchUnit, dependency-cruiser oder eslint-plugin-boundaries können solche Regeln automatisch prüfen. In meinem Projekt haben eigene Linter-Regeln die typischen Verstösse abgefangen. Ein Beispiel: Die Fachlogik importiert einen Adapter direkt, statt über dessen Port zu gehen. Wer monatelang an einem Projekt arbeitet, verinnerlicht irgendwann dessen Konventionen. Man muss sie ihm nicht mehr eigens sagen. Ein Agent fängt dagegen jede Sitzung bei null an und weiss nichts von den Entscheidungen der vorherigen Sitzungen. Sein Code driftet deshalb nach und nach von der geplanten Struktur weg, obwohl jede einzelne Änderung für sich vernünftig aussieht. Die Konventionen müssen also ausserhalb des Agenten festgehalten werden, und zwar als Regeln, die bei jeder Änderung geprüft werden. Darum ist eine Architektur mit überprüfbaren Regeln in einem Projekt mit Agenten mehr wert als in einem klassischen.

 

 

Die durchgezogenen Pfeile zeigen die einzige erlaubte Richtung für Abhängigkeiten. Der gestrichelte Pfeil zeigt die Art von Verstoss, die die Linter-Regeln im Code der Agenten gefunden haben.

Derselbe Ansatz hat auch bei den Berechtigungen funktioniert. Wie oben beschrieben, lassen sie sich von aussen am schwersten überprüfen. Jede Route der Admin-Oberfläche steckt in einem Berechtigungs-Wrapper. Das ist eine Funktion, die festlegt, welche Berechtigung man braucht, um die Route aufzurufen. Eine Linter-Regel hat verlangt, dass jede Route so einen Wrapper hat. Das galt auch für öffentliche Routen: Sie brauchten einen ausdrücklichen PublicPermissions-Wrapper, sonst schlug die Prüfung fehl. Viele Systeme sind standardmässig offen. Fehlt dort die Prüfung, ist die Route einfach erreichbar, und niemand merkt es. Mit dieser Regel war jede Route erst einmal gesperrt. Das änderte sich erst, wenn jemand bewusst etwas anderes entschieden hat. Berechtigungen wurden immer ausdrücklich angegeben. Eine fehlende Prüfung hiess nie stillschweigend, dass jeder Zugriff hat. Ob die angegebene Berechtigung die richtige ist, kann die Regel natürlich nicht erkennen. Sie sorgt aber dafür, dass sich keine Route um diese Frage drücken kann. Für mich heisst das beim Review: Ich gehe eine Liste aller Routen mit ihren Berechtigungen durch. Ich muss nicht mehr den Code nach Wegen absuchen, die vielleicht ungeschützt geblieben sind. Für den Kunden heisst es: Für jede Seite und jede Aktion im System hat jemand entschieden, wer sie nutzen darf, sonst kommt sie nicht durch die Prüfung. Ausserdem kann er die vollständige Übersicht über alle Berechtigungen selbst prüfen oder von einem Auditor prüfen lassen. Er muss sich also nicht auf Zusicherungen verlassen.

Vereinfacht und mit Beispielnamen unterscheidet die Regel zwischen diesen drei Routen:

// Admin-Route: Nur Konten mit der passenden Rolle dürfen Benutzerkonten auflisten
export const GET = withPermissions(AdminPermissions.ReadUsers, async (request) => {
  return listUsers(request);
});

// Öffentliche Route: Braucht trotzdem einen ausdrücklichen Wrapper, sonst meldet der Linter einen Fehler
export const POST = withPermissions(PublicPermissions, async (request) => {
  return login(request);
});

// Vom Linter abgelehnt: kein Berechtigungs-Wrapper
export const DELETE = async (request) => {
  return deleteUser(request);
};

Gegen die hexagonale Architektur wird meist eingewandt, dass sie viel Code erzeugt: Ports, Adapter und Mapper für jedes Feature. Einem Agenten macht das fast keine Arbeit. Die zusätzlichen Schichten bleiben aber. Wenn etwas schiefgeht, muss man sich durch sie hindurcharbeiten, und das Review einer Änderung über mehrere Schichten dauert nach wie vor länger.

Ob gerade die hexagonale Architektur die richtige Wahl war, kann ich daraus nicht ableiten. Die Regeln haben Fehler abgefangen. Das zeigt mir, dass sich fest durchgesetzte Grenzen gelohnt haben. Eine einfachere Schichtenarchitektur mit ähnlichen Import-Regeln hätte vielleicht genauso gut funktioniert. So oder so hat sich die Architektur bewährt, weil sie die Struktur des Systems schützt. Damit dient sie dem dritten Grund. Daran hatte ich bei der Entscheidung gar nicht gedacht.

 

Wer was prüft

Meine Antwort auf die Ausgangsfrage lautet im Moment: Jeder prüft das, was er am besten kann. Statische Analyse und Linter-Regeln prüfen die Struktur, und zwar bei jeder Änderung auf genau dieselbe Weise. Die Review-Agenten prüfen die Details, etwa Sonderfälle und Sicherheitsprobleme wie bei den Audit-Einträgen. Das schaffen sie schneller und gründlicher als ich. Mir bleibt alles, was es nie bis in den Code schafft: Beschreibt die Spezifikation, was der Kunde braucht? Und gelten die Eigenschaften, auf die sich das System verlässt, auch wirklich?

Jede Prüfung deckt etwas ab, das die vorherige nicht sehen kann. Nur im letzten Schritt wird noch Code gelesen, und auch dort nur die Teile, die Garantien durchsetzen.

Ich muss also nicht mehr den ganzen Code lesen. Die Teile, die Garantien durchsetzen, lese ich aber weiterhin. Dazu gehören Berechtigungsprüfungen, Datenzugriffe und Schnittstellen, auf die sich andere Systeme verlassen. Ein Fehler dort fällt von aussen oft nicht auf, und weder ein Linter noch ein Review-Agent weiss, was eigentlich gemeint war. Beim Rest reicht es mir, das Verhalten zu beurteilen. Dafür gibt es zwei Voraussetzungen: Die Akzeptanzkriterien müssen genau genug sein, um daraus Tests abzuleiten, und ich muss die Kriterien und die Tests sorgfältig gelesen haben.

Der Kunde hält mich für das Ergebnis verantwortlich. Daran ändert auch die Freigabe durch einen Agenten nichts. Sie ist eine weitere Prüfung im Prozess, und zwar eine gute. Die Entscheidung, was gebaut werden soll, liegt aber weiterhin bei mir.

 

Vertrauen entsteht rund um den Agenten

Ich halte KI nach wie vor für eine weitere Abstraktionsebene. In einem wichtigen Punkt unterscheidet sie sich aber von einem Compiler. Einem Compiler vertrauen wir, weil seine Eingabe präzise ist und er immer gleich übersetzt. Ein Agent startet dagegen mit einer ungenauen Absicht. Das Vertrauen muss deshalb rund um ihn herum entstehen. Dazu braucht es präzise Akzeptanzkriterien, daraus abgeleitete Tests und automatisch geprüfte Regeln. Ausserdem braucht es einen Menschen, der sich den Code genau ansieht, an dem die Garantien des Systems hängen. Ist all das gegeben, muss man nicht mehr jede Zeile lesen. Dann reicht es, das Ergebnis mit der Vorgabe abzugleichen.

Das Projekt hat mir ausserdem gezeigt, dass sich eine Abstraktion manchmal aus ganz anderen Gründen lohnt, als man bei der Auswahl dachte. Ich habe mich zum Teil aus Gewohnheit für die hexagonale Architektur entschieden. Wichtig wurde sie dann, weil sie den Agenten Grenzen gesetzt hat, die sich automatisch prüfen liessen. Ich vermute, dass viele Abstraktionen, die wir für unverzichtbar halten, einen zweiten Blick verdienen. Bei manchen wird sich wohl herausstellen, dass es sie nur gibt, weil sie uns das Lesen leichter gemacht haben.

Wie lange wir Menschen noch Produktionscode lesen werden, kann ich nicht sagen. Im Moment reviewen wir Änderungen noch selbst und gehen Störungen selbst auf den Grund. Abstraktionen, die Code lesbar machen, bleiben deshalb wertvoll. Wenn sich unsere Rolle weiter in Richtung Spezifizieren und Überprüfen verschiebt, könnte Lesbarkeit aber zu etwas werden, das man bei Bedarf erzeugt. Ein Agent könnte dann zum Beispiel einen Teil des Codes erklären oder umbauen, sobald jemand ihn verstehen muss. Mit der Formatierung geht das heute schon, denn sie lässt sich unabhängig vom Code anwenden. Ob das gut genug funktioniert, um sich darauf zu verlassen, wird sich noch zeigen.