LLMs auf große Codebasen skalieren
(blog.kierangill.xyz)- Um LLMs in großen Codebasen effektiv zu nutzen, sind Investitionen in „Guidance“ und „Oversight“ entscheidend
- Guidance liefert Kontext und Rahmenbedingungen, damit das LLM bessere Entscheidungen treffen kann, und Oversight übernimmt die Prüfung der Ergebnisse und gibt die Richtung vor
- Der Aufbau einer Prompt-Bibliothek ist wichtig, damit das LLM die Regeln, Dokumentation und Best Practices der Codebasis verstehen kann
- Der Umgang mit technischem Schuldenstand sowie Einfachheit, Modularisierung und Konsistenz der Codestruktur sind direkt mit einem besseren Codeverständnis und höherer Produktivität des LLMs verbunden
- Automatisierte Oversight- und Verifikationssysteme zu nutzen, um das LLM bei der Erzeugung sicheren und konsistenten Codes zu unterstützen, ist der Schlüssel zu langfristiger Skalierbarkeit
Zentrale Konzepte für die Skalierung von LLMs
- Wie LLMs auf große Codebasen angewendet werden sollten, ist noch nicht abschließend geklärt, doch Investitionen in Guidance und Oversight werden als der wirksamste Ansatz dargestellt
- Guidance bezeichnet den Kontext und das Umfeld, die dem LLM helfen, die richtigen Entscheidungen zu treffen, während Oversight die erzeugten Ergebnisse überprüft und ihre Richtung korrigiert
Investitionen in Guidance
- Damit ein LLM beim ersten Versuch hochwertigen Code erzeugt, also „One-Shotting“ erreicht, braucht es klare Guidance
- Umgekehrt ist es ineffiziente Nacharbeit (Rework), wenn das Ergebnis ungeeignet ist und manuell korrigiert werden muss
- Da ein LLM alle Entscheidungen im Code erzeugt — Variablennamen, Funktionsstruktur, Tech-Stack usw. — ist es ideal, wenn der Prompt nur die Geschäftsanforderungen enthält und alles andere ableitbar oder kodiert ist
Aufbau einer Prompt-Bibliothek
- Eine Prompt-Bibliothek ist eine Sammlung von Kontexten für das LLM, darunter Dokumentation, Best Practices und strukturelle Übersichten der Codebasis
- Jedes Mal, wenn die Ausgabe des LLMs danebenliegt, sollte geprüft werden, „was klarer hätte formuliert werden müssen“, und dies der Bibliothek hinzugefügt werden
- Wichtig ist ein Gleichgewicht zwischen Vollständigkeit und Prägnanz
- Im Beispiel werden Dokumente wie
@prompts/How_To_Write_Views.mdund@prompts/The_API_File.mddem LLM bereitgestellt, um die Feature-Entwicklung anzuleiten - Prompts sollten ausreichend konkret sein, trotzdem muss jede Zeile des erzeugten Codes überprüft werden
Umgebung und Codequalität
- Eine Codebasis mit vielen technischen Schulden (technical debt) verringert die Effizienz beim Einsatz von LLMs
- Im Fall von Meta wird erwähnt, dass technische Schulden das Erreichen von Automatisierungszielen erschwerten
- Sauberer Code, Modularisierung, klare Benennung und einfache Strukturen erhöhen das Verständnis und die Genauigkeit des LLMs
- Im Django-Beispiel wird der Einstiegspunkt jeder App in einer Datei namens
_api.pygebündelt, damit das LLM benötigte Funktionen schnell finden kann- Beispiel:
visit_api.handoff_to_doctor(user)als vereinheitlichter externer Zugriff - Das
_api-Muster wird in der Prompt-Bibliothek festgehalten, damit das LLM auf die richtige Stelle verwiesen wird
- Beispiel:
Investitionen in Oversight
- An LLM-Automatisierung sollte man nicht als Ersatz für Engineers denken, sondern als Möglichkeit, das Team zu stärken
- Oversight führt zu Investitionen in Team, Alignment und Workflow
- Auf Teamebene ist die Stärkung der Designkompetenz wichtig, was sich direkt auf die Architekturqualität auswirkt
- Als Methoden zur Stärkung der Designkompetenz werden das Lesen von Büchern, Blogs und Code, das Nachbauen von Meisterwerken sowie praktische Implementierungsübungen genannt
- Beispiel: Erweiterung des Architekturverständnisses durch Codeanalysen von TLDraw und SerenityOS Jakt
Automatisiertes Oversight
- Ein Teil der Designprüfung lässt sich programmatisch automatisieren
- Beispiel: Sofortiges Feedback aus der Umgebung bei Typfehlern oder Regelverstößen
- „Safety“ bedeutet, Abstraktionen zu schützen
- Nach Pierces Definition stellt eine sichere Sprache sicher, dass Programmierer Abstraktionen nicht unbeabsichtigt verletzen
- Beispiel: Eine Regel, die den direkten Zugriff auf interne Dateien zwischen Django-Apps verbietet, wird durch ein AST-basiertes Prüfskript automatisiert
- Unerlaubte Zugriffe wie
from visit import logic.internal_filewerden erkannt
- Unerlaubte Zugriffe wie
Verifikation
- Neben Design und Implementierung ist auch die Verifikationsphase (Code Review, QA) unerlässlich, um Qualität sicherzustellen
- Mit wachsendem Arbeitsvolumen wird die Review-Geschwindigkeit zum Engpass, daher werden folgende Verbesserungen vorgeschlagen
- Die Hürde für QA senken, damit sie auch ohne Entwicklungsumgebung möglich ist
- Eine Umgebung schaffen, in der das Schreiben von Tests einfach ist, etwa durch die Generierung von Testdaten
- Wiederkehrendes PR-Feedback dokumentieren, damit das LLM einen Teil der Reviews automatisch durchführen kann
- Sicherheitsregeln als Framework-Defaults integrieren
Fazit und weitere Beobachtungen
- LLMs funktionieren besonders gut in Greenfield-Projekten
- Weil dort bestehender Kontext fehlt und die Anforderungen an Konsistenz geringer sind
- Je größer ein Projekt wird, desto stärker bestimmen Konsistenz und Modularisierung die Produktivität
- Eine modulare Struktur, die verifizierte Komponenten wiederverwendet, ist der Schlüssel zu effizienter Entwicklung
1 Kommentare
Hacker-News-Kommentare
Mit der fortlaufenden Verbesserung der Modelle können sie inzwischen auch komplexe Codebases oder lange Dateien handhaben.
Deshalb habe ich eine einfache Framework-Schleife gebaut, die ich wiederholt nutze.
Wenn man diese Schleife 20–30 Minuten laufen lässt, kommt ein ziemlich brauchbares Ergebnis heraus. Letztlich geht es vor allem um Kontext-Management und darum, eine Test-Feedback-Schleife aufzubauen.
Wenn man die Erkundung mit dem Schlüsselwort „explore“ startet, ist Research auch ohne separaten Plan oder Kontext-Reset möglich.
Allerdings gehen sie oft davon aus, dass die Codesprache C oder Python ist, und erzeugen daher manchmal unstrukturierten Code, etwa indem sie den Zustand nicht in Objekten kapseln, sondern in fünf Funktionen aufteilen.
Außerdem ignoriert Claude gelegentlich CLAUDE.md; stabiler wird es, wenn man diese Datei zuerst lesen lässt und danach „explore“ startet.
Neuere Modelle werfen nutzlosen Kontext gut weg, aber bei älteren Modellen ist ein planbasiertes Vorgehen oft immer noch besser.
Es kommt häufig vor, dass es Pläne und Richtlinien genau gegenteilig ausführt oder denselben Satz erneut liest und trotzdem zum entgegengesetzten Schluss kommt.
Eine Zeit lang glaubte ich, man könne einen LLM-zentrierten Prozess aufbauen, aber inzwischen bin ich deutlich weniger überzeugt.
Wenn das Modell in einem „guten Zustand“ ist, funktioniert es ordentlich, aber es dorthin zu bringen, hängt immer noch stark vom Zufall ab.
Ich habe benutzerdefinierte Befehle wie
/research_codebase,/create_planund/implement_planzu Claude Code hinzugefügt.Wenn man sorgfältig reviewt und nachbessert, funktioniert das sehr gut, aber im ganzen Team hat es sich nicht durchgesetzt.
Nur bei komplett neuen Funktionen setze ich den Kontext zurück. In Codespaces ist das okay, aber die Tasks-Funktion ist fast nutzlos.
Wenn man große Aufgaben gibt, läuft es manchmal in eine völlig falsche Richtung, deshalb muss man ständig aufpassen.
Auch für Context-Repriming ist das nützlich, deshalb setze ich es oft ein.
Damit eine Prompt-Bibliothek wirklich nützlich wird, braucht es iterative Verbesserung.
Jedes Mal, wenn das LLM leicht danebenliegt, frage ich mich selbst: „Was hätte ich klarer formulieren sollen?“ und füge die Antwort dem Prompt hinzu.
Wenn man einfach nur Enter drückt oder automatisch bestätigt, verschwendet man bloß Tokens. Stattdessen sollte man beobachten, wo das LLM hängenbleibt, und das kurz in CLAUDE.md festhalten.
Wenn die Kontextdatei zu groß wird, teile ich sie nach Aufgabentypen auf.
Meine wichtigsten Anwendungsfälle sind das Erkunden der Codebase, das Nachverfolgen von Ausführungspfaden und das Bereitstellen von Zusammenfassungen relevanter Dateien. Wenn man je Fragetyp die Form der Ergebnisübergabe festlegt, wird es deutlich effizienter.
Ich nenne das das „Student Pattern (Fresh Eyes)“. Ein Subagent liest Dokumentation oder Code mit dem Blick eines Einsteigers und findet Stellen der Verwirrung, Widersprüche und fehlende Informationen.
Das ist hervorragend, um implizites Wissen aufzudecken, das Entwickler übersehen. Als Vorstufe für neue Doku-Reviews oder Prompt-Bewertungen ist das sehr nützlich.
Selbst wenn man mehrfach anweist, CLAUDE.md zu lesen, wird das oft ignoriert, und direkt nach Sitzungsbeginn passiert es ebenfalls zufällig.
Auch wenn Dokumente und Befehle alle vorbereitet sind, „vergisst“ es sie immer noch oft.
Ich experimentiere damit, große Codebases agentenfreundlich zu strukturieren.
Ich zerlege ein Projekt in einen auf nix flakes basierenden gerichteten Graphen, sodass jeder Knoten eine eigene Entwicklungsumgebung hat.
Wenn Claude Code in einer flake devshell läuft, erkennt es nur diesen Bereich und verhindert so Kontextüberlastung.
Über Ein- und Ausgaben zwischen den flakes können sie zusammenarbeiten und sich gegenseitig Feature-Anfragen und Tests zuspielen.
Ich denke, diese Kontextaufteilung ist der Schlüssel, um das Problem explodierender Token-Mengen zu reduzieren.
Statt nur bestehende Workflows zu verbessern, muss man neue Strukturen entwerfen, die sich von LLMs leicht skalieren lassen.
Ich untersuche die Beziehung zwischen Eigenschaften wie „Testbarkeit, Skalierbarkeit und Sicherheit“ und der Codestruktur.
Ich habe mit Docker-basierten Projekten etwas Ähnliches ausprobiert und hätte gern ein Tool, das das automatisiert.
LLMs erklären Themen, die ich selbst nicht gut kenne, oft hervorragend, aber in meinem Fachgebiet liegen sie mit großem Selbstvertrauen falsch.
Ich sehe das etwas anders. Der beste Weg, die Leistung von LLMs zu steigern, ist, Bedeutung direkt in die Codebase einzubetten.
Anders gesagt: Code, der wie bei DDD (Domain-Driven Design) strukturiert ist, ist auch für LLMs leichter verständlich.
Zu versuchen, komplexen Code mit Tools gewaltsam handhabbar zu machen, ist Geldverschwendung.
Letztlich haben LLMs nur bestätigt, dass die DDD-Philosophie mit ihrem Fokus auf „Sprache und Bedeutung“ richtig war.
Passender Artikel: DDD & the Simplicity Gospel
Auf die Frage „Warum funktionieren LLMs in Greenfield-Projekten so gut?“ habe ich die gegenteilige Erfahrung gemacht.
Sobald sich in einer Codebase ein Muster zwei- oder dreimal wiederholt, lernt das LLM es und reproduziert es konsistent.
Aber Konsistenz ist nicht gleich Qualität. Wenn man nur auf Konsistenz ohne Maßstab setzt, endet man bei nicht wartbarem Code.
Der Satz „Code, den ein Ingenieur nicht versteht, versteht auch ein LLM nicht“ stimmt, aber die Umkehrung gilt nicht.
Oft verstehen Menschen etwas, Agenten aber nicht.
Eine Codebase so zu gestalten, dass Agenten sie leichter verstehen als Menschen, ist noch schwieriger.
Es gibt auch die Behauptung, dass die Erfolgsquote beim One-Shot steigt, wenn man Feedback in den Computer verlagert, aber das ist fast so, als würde man P=NP behaupten.
Nur weil Verifikation leicht ist, heißt das nicht, dass es auch leicht ist, die Lösung zu finden.
In Sprachen wie ATS oder Idris kann man Korrektheitsbeweise zusammen mit dem Code schreiben.
Wenn diese Behauptung stimmen würde, müssten LLMs gerade in solchen Sprachen die beste Leistung zeigen.
In der Realität ist das nicht so. Deshalb denke ich derzeit, dass es besser ist, auf bessere Modelle zu warten.
Wegen solcher Probleme glaube ich, dass meinungsstarke Frameworks die Produktivität beim AI-Coding erhöhen werden.
Die Regeln des Frameworks kennt das LLM bereits, sodass keine separaten Richtlinien nötig sind.
Bei Go, Rust, Elixir und C# brauchte es dagegen deutlich mehr Abhängigkeiten und Anweisungen.
Rust lieferte zwar gute Ergebnisse, zog aber mehr als 200 Pakete herein, was eine große Belastung war.
Das Prinzip „Garbage in, garbage out“ stimmt, aber auf LLMs lässt es sich nicht vollständig anwenden.
Obwohl sie mit den Rauschdaten des gesamten Internets trainiert wurden, funktionieren sie überraschend gut.
Halluzinationen entstehen häufiger durch ungenauen Kontext als durch bloßes Rauschen.
Selbst eine chaotisch strukturierte Codebase kann noch nützlichen Kontext liefern, wenn sie genügend Informationen enthält.
Im Grunde lernen die Leute gerade die Grundprinzipien neu.
Sie merken wieder, dass Dokumentation (= Prompt-Bibliothek) und eine aufgeräumte Codestruktur die Entwicklung beschleunigen.