Vor etwa fünf Jahren stieß ich auf eine wissenschaftliche Arbeit mit dem Titel The ACPATH Metric: Precise Estimation of the Number of Acyclic Paths in C-like Languages von Roberto Bagnara, Abramo Bagnara, Alessandro Benedetti und Patricia Hill. Ich fand sie interessant genug, um ein TODO für die Implementierung dieser Softwaremetrik in meiner Bibliothek sebastian/complexity anzulegen. Dann geschah, was mit TODOs eben so passiert: Es lag dort jahrelang herum.
Kürzlich erinnerte ich mich an dieses offene Issue und dachte, es könnte ein interessantes Experiment sein: Anstatt die Metrik selbst zu implementieren, würde ich einen KI-Agenten damit beauftragen. Nicht, weil ich Zeit sparen wollte, sondern weil ich beobachten wollte, was passiert, wenn ich eine nicht triviale Aufgabe an einen KI-Agenten delegiere, und zwar in einem Bereich, in dem ich selbst kein Experte bin.
Was ACPATH misst
Die meisten Entwicklerinnen und Entwickler kennen die zyklomatische Komplexität, die die Anzahl der linear unabhängigen Pfade durch eine Funktion zählt. ACPATH verfolgt einen anderen Ansatz und zählt die Anzahl der einzigartigen, schleifenfreien (azyklischen) Ausführungspfade.
Um zu verstehen, was das bedeutet, betrachten wir den Kontrollflussgraphen einer Funktion: einen gerichteten Graphen, in dem Knoten Anweisungen oder Entscheidungen darstellen und Kanten den Kontrollfluss zwischen ihnen repräsentieren. Bedingungen wie if-Anweisungen erscheinen als rautenförmige Entscheidungsknoten mit zwei ausgehenden Kanten: eine für den True-Zweig und eine für den False-Zweig.
Sequenzielle Entscheidungen multiplizieren die Pfadanzahl. Betrachten wir das folgende Beispiel:
function f(bool $a, bool $b, bool $c): int { $x = 0; if ($a) { $x += 2; } if ($b) { $x -= 1; } if ($c) { $x *= 2; } return $x; }
Jede der drei if-Anweisungen spaltet den Kontrollfluss unabhängig in zwei Zweige auf. Da die Entscheidungen sequenziell sind, multiplizieren sich die Pfadanzahlen: 2 × 2 × 2 = 8 azyklische Ausführungspfade. Der ACPATH-Wert dieser Funktion ist daher 8.
Das Experiment
Mein erster Prompt forderte den KI-Agenten auf, die ACPATH-Metrik wie in der Arbeit beschrieben zu implementieren. Die Ausgabe vermittelte den Eindruck, dass genau das umgesetzt worden war, wonach ich verlangt hatte. Es wurden auch Tests generiert. Die Tests sahen für mich gut aus, und bei einfachen Funktionen, bei denen ich die Anzahl der Ausführungspfade manuell zählen konnte, lieferte die Implementierung dasselbe Ergebnis. Dasselbe schien auch für den Code zu gelten, der in den Tests als Fixture verwendet wird.
Mit meinem zweiten Prompt verlangte ich ein Werkzeug zur Visualisierung des Kontrollflussgraphen, der Pfad-Enumeration und der Komplexitätszerlegung. Der Zweck dieser Visualisierungen war, mein Nachdenken darüber zu unterstützen, ob die Implementierung korrekt ist. Auch das schien auf den ersten Blick zu funktionieren.
Der Code für die Implementierung des Algorithmus zur Berechnung der ACPATH-Metrik sieht sauber aus und ich kann ihn lesen. All das hat den KI-Agenten etwa 15 Minuten gekostet.
Das Problem
Und hier wird es interessant. Anders als bei jeder bisherigen Softwareentwicklungsaufgabe, bei der ich mit LLM-gestütztem Programmieren experimentiert habe, bin ich in diesem Fall kein Domänenexperte.
Ja, ich bin Informatiker. Ich kann die Arbeit lesen, und wenn ich genug Zeit investiere, bin ich zuversichtlich, dass ich sie verstehen und den Algorithmus selbst implementieren könnte. Aber das würde Stunden dauern, vielleicht Tage. Der KI-Agent hat 15 Minuten gebraucht.
Und nun frage ich mich schon seit Stunden, ob die generierte Implementierung korrekt ist oder nicht.
Die Tests sind grün. Der Code ist lesbar. Die Visualisierungen sehen plausibel aus. Für die Fälle, die ich manuell überprüfen kann, sind die Ergebnisse korrekt. Aber für die Fälle, die ich nicht manuell überprüfen kann, und das sind genau die Fälle, auf die es ankommt, habe ich keine Möglichkeit festzustellen, ob die Implementierung den in der Arbeit beschriebenen Algorithmus korrekt umsetzt. Ich müsste die Arbeit tief genug verstehen, um das zu überprüfen, und wenn ich die Arbeit so tief verstünde, hätte ich sie selbst implementieren können.
Ist das ein Produktivitätsgewinn?
Das ist die Frage, die mich nicht loslässt. 15 Minuten KI-Agentenzeit versus Stunden oder Tage meiner eigenen Zeit klingt nach einem klaren Gewinn. Aber ich habe inzwischen mehr Zeit damit verbracht, das Ergebnis zu überprüfen, als ich durch die Nicht-Implementierung gespart habe. Und ich bin immer noch nicht sicher, ob die Implementierung korrekt ist.
Ist das wirklich ein Produktivitätsgewinn? Wahrscheinlich nur für Leute, denen es egal ist, ob sie Software produzieren, die korrekt funktioniert. Oder für Leute, die nicht erkennen – oder sich nicht eingestehen –, dass sie den von einem KI-Agenten generierten Code nicht beurteilen können.
Wenn ich KI-Agenten für Aufgaben eingesetzt habe, bei denen ich Domänenexperte bin, ist die Überprüfung unkompliziert. Ich kann den generierten Code lesen und sofort sehen, ob er das tut, was er soll. Der KI-Agent erspart mir den mechanischen Aufwand des Tippens, aber der intellektuelle Aufwand des Verstehens und Überprüfens bleibt bei mir, wo er auch hingehört.
Aber wenn die Domänenexpertise fehlt, entsteht eine fundamentale Asymmetrie: Der KI-Agent kann Code in einer Domäne schneller generieren, als ich diese Domäne verstehen kann. Der Code sieht richtig aus. Die Tests sind grün. Aber richtig aussehen und richtig sein sind nicht dasselbe.
Stoff zum Nachdenken
Dieses Experiment hat mir viel zu denken gegeben. Das Versprechen des KI-gestützten Programmierens ist, dass es uns produktiver macht. Und das tut es, in einem engen Sinne: Code wird schneller geschrieben. Aber Code zu schreiben war nie der Engpass. Das Problem zu verstehen, die Lösung zu entwerfen und das Ergebnis zu überprüfen: das sind die schwierigen Teile. Der KI-Agent hilft bei keinem davon.
Das ACPATH-Experiment hat etwas greifbar gemacht, was ich zuvor nur vermutet hatte: Die eigentliche Gefahr des KI-gestützten Programmierens liegt nicht darin, dass es schlechten Code produziert. Sie liegt darin, dass es Code produziert, der gut genug aussieht, damit wir aufhören, ihn zu hinterfragen.
Sir Tony Hoare sagte einmal:
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies.
KI-generierter Code tendiert zur zweiten Art. Unsere Aufgabe ist es, auf der ersten zu bestehen.