Warum KI-generierter Code ohne Tests eine tickende Zeitbombe ist
*Wie wir durch einen einzigen undefined-Wert gelernt haben, dass Vertrauen gut ist, aber Testen besser.*
Tools wie Claude Code, GitHub Copilot oder Cursor revolutionieren die Softwareentwicklung. Features, die früher Tage brauchten, entstehen in Minuten. Komplette CRUD-Systeme, Datenbank-Hooks, UI-Komponenten - alles wird schneller. Aber genau diese Geschwindigkeit hat eine Schattenseite: Wir vertrauen dem generierten Code zu sehr und deployen ihn ohne ausreichende Prüfung.
In diesem Artikel zeigen wir anhand eines realen Beispiels aus einem unserer Kundenprojekte, warum automatisierte Tests bei KI-gestützter Entwicklung nicht optional, sondern überlebenswichtig sind.
Der Bug, der nicht hätte passieren dürfen
In einem Sponsoring-Management-Tool (React, TypeScript, Firebase) haben wir mit Claude Code ein komplettes Aufgabensystem implementiert: Aufgaben erstellen, Mitarbeitern zuweisen, Fälligkeitsdaten setzen, Status tracken. Der Code sah sauber aus, der TypeScript-Compiler war zufrieden, der Build war grün.
Dann hat der Kunde die erste Aufgabe erstellt. Fehler.
Die Ursache war fast schon banal:
``
typescript
// So sah der Code aus
await addDoc(tasksRef, {
title: values.title,
assignedTo: values.assignedTo,
dueDate: values.dueDate ? Timestamp.fromDate(values.dueDate) : undefined,
relatedEntityType: values.relatedEntityType, // undefined wenn nicht gesetzt
relatedEntityId: values.relatedEntityId, // undefined wenn nicht gesetzt
createdAt: Timestamp.now(),
});
`
Firestore akzeptiert keine undefined-Werte. Punkt. Wenn ein Feld optional ist und der User es nicht ausfüllt, steht dort undefined - und Firestore wirft: "undefined is not a valid Firestore value".
TypeScript erkennt das Problem nicht, weil der Typ Timestamp | undefined` technisch korrekt ist. Der Build kompiliert fehlerfrei. Erst zur Laufzeit, wenn ein echter User das Formular abschickt, fliegt alles auseinander.Warum KI-Assistenten diesen Fehler immer wieder machen
Das Muster ist typisch für KI-generierten Code:
1. KI kennt die API-Dokumentation, aber nicht die Runtime-Eigenheiten.
Claude weiß, dass Firestore eine NoSQL-Datenbank ist. Aber die spezifische Eigenheit, dass
undefined in JavaScript-Objekten bei Firestore zu einem Fehler führt, während null oder das Weglassen des Feldes funktioniert - das ist eine Falle, die in keiner Typdefinition steht.
2. TypeScript gibt falsches Sicherheitsgefühl.
Der Code ist typkorrekt. dueDate?: Timestamp erlaubt undefined. TypeScript sagt: alles gut. Aber Firestore sagt zur Laufzeit: nein.
3. Der Happy Path funktioniert immer.
Wenn man beim Testen alle Felder ausfüllt, funktioniert alles. Der Bug tritt nur auf, wenn optionale Felder leer bleiben - also genau dann, wenn echte User die App benutzen.
4. KI optimiert für Geschwindigkeit, nicht für Robustheit.
Ein KI-Assistent liefert schnell funktionierenden Code. Aber "funktionierend" bedeutet in der Regel: kompiliert und sieht richtig aus. Ohne explizite Anweisung schreibt kein KI-Assistent von sich aus Tests.Der Fix ist simpel - aber er hätte automatisch gefunden werden müssen
Die Lösung ist eine Hilfsfunktion, die
undefined-Werte aus Objekten entfernt:
``typescript
function stripUndefined>(obj: T): Partial {
return Object.fromEntries(
Object.entries(obj).filter(([, v]) => v !== undefined)
) as Partial;
}
// Anwendung
const cleanData = stripUndefined(data);
await addDoc(tasksRef, { ...cleanData, createdAt: Timestamp.now() });
``
Drei Zeilen Code. Der Bug war in fünf Minuten gefixt. Aber der Kunde hatte bereits einen Fehler gesehen, Vertrauen verloren, und wir mussten erklären, warum eine Basisfunktionalität nicht funktioniert. Das hätte ein einziger Test verhindert.Das Testing-Setup: Vitest als Standard
Für React-Projekte mit Vite ist Vitest die natürliche Wahl. Es nutzt dieselbe Konfiguration wie Vite, ist extrem schnell und bietet eine Jest-kompatible API.
Installation:
``
bash
npm install --save-dev vitest @testing-library/react @testing-library/jest-dom jsdom
`
Konfiguration in vite.config.ts:
`typescript
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{ts,tsx}'],
},
});
``
Das gesamte Setup dauert keine zehn Minuten. Danach stehen alle Tools bereit, um den Code abzusichern.Was muss getestet werden? Die vier Kategorien
Nicht jede Zeile Code braucht einen Test. Aber es gibt vier Kategorien, die bei jedem Feature abgedeckt sein müssen:
1. Schema-Validierung (Zod, Yup)
Jedes Formular hat ein Validierungsschema. Tests stellen sicher, dass Pflichtfelder erzwungen, Grenzwerte eingehalten und ungültige Werte abgelehnt werden.
2. Utility-Funktionen und Berechnungslogik
Reine Funktionen ohne Seiteneffekte sind am einfachsten zu testen. Rabattberechnungen, Datumslogik, Formatierungen.
3. Datenaufbereitung für die Datenbank (der blinde Fleck)
Das ist die Kategorie, die fast niemand testet - und wo die meisten Bugs entstehen. Der Code zwischen Formular und Datenbank.
``
typescript
describe('Firestore-Datenaufbereitung', () => {
it('enthält keine undefined-Werte', () => {
const taskData = {
title: 'Test',
dueDate: undefined, // User hat kein Datum gewählt
};
const cleanData = stripUndefined(taskData);
expect(Object.values(cleanData)).not.toContain(undefined);
});
});
``
4. Zusammenspiel von Formular und Mutation
Wird der User-Name korrekt aufgelöst? Wird das Datum richtig konvertiert?Die CLAUDE.md als Guardrail für KI-Assistenten
Wenn KI-Assistenten den Code schreiben, müssen die Regeln dort stehen, wo die KI sie liest. Bei Claude Code ist das die CLAUDE.md im Projektroot.
Was gehört rein:
``
markdown
## Testing Strategy
### PFLICHT: Tests bei jedem neuen Feature
Bevor ein Feature als fertig gilt, MÜSSEN folgende Tests existieren:
1. Zod-Schema-Validierung - Jedes neue Schema testen
2. Utility-Funktionen - Jede reine Logik-Funktion
3. Firestore-Datenaufbereitung - NIEMALS undefined an Firestore
4. Formular-Logik - Transformation von Form-Werten zu DB-Daten
### Bekannte Firestore-Fallstricke
Firestore akzeptiert KEIN undefined. Immer stripUndefined() verwenden.
``
Der Effekt: Wenn Claude Code das nächste Feature implementiert, liest es diese Regeln und schreibt automatisch Tests dazu. Die KI wird zum eigenen Quality Gate.Der Workflow, der funktioniert
Nach einigen Iterationen hat sich bei uns folgender Workflow etabliert:
``
Feature-Anforderung
↓
Implementierung (KI schreibt Code)
↓
Tests schreiben (KI schreibt Tests, weil CLAUDE.md es vorschreibt)
↓
npm run test → Grün?
↓ Ja ↓ Nein
npm run build Bug fixen → zurück zu Tests
↓ Erfolgreich?
Deploy
``
Die Schlüsselstelle: Tests sind kein separater Schritt, der vergessen werden kann. Sie sind Teil der Definition of Done.
Konkrete Zahlen aus unserem Projekt:
- 39 Tests in drei Dateien, geschrieben in unter 15 Minuten
- Laufzeit: unter 2 Sekunden (Vitest ist schnell)
- Hätte verhindert: Den undefined-Bug, der den Kunden Vertrauen gekostet hatDie fünf wichtigsten Erkenntnisse
1. TypeScript ist nicht genug.
TypeScript prüft Typen zur Compile-Zeit. Aber Runtime-Verhalten (wie Firestores Ablehnung von undefined) wird nicht erkannt. Tests schließen diese Lücke.
2. Der Happy Path ist nicht genug.
Wenn alle Felder ausgefüllt sind, funktioniert alles. Die Bugs stecken in den optionalen Feldern, den leeren Eingaben, den Edge Cases.
3. KI braucht klare Regeln.
Ohne explizite Anweisung schreibt kein KI-Assistent Tests. Die CLAUDE.md muss klar definieren: Kein Feature ohne Tests.
4. Tests müssen schnell sein.
Wenn Tests 30 Sekunden dauern, werden sie übersprungen. Vitest läuft in unter 2 Sekunden. Es gibt keine Ausrede.
5. Testen, was schiefgehen kann, nicht was funktioniert.
Der wichtigste Test prüft nicht, ob eine Aufgabe erstellt werden kann. Er prüft, ob undefined-Werte korrekt entfernt werden.
Fazit: Vertrauen ist gut, Testen ist besser
KI-Assistenten machen uns produktiver. Aber Produktivität ohne Qualitätssicherung ist wie Autofahren ohne Bremsen: Es geht schnell, bis es knallt.
Die gute Nachricht:
- Testing-Setup: 10 Minuten
- CLAUDE.md-Anpassung: 5 Minuten
- Ab dann schreibt die KI die Tests automatisch mit
Der ROI:
Weniger Bugs in Produktion, weniger peinliche Kundengespräche, mehr Vertrauen in den eigenen Code.
Der Bug, der diesen Artikel inspiriert hat, war ein einziger undefined-Wert in einem Firestore-Dokument. Drei Zeilen Fix. Aber er hat uns eine wichtige Lektion gelehrt: Vertraue keinem Code - auch nicht dem eigenen, auch nicht dem KI-generierten - ohne Tests.