Vibe-Coding in der Praxis: Learnings, Grenzen und deterministische Leitschienen
LLM-gestützte Entwicklung („Vibe-Coding“) verspricht enorme Produktivitätssprünge, stößt bei komplexen Enterprise-Applikationen jedoch oft an ihre Grenzen. Wenn probabilistische Systeme auf deterministische Anforderungen treffen, schleichen sich systematische Architekturfehler ein. Dieser Artikel analysiert anhand eines realen Praxisprojekts typische Fehlerklassen der KI-Codegenerierung. Erfahren Sie, wie Sie mit generativen Ansätzen (FASTCODE) und formalen Methoden deterministische Leitschienen einziehen, um KI-Potenziale zuverlässig und skalierbar im Unternehmenskontext zu nutzen.
1. Einleitung
Im Anschluss an den letzten Artikel „Wenn der Vibe verfliegt“ und nach über einem Jahr produktiver Arbeit mit LLM-gestützter Softwareentwicklung erlauben wir uns folgende Einschätzung: Vibe-Coding funktioniert in vielen Bereichen sehr gut und die Fortschritte sind laufend und schnell. Wer heute mit Claude Code, RooCode, OpenCode, Cursor oder GitHub Copilot arbeitet, erlebt eine Produktivitätssteigerung, die weit über das Generieren von Boilerplate-Code hinausgeht. LLMs helfen bei Architekturentscheidungen, beim Erschließen neuer Frameworks und beim Durchdenken komplexer Algorithmen. Gleichzeitig zeigen sich in der täglichen Arbeit wiederkehrende Muster, die auf systematische Grenzen hinweisen. Diese Grenzen sind weder zufällig noch modellspezifisch; sie entstehen aus einem fundamentalen Spannungsfeld, in dem probabilistische Systeme auf deterministische Anforderungen treffen. Ein LLM optimiert auf das statistisch plausibelste nächste Token, während eine Unternehmensapplikation globale Konsistenz über Hunderte von Dateien, Schichten und Datenmodellen hinweg verlangt.
Die zentrale Frage, die wir stellen, lautet: Wie können wir probabilistischen Systemen deterministische Leitschienen geben, damit sie zuverlässiger arbeiten? Solche Leitschienen können auf verschiedenen Ebenen ansetzen – von pragmatischen Ansätzen wie MCP-Servern und RAG über die tiefere Integration von Compiler-Strukturen bis hin zu formalen Methoden aus der Symbolic Computation. In diesem Artikel betrachten wir die Ansätze.
Als „Bühne“ für das Vibe-Coding dient in diesem Artikel ein Produktinformationssystem (PIM) mit automatischer Shopify-Synchronisation. Der Artikel gliedert sich in vier inhaltliche Blöcke:
- Zunächst stellen wir einen FASTCODE-Generator-Ansatz vor (Kapitel 2), der dem Vibe-Coding einen deterministischen Rahmen gibt.
- Danach beleuchten wir die beiden Haupt-Use-Cases: das Produktinformationssystem (Kapitel 3) und eine automatisierte Shopify-Synchronisation mit KI-gestützter Bildgenerierung (Kapitel 4).
- In Kapitel 5 analysieren wir, warum die Kombination aus Generator und LLM funktioniert, bevor wir in Kapitel 6 systematische Fehlerklassen beschreiben, die uns während des Projekts begegnet sind.
- Abschließend skizzieren wir in Kapitel 7 Ansätze, die das Potenzial haben, die Grenzen probabilistischer Systeme grundlegender zu verschieben – von AST-Integration über formale Verifikation bis zur Symbolic Computation. Dabei handelt es sich ausdrücklich nicht um ausgearbeitete Lösungen, sondern um Denkrichtungen und offene Forschungsfragen, die zeigen, in welche Richtung sich die Werkzeuge entwickeln könnten.
2. FASTCODE statt Lowcode: Die Meta-Ebene
Wer mittels Vibe-Coding eine datengetriebene Applikation entwickelt, stellt schnell fest, dass sich bestimmte Aufgaben in jeder Iteration wiederholen: Formulare anlegen, Lookup-Felder verdrahten, Beziehungen zwischen Entitäten in der UI abbilden, Validierungen einbauen, die Navigation konfigurieren. Das LLM erledigt jede dieser Aufgaben für sich genommen zuverlässig. Aber über das Gesamtsystem hinweg schleichen sich Inkonsistenzen ein: Ein Formular verwendet einen anderen Feldnamen als die Liste daneben, ein Lookup greift auf ein falsches Datenbankfeld zu, eine Beziehung wird in Tab A korrekt aufgelöst, in Tab B aber vergessen. Der Entwickler verbringt Zeit damit, die Ergebnisse des LLMs zu korrigieren und zu harmonisieren. Das sind die Trial-and-Error-Schleifen des Vibe-Codings.
Die Erkenntnis, die uns zu FASTCODE geführt hat: Wenn achtzig Prozent einer datengetriebenen Applikation aus immer denselben Mustern bestehen – CRUD-Formulare, relationale Navigation, Filterlogik, Lookup-Auflösung –, dann sollte man diese achtzig Prozent nicht bei jedem Projekt mittels KI neu generieren, sondern sie einmal korrekt in einen Generator gießen. Der Generator selbst ist teilweise über Vibe-Coding entstanden. Er liest das Datenbankschema, versteht die Beziehungen zwischen Tabellen und erzeugt daraus deterministische, typsichere Applikationsseiten. Was er erzeugt, ist korrekt – nicht nur probabilistisch plausibel, sondern strukturell garantiert.
Dabei verschwindet das Vibe-Coding nicht, es verschiebt sich auf eine produktivere Ebene. Statt mit dem LLM über die korrekte Verdrahtung eines Lookup-Feldes zu diskutieren, arbeiten wir gemeinsam an der Architektur des Generators selbst, an komplexer Businesslogik oder an Integrationen mit externen Systemen wie Shopify. Das LLM wird dort eingesetzt, wo seine Stärken liegen: bei kreativen, lokal begrenzten Aufgaben. Der Generator stellt währenddessen die globale Konsistenz sicher, an der das LLM regelmäßig scheitert.
Die Motivation hinter diesem Ansatz ist nicht neu. Vor dreißig Jahren ermöglichten Werkzeuge wie PowerBuilder, Gupta SQL/Windows oder Microsoft Access das Erstellen datengetriebener Anwendungen in wenigen Tagen. Diese Werkzeuge sind nahezu verschwunden, aber die Lücke, die sie hinterlassen haben, wurde nie ganz geschlossen. Auf der einen Seite stehen moderne Frameworks wie React und Next.js, die enorme Flexibilität bieten, aber für eine interne CRUD-Applikation einiges an Entwicklungszeit erfordern. Auf der anderen Seite stehen Spreadsheet-Lösungen und „Smart Tables“ wie Airtable oder NocoDB, die in kurzer Zeit eine Lösung bieten, aber bei umfangreicheren relationalen Anforderungen Schwächen zeigen. Außerdem zeigt sich oft eine Art Schatten-IT: Business-Abteilungen, die nicht auf die IT warten können und ihre Prozesse in immer komplexer werdenden Smart Tables oder sogar Excel-Dateien abbilden. Diese sind schlecht auditierbar und ruhen oft auf den Schultern einer einzigen Person.
Der FASTCODE-Generator soll diese Lücke schließen. Er erzeugt zum Beispiel:
- Zu-1-Beziehungen werden automatisch als durchsuchbare Lookup-Felder dargestellt.
- Zu-n-Beziehungen als eingebettete Tabs mit eigenen Listen.
- Eine komfortable Zuordnungs-UI für n:m-Beziehungen.
- Cascading Lookups, bei denen die Auswahl in einem Feld die verfügbaren Optionen in einem anderen filtert.
Für die verbleibenden zwanzig Prozent, die über generische Muster hinausgehen, bietet der Generator ein typsicheres Extension-System: Entwickler schreiben sogenannten Code-Beside – separate TypeScript-Dateien, die neben dem generierten Code liegen und über typisierte Extension Points eingebunden werden. Der generierte Code bleibt unangetastet und kann jederzeit neu generiert werden, ohne handgeschriebene Logik zu überschreiben.
Die Pipeline: Vom Schema zur Applikation
PostgreSQL-Schema
→ PostGraphile 5 → GraphQL-API (Queries, Mutations, Filter, Sortierung)
→ GraphQL-Codegen → Typisierte TypeScript-Definitionen (graphql.ts)
→ FASTCODE-Generator (Schema-Registry + TSX-Templates)
→ Vollständige React-Applikation (Listen, Formulare, Tabs, Lookups, Navigation)
Den Ausgangspunkt bildet ein PostgreSQL-Schema. Den ersten Schritt übernimmt PostGraphile 5. Dies ist ein Open-Source-Werkzeug, das sich direkt mit einer PostgreSQL-Datenbank verbindet, deren Struktur liest und daraus vollautomatisch eine vollwertige GraphQL-Schnittstelle bereitstellt. Es erzeugt sämtliche Queries, Mutations, Filter- und Sortieroperatoren für jede Tabelle und View – ohne eine einzige Zeile manuellen Resolver-Code. Man erhält für jede Tabelle typisierte Queries zum Lesen mit Paginierung, Filterung und Sortierung, Mutations zum Anlegen, Ändern und Löschen sowie navigierbare Felder entlang aller Fremdschlüsselbeziehungen. PostGraphile kompiliert dabei selbst tief verschachtelte GraphQL-Queries in ein einziges, optimiertes SQL-Statement. Das N+1-Query-Problem, das bei manuell geschriebenen APIs häufig auftritt, existiert hier schlicht nicht.
Der GraphQL-Codegenerator erzeugt aus dem GraphQL-Schema typisierte TypeScript-Definitionen. Ein Auszug zeigt, wie die Typen einer Entität aussehen:
export type Offering = Node & {
offeringId: Scalars['Int']['output'];
price?: Maybe<Scalars['BigFloat']['output']>;
productDefinitionByProductDefId?: Maybe<ProductDefinition>;
materialByMaterialId?: Maybe<Material>;
formByFormId?: Maybe<Form>;
offeringImagesByOfferingId: OfferingImageConnection;
// ...
};
Die Relationen sind bereits im Typ enthalten: So ist productDefinitionByProductDefId beispielsweise kein separater API-Aufruf, sondern ein navigierbares Feld, das PostGraphile in denselben SQL-Join auflöst. Analog existieren für jede Entität typisierte Mutations:
export type Mutation = {
createOffering?: Maybe<CreateOfferingPayload>;
updateOffering?: Maybe<UpdateOfferingPayload>;
deleteOffering?: Maybe<DeleteOfferingPayload>;
// ... für jede weitere Entität
};
Der FASTCODE-Generator konsumiert diese Typen und verschmilzt sie mit der introspektierten Datenbankstruktur zu einer Schema-Registry. Dies ist eine zentrale Datenstruktur, die für jede Tabelle, jede Spalte und jede Beziehung exakt dokumentiert, wie das Datenbankfeld heißt, welchem GraphQL-Typ es entspricht, ob es nullable ist, welche Fremdschlüssel es besitzt und welche inversen Beziehungen darauf verweisen. Diese Registry ist die Single Source of Truth des Generators, durch die er fehlerhafte Konfigurationen bereits zur Build-Zeit aufdeckt.
Aus der Registry erzeugen TSX-Templates den vollständigen Frontend-Code. Für jede Entität entstehen so je eine Liste, ein Formular, die zugehörige Navigation und die GraphQL-Queries. Das Datenladen und Caching übernimmt TanStack Query, das die generierten GraphQL-Aufrufe mit automatischem Cache-Management, Hintergrund-Refetching und optimistischen Updates verbindet.
Nach dem Pareto-Prinzip sind achtzig Prozent der Anforderungen einer datengetriebenen Applikation damit abgedeckt.
Die verbleibenden zwanzig Prozent
Diese verbleibende Funktionalität ist ein exzellenter Kandidat für die Kombination aus deterministischem Generator und Vibe-Coding.
3. Use Case: Ein Produktinformationsmanagement (PIM)
Um die Zusammenarbeit zwischen Generator und Vibe-Coding greifbar zu machen, beschreiben wir das damit erstellte System. Aus fachlicher Business-Sicht dient es der zentralen Verwaltung und dem Verkauf eines spezialisierten Produktsortiments aus dem Edelstein-, Natur- und Energiebereich. Das Produktinformationssystem (PIM) bildet dabei die Datenbasis für den angebundenen Onlineshop. Wer sich ansehen möchte, wie der resultierende Shop in der Praxis aussieht, findet das System unter www.energiemanufaktur.at.
Das Domänenmodell
Das Datenmodell umfasst rund dreißig Tabellen und bildet die vollständige Lieferkette ab: von den Lieferanten und ihren Angeboten über abstrakte Produktdefinitionen bis hin zu einer mehrstufigen Kategorisierung in Produktkategorien, Produkttypen und Hauptkategorien. Hinzu kommen materialbezogene Dimensionen, die später im Shopify-Shop als filterbare Metadaten dienen.
Wholesaler ──1:n──→ Offering ──n:1──→ ProductDefinition
│
├──n:m──→ ProductCategory ──n:m──→ ProductType ──n:m──→ MainCategory
│
├──n:1──→ Material ──→ Chakras, HealingProperties, EmotionalEffects
│
└──n:1──→ Shape (Kugel, Herz, Obelisk, ...)
Ein Lieferant (Wholesaler) bietet Angebote (Offerings) an, welche die konkreten Produkte mit Preisen, Gewichten und Verpackungseinheiten repräsentieren. Jedes Angebot verweist auf eine Produktdefinition, die das Produkt unabhängig vom Lieferanten beschreibt. Die Produktdefinition ist der zentrale Knotenpunkt: Von ihr aus verzweigen sich die Kategorisierungspfade über Produktkategorien und Produkttypen bis zu den Hauptkategorien, und die materialbezogenen Dimensionen wie Chakren und Wirkungen werden über das zugeordnete Material aufgelöst.
Die generierte Applikation
FASTCODE erzeugt aus diesem Modell eine vollständige Verwaltungsoberfläche, ohne dass wir eine einzige Zeile UI-Code für die Grundfunktionalität schreiben mussten. Listen mit konfigurierbaren Filtern, Detailformulare mit automatisch aufgelösten Lookups, Tabs für abhängige Entitäten, Cascading Lookups und komfortable Zuordnungs-UIs für n:m-Beziehungen – all das entsteht deterministisch aus dem Schema.
┌─────────────────────────────────────────────────────┐
│ Offering: Amethystkugel 50mm (Lieferant: XY) │
│─────────────────────────────────────────────────────│
│ Produktdefinition: [Amethystkugel poliert ▾] │ ← Lookup (durchsuchbar)
│ Material: [Amethyst ▾] │ ← Lookup (aus ProductDef)
│ Form: [Kugel ▾] │ ← Lookup (aus ProductDef)
│ Preis: [12.50] Gewicht:[85g] │
│─────────────────────────────────────────────────────│
│ [Bilder] [Attribute][Preishistorie] │ ← Tabs (zu-n-Beziehungen)
│ ┌──────────────────────────────────────────────┐ │
│ │ bild_001.jpg | bild_002.jpg | + Neu │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Dort, wo die generierten Masken nicht ausreichen, greifen typsichere Extensions.
Bereits bei der Entwicklung dieser Extensions zeigte sich ein typisches Vibe-Coding-Muster: Das LLM erzeugte redundante TypeScript-Interfaces, obwohl ein strukturell identisches bereits existierte. Zwei Typen für dasselbe Konzept entstanden und liefen auseinander. Ein klassisches Beispiel für mangelnde Typ-Unifikation.
4. Use Case: Shopify-Sync – Multidimensionale Produktwelt
Während das PIM die interne Verwaltung abdeckt – Lieferanten, Angebote, Produktdefinitionen und ihre Kategorisierung –, läuft der gesamte Verkauf über einen Shopify-Shop. Die Brücke zwischen beiden Welten ist ein automatischer Synchronisationsprozess, der die Produkte mitsamt ihrer multidimensionalen Kategorisierung nach Shopify überträgt. Und genau bei diesem Sync hat uns Vibe-Coding sowohl enorm geholfen als auch seine Grenzen gezeigt.
Die Sync-Pipeline im Überblick
Der Sync durchläuft drei Phasen in fester Reihenfolge, wobei jede Phase Ergebnisse produziert, die die nächste konsumiert:
Phase 1: Image Sync
Lokale Produktbilder → Shopify CDN
Ergebnis: Ein Index, der jeden lokalen Bildpfad seiner CDN-URL zuordnet
Phase 2: Product Sync
PIM-Offerings → Dimensionsauflösung → Shopify-Produkte mit Metafields
Nutzt den Bild-Index aus Phase 1, um Produktbilder zuzuordnen
Delta-Sync: Nur geänderte Produkte werden synchronisiert (Fingerprint-Vergleich)
Phase 3: Shopify-Collection Sync
Synchronisierte Produkte → Bottom-Up Collection-Generierung
Collection-Bilder über fal.ai generieren und hochladen
Navigationsstruktur als JSON-Metafields auf den Collections speichern
Die Produkt- und Bild-Synchronisationen berechnen Fingerprints aus den relevanten Produktfeldern beziehungsweise dem Bild und synchronisieren nur die Deltas. Phase 3 erzeugt aus den vorhandenen Produkten Shopify-Collections.
Flaches Shopify, tiefes Modell
Shopify bietet von Haus aus eine flache Produktstruktur: Titel, Typ, Tags. Unser Domänenmodell arbeitet dagegen mit mehreren unabhängigen Dimensionen, die ein Produkt gleichzeitig beschreiben:
| Dimension | Beispiele | Metafield-Typ |
|---|---|---|
| Hauptkategorie | Edelsteine, Energiewerkzeuge, Kosmetik | Liste |
| Produkttyp | Handstein, Pendel, Halskette, Creme | Liste |
| Essenz/Material | Amethyst, Rosenquarz, Lavendel | Text |
| Chakren | Herz, Stirn, Wurzel | Liste |
| Heilwirkungen | Immunsystem, Schlaf, Gelassenheit | Liste |
| Form | Kugel, Herz, Obelisk | Text |
Diese Dimensionen werden als sogenannte Shopify-Metafields synchronisiert und bilden die Grundlage für die gesamte Shop-Navigation und Filterung.
ProductDefinition
├──n:m──→ ProductCategory
│ └──n:m──→ ProductType
│ └──n:m──→ MainCategory → Metafield "mainCategories"
│ └─────────────────────→ Metafield "productTypes"
├──n:1──→ Material ─────────────────────────────────→ Metafield "essenceName"
│ ├──→ Chakras ────────────────────────→ Metafield "chakras"
│ └──→ HealingProperties ─────────────→ Metafield "healingProperties"
└──n:1──→ Form ─────────────────────────────────────→ Metafield "shape"
Ein Produkt kann dadurch in mehreren Hauptkategorien und unter mehreren Produkttypen erscheinen.
Die eigentliche Synchronisation läuft über Shopifys GraphQL-API
Collection-Generierung
Shopify organisiert die Produkte in so genannten Collections, aus denen im Shopify-Store-Frontend Produktlistenseiten gerendert werden.
Wir generieren die Collections aus den tatsächlich vorhandenen Produkten und ihren "Dimensionen". Eine deklarative Konfiguration beschreibt, welche Dimensionskombinationen als Collections existieren sollen:
export const COLLECTION_CONFIG: DimensionKey[][] = [["mainCategories"], // "Edelsteine"
["mainCategories", "productTypes"], // "Edelsteine > Handstein"
["mainCategories", "chakras"], // "Edelsteine > Herzchakra"
["mainCategories", "essenceName"], // "Edelsteine > Amethyst"
];
Der Synchronisation durchläuft alle synchronisierten Produkte, extrahiert ihre Dimensionswerte und erzeugt nur diejenigen Collections, für die tatsächlich Produkte existieren. Die resultierende Navigationsstruktur lässt sich mit einem OLAP-Cube vergleichen: Der Benutzer navigiert per Drill-Down von der Hauptkategorie in die Tiefe und kann auf jeder Ebene die Betrachtungsdimension wechseln:
Shop (Root)
└── Edelsteine
├──[Tab: Produkttyp] Handsteine | Anhänger | Halsketten | ...
├──[Tab: Material] Amethyst | Rosenquarz | Bergkristall | ...
└──[Tab: Chakra] Herzchakra | Stirnchakra | ...
Auf den ersten beiden Ebenen zeigt der Shop Navigationskacheln mit generierten Bildern; ab der dritten Ebene erscheint eine Produktliste mit facettierter Suche.
Bildgenerierung mit fal.ai - KI abseits von Vibe-Coding
Vibe-Coding ist nicht die einzige Stelle, an der KI in diesem Projekt Zeit spart. Jede Shopify-Collection benötigt ein repräsentatives Bild. Bei Dutzenden Collections wäre die manuelle Zuordnung ein hoher Aufwand: Bilder suchen oder erstellen, zuschneiden, hochladen, zuordnen. Stattdessen generieren wir die Bilder vollautomatisch über fal.ai mit dem Flux-Modell – zu einem Bruchteil der Kosten und der Zeit. Ein Prompt-Builder erzeugt kontextabhängige Bildbeschreibungen auf Basis der jeweiligen Dimensionskombination. Der Aufruf selbst ist kompakt:
const result = await fal.subscribe("fal-ai/flux/schnell", {
input: {
prompt: prompt,
negative_prompt: negativePrompt,
image_size: "landscape_16_9",
num_inference_steps: 4,
},
});
const imageUrl = result.images[0].url;
Vibe-Coding-Learnings
Gerade beim Shopify-Sync traten zwei besonders lehrreiche Fehlerklassen zutage. Das LLM generierte wiederholt GraphQL-Mutations in der API-Syntax einer älteren Shopify-Version, obwohl wir explizit die aktuelle API spezifiziert hatten. Ältere Versionen kommen in den Trainingsdaten schlicht häufiger vor, und das probabilistische „nächstes Token“ greift bevorzugt darauf zu. Außerdem verschwanden bei Refactorings regelmäßig Log- und Trace-Statements, die für das Debugging der Sync-Pipeline essenziell waren. Das LLM betrachtet sie als nicht-funktionalen Code und entfernt sie „aufräumend“, ohne zu erfassen, dass ein Sync-Prozess, der Hunderte von Produkten verarbeitet, ohne detailliertes Logging schlicht nicht wartbar ist.
5. Der deterministische Rahmen: Warum Generatoren und LLMs komplementär sind
Die Erfahrungen aus dem PIM und dem Shopify-Sync verdichten sich zu einem Muster, das über das einzelne Projekt hinausweist. Es gibt Aufgaben, bei denen ein LLM zuverlässig gute Ergebnisse liefert, und es gibt Aufgaben, bei denen es in einen Trial-and-Error-Loop führt. Die Trennlinie verläuft nicht zwischen „einfach“ und „komplex“, sondern zwischen lokal und global.
Lokal begrenzte Aufgaben – eine Funktion implementieren, einen Algorithmus entwerfen, eine Extension schreiben, einen Prompt formulieren – gelingen mit Vibe-Coding hervorragend. Das LLM hat den relevanten Kontext vollständig im Blick, kann kreativ und effizient arbeiten und liefert Ergebnisse, die ein Entwickler oft nur noch marginal anpassen muss. Die gesamte Businesslogik unseres PIM, die Shopify-Integration, die Bildgenerierung, die Mapping-Ketten: All das ist mit substanzieller LLM-Unterstützung entstanden, schneller als es ohne möglich gewesen wäre.
Global konsistente Aufgaben hingegen – z. B. sicherzustellen, dass Dutzende Tabellen einheitlich aufgelöst werden, dass Schichtentrennung über viele Dateien eingehalten wird, dass Typdeklarationen nicht dupliziert werden – können das LLM überfordern. Der Grund liegt nicht in mangelnder Leistungsfähigkeit, sondern in der Architektur: Das Kontextfenster ist endlich, und das Optimierungsziel ist das nächste Token, nicht die Systemkohärenz.
Der FASTCODE-Generator adressiert exakt diese Schwäche, indem er essenzielle Fehlerquellen strukturell ausschließt. Die Schichtentrennung ist in den Templates unverrückbar verankert und kann nicht unterlaufen werden. Durch die strikte Bindung an das extrahierte GraphQL- und Datenbank-Schema garantiert er, dass keine fehlerhaften Typen, Properties oder Relationen verwendet werden. Die meisten Fehler werden zur Build-Time erkannt.
Was übrig bleibt, ist genau der Bereich, in dem das LLM seine Stärken entfaltet. Die Mapping-Kette zwischen PIM und Shopify etwa wurde iterativ im Dialog mit dem LLM entwickelt: Wir beschrieben das fachliche Problem, das LLM schlug Architekturen vor, wir evaluierten und verfeinerten. Wenn das LLM dabei eine falsche Richtung einschlug, blieb der Schaden begrenzt, weil der deterministische Rahmen die Grenzen setzte. Eine fehlerhafte Mapping-Funktion kann nicht die Schichtentrennung der gesamten Applikation stören, weil diese im FASTCODE-Generator verankert ist, nicht im handgeschriebenen Code.
Der Entwickler bleibt dabei durchgehend in der Rolle des Architekten und Piloten. Er delegiert Teilaufgaben an das LLM, behält aber das Gesamtbild und die Entscheidungshoheit. Das ist kein Rückschritt gegenüber dem Versprechen des autonomen Vibe-Codings – es ist die realistische Variante, die in der Praxis funktioniert.
6. Klassen von KI-Herausforderungen
Die folgenden neun Klassen sind während der Erstellung von FASTCODE, dem PIM und dem Shopify-Sync wiederholt aufgetreten. Sie sind keine zufälligen Bugs und auch nicht modellspezifisch; wir haben sie mit verschiedenen LLMs beobachtet. Wir ordnen sie nach der Art des zugrundeliegenden Versagens ein und skizzieren, warum sie aus der Architektur probabilistischer Systeme heraus entstehen.
Klasse 1: Verletzung struktureller Invarianten (Architektur-Mix)
In unserem PIM gibt es Operationen, die serverseitig in einer Datenbanktransaktion ablaufen müssen: etwa ein kaskadierendes Löschen, bei dem vor der eigentlichen Löschung alle abhängigen Datensätze analysiert werden, oder ein „Copy & Link“, bei dem ein Datensatz dupliziert und gleichzeitig mit dem Original verknüpft wird. Beide Operationen sind als Backend-Endpoints implementiert, weil sie Transaktionssicherheit und atomares Verhalten erfordern. Das LLM tendierte jedoch dazu, diese Logik direkt im Frontend-Code zu implementieren, mit einzelnen, nicht-transaktionalen API-Aufrufen. Es geht hier weniger um diese konkreten Beispiele als um das zugrundeliegende Muster: Das LLM optimiert auf die funktionale Korrektheit des erzeugten Codes, ohne die architektonischen Invarianten des Systems zu berücksichtigen. Die Schichtentrennung ist eine Eigenschaft des Gesamtsystems, nicht des einzelnen Codeblocks, und genau das macht sie für ein tokenbasiertes Modell schwer greifbar.
Klasse 2: Mangelnde Typ-Unifikation (Redundante Interfaces)
Das LLM erzeugt ein neues Interface, obwohl ein strukturell identisches bereits existiert. Beide Typen laufen parallel, müssen synchron gehalten werden und divergieren in Folge. Das LLM scheitert hier an der Strukturidentität: Es erkennt nicht, dass zwei syntaktisch leicht unterschiedliche Deklarationen semantisch denselben Typ repräsentieren. Es fehlt ein Kanonisierungsschritt, der vor der Codegenerierung prüft, ob ein äquivalenter Typ bereits in der Codebasis vorhanden ist.
Klasse 3: Semantische Flucht durch Type-Casting (Any-Laziness)
Statt die korrekte generische Typisierung über den gesamten Ableitungsbaum sicherzustellen, castet das LLM bei komplexeren Typen (Generics, Union Types, Indexed Types) teilweise auf any. Der Compiler schweigt, der Code kompiliert, aber die Typsicherheit ist an dieser Stelle aufgehoben und Fehler durch neuere Framework-Versionen werden erst zur Laufzeit sichtbar. Das LLM wählt den Weg des geringsten Widerstands: Ein any-Cast löst den lokalen Typenkonflikt, während die korrekte Lösung unter Umständen mehrere Dateien und Typ-Parameter umfasst. Das Ergebnis ist eine Einschränkung der globalen Typkorrektheit zugunsten lokaler Konsistenz.
Klasse 4: Zeitliche Inkonsistenz (Version-Mismatch)
Das LLM generiert Code für eine veraltete Library-Version, obwohl im System-Prompt explizit die aktuelle Version spezifiziert wurde. Beim Shopify-Sync äußerte sich das in GraphQL-Mutations mit der Feldbenennung der 2024er-API, während wir die 2025er-Version einsetzten. Die Ursache ist statistischer Natur: Ältere API-Versionen kommen in den Trainingsdaten häufiger vor, und das probabilistische Sprachmodell greift bevorzugt auf das häufigere Muster zu. Es fehlt ein Mechanismus, der veraltete Fakten explizit aus der Inferenz ausschließt.
Klasse 5: Fehlende Normalisierung (Code-Duplikation)
Das LLM implementiert eine neue Hilfsfunktion, obwohl eine mit identischer Logik bereits existiert. Es erkennt nicht, dass eine Teilaufgabe bereits durch eine bestehende Funktion gelöst ist, da es auf Token-Ähnlichkeit basiert und nicht auf funktionale Äquivalenz prüfen kann. Zwei Funktionen können unterschiedliche Namen tragen und andere Code-Tokens enthalten und trotzdem semantisch identisch sein. Diese Erkenntnis liegt außerhalb dessen, was ein rein tokenbasiertes Modell oder ein RAG mit semantischer Suche zuverlässig leisten kann.
Klasse 6: Komplexitätsblindheit (O(n*m) statt O(n+m))
Beim Zuordnen von Produkten zu Collections iterierte der vom LLM erzeugte Code über alle Produkte und prüfte für jedes einzelne alle Collections auf Übereinstimmung – eine verschachtelte Schleife mit O(n*m). Ein vorberechneter Index hätte das Problem in linearer Zeit gelöst. Das LLM optimiert auf das nächste Token, nicht auf algorithmische Komplexität. Der Code war korrekt, aber seine Laufzeit skalierte schlecht.
Klasse 7: Willkürliche Löschung (Nicht-monotones Verhalten)
Bei Refactorings verschwinden Log-Statements, Debug-Ausgaben und Tracing-Code. Das LLM betrachtet sie als nicht-funktional und entfernt sie stillschweigend. Es verfügt über kein Modell für die Unterscheidung zwischen essenziellen und akzidentiellen Codezeilen. Logging ist nicht funktional im Sinne der Geschäftslogik, aber essenziell für die Wartbarkeit. Ein Synchronisationsprozess, der Hunderte von Produkten mit einer externen API abgleicht, ist ohne detailliertes Logging schlecht debuggbar. Eine formale Spezifikation müsste festlegen, welche Code-Eigenschaften bei einer Transformation erhalten bleiben müssen, eine Art Post-Conditions für Refactorings.
Klasse 8: Semantischer Kontextverlust (Kurzzeitgedächtnis)
Das LLM „vergisst“ zentrale Definitionen, die es zuvor in der gleichen Sitzung noch korrekt verwendet hat, und erzeugt eine neue, inkompatible Datenstruktur. Der „Kontext“ eines LLM ist kein symbolischer State, sondern ein gleitendes Fenster von Wahrscheinlichkeiten ohne echtes Persistenzmodell. Gebraucht wäre eine Single Source of Truth, die während der gesamten Sitzung als Referenz dient und gegen die jeder Generierungsschritt validiert wird – vergleichbar einem Proof Assistant in der formalen Verifikation.
Klasse 9: Fehlende Guards (Null/Undefined-Logik)
Das LLM generiert Code, der auf potenziell leere Werte zugreift, ohne sie vorher zu prüfen. Der Standardfall funktioniert, der Grenzfall führt zum Absturz. Die notwendigen Sicherheitsprüfungen fehlen, da sie in den Trainingsdaten statistisch seltener vorkommen als der erfolgreiche Codepfad. Ein formaler Ansatz würde solchen Code sofort als nicht beweisbar korrekt ablehnen, weil die Vorbedingung nicht sichergestellt ist.
7. Leitschienen für probabilistische Systeme: Vom Pragmatischen zum Formalen
Die neun Fehlerklassen aus dem vorangegangenen Kapitel lassen sich adressieren, allerdings nicht durch bessere Prompts oder größere Kontextfenster allein, sondern durch Werkzeuge und Methoden, die auf unterschiedlichen Ebenen ansetzen. Wir skizzieren drei solcher Ebenen, aufsteigend nach Tiefe und Ambition.
Innerhalb der LLM-Welt: Kontextanreicherung und Agentic Tool-Use
Die nächstliegende Ebene arbeitet mit den Mitteln, die das LLM-Ökosystem selbst bereitstellt. Im ersten Teil dieser Serie („Wenn der Vibe verfliegt“) haben wir die Mechanismen ausführlich beschrieben: die vier Schichten der Kontextbereitstellung (lokaler Index, semantischer Index, RAG, LLM), das Model Context Protocol (MCP) als strukturierte Schnittstelle zu kuratiertem Wissen, Prompt-Caching als ökonomischer Enabler und den Generate-Validate-Zyklus mit AST-Integration.
Zusammengefasst: MCP erlaubt es, einem LLM Architekturvorgaben, Qualitätsregeln und Codebasis-Wissen über dedizierte Server bereitzustellen. RAG reichert den Kontext mit relevanten Informationen aus externen Quellen an. Beide Mechanismen sind komplementär – MCP definiert das Protokoll, RAG beschreibt das Pattern – und in beiden Fällen landet das Ergebnis im selben Kontextfenster des LLM.
Entscheidend wird diese Kontextanreicherung aber erst durch Agentic Tool-Use: Das LLM erkennt, welche Information ihm fehlt, und fordert sie aktiv an. Es stellt fest, dass ihm die aktuelle API-Dokumentation fehlt, und ruft sie gezielt ab, statt dass der Client vorab „raten“ muss. Dieser Unterschied – zwischen dem Modell Informationen passiv bereitzustellen und einem Modell, das sich holt, was es braucht – macht den Kontextabruf erheblich präziser und adressiert direkt die Klassen 4 (Version-Mismatch) und 5 (Code-Duplikation).
Diese Ansätze sind pragmatisch und heute bereits umsetzbar. Sie mildern die Symptome, adressieren aber die Wurzel noch nicht: Das LLM bleibt ein probabilistisches System, das keine strukturellen Garantien geben kann. Es kann den MCP-Server ignorieren, es kann das RAG-Ergebnis falsch interpretieren. Die Fehlerrate sinkt, aber die Fehlerklassen verschwinden nicht.
Deterministische Integration: Language-Server und AST-Strukturen
Eine tiefere Ebene setzt an der Schnittstelle zwischen LLM und den deterministischen Werkzeugen an, die in jeder modernen IDE bereits vorhanden sind: Language-Server, Compiler, Typ-Checker. Heute arbeiten diese Werkzeuge nachgelagert – sie analysieren den Code, den das LLM bereits erzeugt hat, und melden Fehler im Nachhinein.
Vorreiter wie Cursor oder RooCode gehen hier bereits erste, vielversprechende Schritte, indem sie Language-Server-Diagnosen aktiv in automatisierte Validierungsschleifen (Generate-Validate) einbinden. Sie füttern Linter-Fehler und Compiler-Warnungen direkt an das Modell zurück, sodass das LLM seinen generierten Code anhand dieses deterministischen Feedbacks iterativ selbst korrigiert, bevor der Entwickler eingreifen muss.
Der entscheidende Schritt wäre, diese deterministischen Strukturen in den Generierungsprozess selbst einzubinden. Wenn das LLM während der Codeerzeugung Zugriff auf den Abstract Syntax Tree (AST) der bestehenden Codebasis hätte, könnte es strukturelle Fragen beantworten, bevor es Code schreibt: Existiert dieser Typ bereits? Ist dieses Feld nullable? Welche Imports sind an dieser Stelle gültig? Die Typinferenz des Compilers könnte als Constraint in die Generierung einfließen, statt erst danach als Fehlermeldung aufzutauchen.
Das unterscheidet sich von statischen Analysewerkzeugen oder „Code-Tomografen“, die auf dem bereits erstellten Code operieren. Diese Werkzeuge sind wertvoll, aber sie arbeiten prinzipbedingt zu spät: Der Code ist bereits geschrieben, die architektonische Entscheidung bereits getroffen. Was wir brauchen, ist keine bessere Diagnose, sondern eine Prävention – deterministische Leitschienen, die den Raum der möglichen Generierungsergebnisse vor der Erzeugung einschränken, nicht danach. Hier liegt eine natürliche Schnittstelle zwischen der KI-Forschung und dem Compilerbau.
Formale Methoden: Von Generate-then-Check zu symbolisch gelenkter Generierung
Die dritte Ebene geht über die praktische Softwareentwicklung hinaus und berührt Grundfragen der Informatik. Die Fehlerklassen 6 (Komplexitätsblindheit) und 7 (Willkürliche Löschung) lassen sich weder durch bessere Kontextbereitstellung noch durch AST-Integration vollständig lösen. Sie erfordern ein System, das über den erzeugten Code formal schlussfolgern kann: Ist dieser Algorithmus äquivalent zum vorherigen, nur effizienter? Bleiben bei dieser Transformation alle Invarianten erhalten?
Ein konkretes Beispiel dafür, wie weit formale Verifikation in Kombination mit LLMs bereits reicht, liefert Harmonic Aristotle (aristotle.harmonic.fun), ein KI-Agent für formale mathematische Beweise. Das Prinzip: Das LLM generiert Beweisschritte, der Lean-Compiler verifiziert sie formal. Was nicht beweisbar ist, wird verworfen und erneut versucht.
Aristotle zeigt eindrucksvoll, dass die Kombination von probabilistischer Generierung und formaler Verifikation funktioniert, aber es zeigt auch die Grenzen des heutigen Ansatzes. Das System arbeitet heuristisch im Generate-then-Check-Modus: Es probiert, prüft, verwirft und probiert erneut. Jeder Fehlversuch kostet Rechenzeit. Und es funktioniert bisher nur für Mathematik, nicht für Software-Architektur.
Die eigentliche Vision geht einen Schritt weiter. Die Forschung zu Symbolic Computation, Computeralgebra und automatischem Beweisen beschäftigt sich seit Jahrzehnten mit Methoden, die Lösungsräume vor der Suche systematisch einschränken. Prof. Bruno Buchberger, der Begründer der Gröbner-Basen-Theorie, verfolgt mit seinem Theorema-System am RISC-Institut der JKU Linz den Ansatz, mathematisches Schlussfolgern zu automatisieren – nicht als nachgelagerte Prüfung, sondern als konstruktiven Prozess. Der Kern der Gröbner-Basen-Methode – ein Gleichungssystem so umzuformen, dass sich die Lösungen systematisch ablesen lassen, statt sie durch Ausprobieren zu finden – ist exakt das Prinzip, das auf Code-Generierung übertragen werden könnte.
Statt heuristisch zu erzeugen und danach zu prüfen, könnten symbolische Methoden den Lösungsraum vor der Generierung so einschränken, dass das LLM von vornherein nur korrekte Pfade beschreitet. Ein symbolischer Reasoner würde nicht den fertigen Code validieren, sondern die Generierung selbst lenken.
Das wäre nicht nur eine qualitative Verbesserung, sondern auch eine enorme Ressourceneinsparung: Weniger Fehlversuche bedeuten weniger Tokens, weniger Inferenz-Aufrufe, weniger Stromverbrauch. In einer Branche, die zunehmend über den Energieverbrauch ihrer Rechenzentren diskutiert, ist das kein Nebenaspekt.
Europa – Quo Vadis?
Diese Überlegungen führen zu einer Standortfrage, die über das Technische hinausgeht. Das Rennen um die größten Sprachmodelle und die leistungsfähigsten Rechenzentren hat Europa aufgrund einer weniger ausgeprägten Venture-Kapital-Szene und regulatorischer Zurückhaltung wahrscheinlich weitgehend verloren. Die Frage ist, ob das bedeutet, dass wir nur noch Konsumenten der Modelle und Plattformen sind, die uns aus den USA und China vorgegeben werden.
Aber die neun Fehlerklassen zeigen: Rohe Modellleistung ist nicht alles. Die formale Verifikation, der Compilerbau, die Expertise in symbolischer Mathematik und automatischem Beweisen – das sind Disziplinen, in denen Europa über eine tiefe Tradition und aktive Forschungslandschaft verfügt. Wenn die Zukunft nicht nur im Skalieren von Parametern liegt, sondern in der Kombination von probabilistischen Modellen mit symbolischen, deterministischen Methoden, dann hat Europa Karten, die es spielen kann.
Dabei geht es auch um eine kulturelle Frage: den Meister-Lehrling-Meister-Kreislauf. Wenn wir als Entwickler zunehmend an LLMs delegieren, ohne die zugrundeliegenden Strukturen zu durchdringen, verlieren wir über die Zeit die Meisterschaft, die nötig ist, um die nächste Generation von Werkzeugen zu bauen. Der deterministische Rahmen, ob als Codegenerator, als AST-integriertes Typsystem oder als formaler Beweiser, ist auch ein Werkzeug gegen diesen schleichenden Kompetenzverlust: Er zwingt uns, die Architektur zu verstehen, bevor wir sie an ein probabilistisches System delegieren.
Literatur & Links
[1] PostGraphile: Open-Source-Engine zur automatischen Generierung hochperformanter GraphQL-APIs direkt aus PostgreSQL-Datenbankschemata.
🔗 https://graphile.org/postgraphile
[2] TanStack Query (ehemals React Query): Bibliothek für asynchrones State-Management, Caching und Daten-Synchronisation in Web-Applikationen.
🔗 https://tanstack.com/query
[3] fal.ai & Flux: Entwicklerplattform für generative KI-Inferenz, spezialisiert auf extrem schnelle Bild- und Mediengenerierung (u. a. Black Forest Labs „Flux“ Modelle).
🔗 https://fal.ai
[4] Model Context Protocol (MCP): Ein von Anthropic initiierter, offener Standard, der es KI-Modellen ermöglicht, standardisiert und sicher auf lokale oder externe Datenquellen und Werkzeuge zuzugreifen.
🔗 https://modelcontextprotocol.io
[5] Harmonic Aristotle: Ein KI-gestütztes System, das mathematische Theoreme generiert und deren Korrektheit über den formalen Beweisassistenten (Compiler) „Lean“ verifiziert.
🔗 https://aristotle.harmonic.fun
[6] Theorema Project (RISC, JKU Linz): Forschungsprojekt am Research Institute for Symbolic Computation (RISC) unter der Leitung von Prof. Dr. Bruno Buchberger zur Automatisierung des mathematischen Beweisens.
🔗 https://risc.jku.at/project/theorema