So helfen uns LLMs beim Programmieren

Vor Kurzem wurden wir von einem weltweit agierenden Chemiekonzern angefragt, ob wir deren Ingenieur*innen eine Einführung in die Programmierung mit Python und Large Language Models (LLM) geben könnten. Ihre Erwartungen, was sie innerhalb kurzer Zeit mit einem LLM erreichen wollten, waren fast grenzenlos.

Programmieren mit LLMs ist jedoch häufig schwierig und wenig intuitiv. Es bedarf meist erheblicher Anstrengungen, um ihre Beschränkungen herauszufinden. Zudem gibt es kaum Anleitungen, wie sie am besten verwendet werden können. Seit zweieinhalb Jahrenarbeiten wir nun mit LLMs beim Programmieren. Im Folgenden versuchen wir, etwas von unserer Erfahrung mit LLMs an euch weiterzugeben.

Was dürft ihr von LLMs erwarten?

Der Hype um Artificial General Intelligence (AGI) weckt Erwartungen, die die LLMs nicht erfüllen können: Wenn ihr davon ausgeht, dass LLMs euer Software-Projekt perfekt realisieren werden, ohne dass ihr selbst etwas dafür tun müsst, werdet ihr schnell enttäuscht sein.

Ihr könnt jedoch stattdessen eure Fähigkeiten mithilfe eines LLM erweitern. Stellt euch ein LLM als Unterstützung wie beim Pair Programming vor, die blitzschnell nachschlagen, relevante Beispiele liefern und aufwändige Aufgaben klaglos erledigen kann. Es wird jedoch auf jeden Fall kleinere oder größere Fehler machen, z.B. eine Bibliothek oder Methode halluzinieren. Wir haben solche Beispiele notiert um dann bei neueren und größeren LLMs zu schauen, ob diese brauchbarere Ergebnisse für eine Aufgabe liefern.

Vibe-Coding

Andrej Karpathy hat im Februar 2025 den Begriff Vibe-Coding geprägt:

Es gibt eine neue Art der Codierung, die ich „Vibe Coding“ nenne, bei der man sich ganz den Vibes hingibt, die Exponentiale annimmt und vergisst, dass der Code überhaupt existiert … Ich frage nach den dümmsten Dingen, wie z.B. „Verringere den Abstand der Seitenleiste um die Hälfte“, weil ich zu faul bin, sie zu finden. Ich akzeptiere immer „Alles“, ich lese die Diffs nicht mehr.

Wenn ich Fehlermeldungen erhalte, kopiere ich sie einfach kommentarlos in den Code, das behebt das Problem normalerweise … Manchmal können die LLMs einen Fehler nicht beheben, also arbeite ich einfach drum herum oder bitte um zufällige Änderungen, bis er verschwindet. Das ist nicht so schlimm, wenn es sich um Wochenendprojekte handelt, und trotzdem recht amüsant. [1]

Dies ist eine gute Möglichkeit, die LLMs zu erkunden – und es macht wirklich Spaß. So entwickelt ihr auch ein Gespür dafür, was mit LLMs funktioniert und was nicht.

Für unsere professionellen Aufgaben verwenden wir jedoch einen anderen Ansatz um robusten, wart- und pflegbaren Code zu erhalten.

1. Prototyping

Die meisten unserer Projekte beginnen mit offenen Fragen für einen Prototyp:

  • Ist das, was wir vorhaben, möglich?
  • Welche Möglichkeiten gibt es, dies umzusetzen?
  • Welche dieser Möglichkeiten sind die besten?

Wir verwenden LLMs also als Teil dieser ersten Forschungsphase.

Der nächste Schritt ist dann ein Prototyp, der beweist, dass die Hauptanforderungen des Projekts erfüllt werden können. Wir stellen oft fest, dass mit einem LLM innerhalb weniger Minuten ein Minimum Viable Product, ein funktionierender Prototyp, erstellt werden kann.

Berücksichtigt den Trainingsstichtag

Ein entscheidendes Merkmal eines jeden Modells ist sein training cut-off date (engl.: Trainingsstichtag). Das ist das Datum, an dem die Daten, auf denen sie trainiert wurden, nicht mehr gesammelt werden. Bei den Modellen von OpenAI ist dies der Oktober 2023; Anthropic und Gemini haben meist aktuellere Daten.

So werden die OpenAI-Modelle von uv kaum etwas wissen, da die ersten Commits aus dem Oktober 2023 stammen. Dasselbe gilt für die umfangreichen Änderungen in Version 2 von pandas oder NumPy.

LLMs können euch zwar immer noch helfen, mit Bibliotheken zu arbeiten, die außerhalb ihrer Trainingsdaten existieren, aber ihr müsst deutlich mehr Arbeit investieren und aktuelle Beispiele liefern, wie diese Bibliotheken als Teil eures Prompt verwendet werden sollten.

2. Spezifikation

Sobald der anfängliche Prototyp erstellt wurde, ändern wir den Modus drastisch. Für produktiven Code sind wir viel pingeliger zum LLM: auf der Grundlage unserer detaillierten Anweisungen schreibt das LLM den Code viel schneller, als wir dies selbst könnten. LLMs können sehr gut mit solch spezifischen Anweisungen umgehen, und wir können uns auf das Software-Design konzentrieren.

Dies bringt uns zum wichtigsten Punkt, den ihr bei der Arbeit mit LLMs verstehen müsst:

Der Kontext ist entscheidend

Um gute Ergebnisse mit einem LLM zu erzielen, besteht der größte Aufwand darin, den Kontext zu verwalten, also den Text, der Teil eurer aktuellen Konversation ist.

Dieser Kontext beschränkt sich nicht nur auf den Prompt, mit dem ihr das LLM gefüttert habt, sondern auf den gesamten Gesprächsfaden. Wenn ihr also eine neue Unterhaltung beginnt, solltet ihr diesen Kontext wieder zurücksetzen.

Tools wie Cursor und VS Code Copilot beziehen den Kontext aus der aktuellen Editor-Sitzung und dem Dateilayout automatisch mit ein, und ihr könnt manchmal Mechanismen wie die @-Befehle von Cursor verwenden, um zusätzliche Dateien oder Dokumentationen hinzuzuziehen. Claude Projects hingegen ermöglicht euch, den Kontext mit einer großen Menge an Text vorzubefüllen – einschließlich einer neuen Möglichkeit, Code direkt aus einem GitHub-Repository zu importieren (siehe Verwendung der GitHub-Integration).

Einer der Gründe, warum wir meistens direkt mit ChatGPT- und Claude arbeiten, ist, dass wir besser verstehen, was in den Kontext eingeht. LLM-Tools, die diesen Kontext vor uns verbergen, sind da wenig hilfreich. So können wir die Tatsache nutzen, dass bei komplexen Programmieraufgaben frühere Antworten ebenfalls Teil des Kontexts sind:

  1. zunächst können wir eine vereinfachte Variante schreiben lassen
  2. dann können wir überprüfen, ob diese funktioniert
  3. und schließlich iterativ auf die komplexere Implementierung hinzuarbeiten.

Wir beginnen oft einen neuen Chat, indem wir vorhandenen Code einfügen, um den Kontext zu schaffen, und dann arbeiten wir mit dem LLM zusammen die Veränderungen aus. Oder wir bringen mehrere vollständige Beispiele ein um dann das LLM aufzufordern, sie als Inspiration für ein neues Projekt zu verwenden.

3. Code-Review und Refactoring

Das, was ihr nicht an das LLM auslagern dürft, ist das Testen und den Code-Review dessen, was das LLM geliefert hat. Unsere Aufgabe als Software-Ingenieur*innen ist es, funktionierende Systeme zu liefern. Wenn wir nicht gesehen haben, wie es läuft, können wir uns auch nicht darauf verlassen, dass es überhaupt läuft. Diese Qualitätssicherung bleibt uns Menschen vorbehalten.

Wenn uns nicht gefällt, was ein LLM geschrieben hat, lassen wir es einfach von ihm überarbeiten. „Nutze für den Code die Vektorisierung mit NumPy“. Der Code, den ein LLM beim ersten Mal produziert, ist selten die endgültige Implementierung, aber es kann ihn dutzende Male für euch neu schreiben, ohne frustriert oder gelangweilt zu sein. Wenn ein erstes Ergebnis nicht schon zum gewünschten Ergebnis führt, ist dies kein Misserfolg, sondern der Beginn eines Prozesses, den Code in die gewünschte Richtung voranzutreiben.

Tools für die Code-Ausführung

Eine wachsende Zahl von LLMs zum Programmieren bieten jetzt auch die Möglichkeit, den Code für euch auszuführen. Ihr solltet bei einigen von ihnen jedoch vorsichtig sein, da ein falscher Befehl echten Schaden anrichten kann. Daher bleiben wir aktuell bei jenen, die den Code in einer Sandbox ausführen:

ChatGPT Code Interpreter
erlaubt euch, mit ChatGPT Python-Code zu schreiben und dann direkt in einer von OpenAI verwalteten Kubernetes-Sandbox-VM ausführen.
Claude Artifacts
kann eine vollständige HTML-, JavaScript- und CSS-Webanwendung erstellen, die in der Claude-Oberfläche angezeigt wird. Diese Webanwendung wird in einer iframe-Sandbox angezeigt, was die Möglichkeiten stark einschränkt, aber Probleme wie die versehentliche Weitergabe eurer privaten Claude-Daten, verhindert.
ChatGPT Canvas
ist eine neuere ChatGPT-Funktion mit ähnlichen Möglichkeiten wie Claude Artifacts, die wir jedoch noch nicht hinreichend testen konnten.

4. Dokumentation und Tests

Abschließend kann das LLM auch die Docstrings, die Sphinx-Dokumentation und die Tests für pytest schreiben. Gute LLMs für die Programmierung sind hervorragend geeignet, mögliche Ausnahmen abzufangen, genaue Dokumentationen hinzuzufügen und den Code mit den relevanten Typen zu kommentieren.


[1]https://x.com/karpathy/status/1886192184808149383