Wer "add user authentication" in Claude Code eingibt, bekommt jedes Mal ein anderes Ergebnis. Vielleicht JWT. Vielleicht Session-Cookies. Vielleicht ein vollstaendiger OAuth2-Flow mit Refresh Tokens und PKCE. Der Agent weiss nicht, was du willst, weil du es ihm nicht gesagt hast. Du hast eine Richtung angegeben, kein Ziel.
Die Entwickler, die ich kenne und die konstant auslieferbaren Output aus Claude Code bekommen, haben eine Gemeinsamkeit: Sie schreiben eine Spezifikation, bevor sie Arbeit an den Agent uebergeben. Keinen Roman. Kein Jira-Ticket mit drei Saetzen Kontext. Ein konkretes Dokument, das definiert, wie "fertig" aussieht, bevor jemand eine Zeile Code schreibt.
Das ist keine neue Erkenntnis. Spec-first-Entwicklung gibt es seit Jahrzehnten vor KI. Aber bei Agents sind die Kosten fuer das Ueberspringen der Spec hoeher und der Nutzen einer geschriebenen Spec groesser. Ein menschlicher Entwickler kann mittendrin stoppen und fragen "Moment, meinst du Passwort-Auth oder SSO?" Ein Agent waehlt stillschweigend eine Option und macht weiter. Bis du es merkst, hat er das Falsche gebaut, und du hast 20 Minuten damit verbracht, Code zu reviewen, der weggeworfen werden muss.
Dieser Artikel beschreibt den Spec-getriebenen Lebenszyklus, den ich taeglich mit Claude Code nutze: wie man Spezifikationen schreibt, gegen die Agents arbeiten koennen, den Plan-vor-Code-Checkpoint, der Missverstaendnisse frueh abfaengt, und ein Verifikationsprotokoll, das gruendlicher ist als "es kompiliert."
Warum "Einfach bauen" bei Agents scheitert
Schauen wir uns den Fehlermodus genau an. Wenn du Claude Code eine vage Anweisung gibst, gehen drei Dinge schief:
Stille Annahmen. Der Agent füllt jede Luecke in deiner Spec mit eigenen Annahmen. Manchmal sind diese Annahmen vernuenftig. Manchmal nicht. Du erfaehrst erst beim Lesen des Outputs, in welcher Kategorie du bist. Bei vagen Anweisungen liest du den Output gruendlicher, als du fuer das Schreiben einer Spec gebraucht haettest.
Nicht-reproduzierbare Ergebnisse. Fuehre denselben vagen Prompt zweimal aus und du bekommst zwei verschiedene Implementierungen. Nicht nur andere Variablennamen oder Formatierung. Andere Architekturentscheidungen. Andere Bibliotheken. Andere Fehlerbehandlungsstrategien. Wenn du den Output nicht reproduzieren kannst, kannst du keinen zuverlaessigen Prozess darum herum bauen.
Review wird zum Flaschenhals. Wenn der Agent alle Entscheidungen trifft, musst du alle Entscheidungen ueberpruefen. Ein 400-Zeilen-Diff, bei dem du jede Entscheidung verstehst, dauert 5 Minuten. Ein 400-Zeilen-Diff, bei dem der Agent das Datenbankschema, die API-Form, die Fehlercodes und die Validierungslogik gewaehlt hat, dauert 30 Minuten, weil du die Spec aus der Implementierung rekonstruierst.
Die Loesung sind nicht bessere Prompts. Es geht darum, die wichtigen Entscheidungen in ein Dokument vorzuverlagern, gegen das der Agent arbeiten kann.
Der Spec-getriebene Lebenszyklus
Der Workflow hat fuenf Phasen. Jede hat eine klare Eintrittsbedingung und eine klare Austrittsbedingung.
Phase 1: Brainstorming. Du erkundest den Problemraum. Was sind die Einschraenkungen? Welche Ansaetze gibt es? Was hast du vorher probiert? Hier denkst du laut nach, entweder allein oder mit Claude Code im Gespraechsmodus. Die Austrittsbedingung: Du hast einen bevorzugten Ansatz und verstehst die Kompromisse.
Phase 2: Review. Du testest den Ansatz unter Druck. Was koennte schiefgehen? Welche Randfaelle gibt es? Steht das im Konflikt mit etwas, das bereits in der Codebase ist? Wenn du mit mehreren Agents arbeitest, ist hier ein Architektur-Agent oder eine zweite Meinung wertvoll. Die Austrittsbedingung: Du bist sicher, dass der Ansatz tragfaehig ist.
Phase 3: Spec. Du schreibst auf, was du entschieden hast. Problembeschreibung, vorgeschlagener Ansatz, zu aendernde Dateien, mechanisch verifizierbare Akzeptanzkriterien und ein Testplan. Das ist der Vertrag. Die Austrittsbedingung: Jemand (Mensch oder Agent) koennte diese Spec lesen und genau wissen, was zu bauen ist und wie man es verifiziert.
Phase 4: Implementierung. Der Agent arbeitet gegen die Spec. Nicht gegen eine vage Idee. Gegen ein konkretes Dokument mit testbaren Kriterien. Die Austrittsbedingung: Der Agent meldet Fertigstellung und hat Verifikationsnachweise gepostet.
Phase 5: Verifikation. Du (oder ein QA-Agent) bestaetigst, dass die Implementierung mit der Spec uebereinstimmt. Nicht "sieht es richtig aus", sondern "erfuellt es jedes Akzeptanzkriterium." Die Austrittsbedingung: Jedes Kriterium ist geprueft, und die fehlgeschlagenen gehen zurueck zu Phase 4.
Die zentrale Erkenntnis: Die Phasen 1-3 sind guenstig. Sie dauern 10-20 Minuten fuer ein mittelgrosses Feature. Phase 4 dauert so lange wie die Implementierung braucht. Phase 5 dauert 5-10 Minuten. Das Ueberspringen der Phasen 1-3 spart nicht 10-20 Minuten. Es kostet die Zeit fuer Review, Debugging und Nacharbeit an Code, der in die falsche Richtung ging.
Wie eine gute Agent-Spec aussieht
Hier ist ein echtes Spec-Template. Keine User Story. Kein Produkt-Requirements-Dokument. Ein Arbeitsdokument, das einem Agent genau sagt, was er bauen soll.
## Problem
The filter bar resets when switching workspaces. Users lose their
filter state and have to re-apply filters every time they switch.
## Approach
Persist filter state per-workspace in localStorage. Key the stored
state by workspace database path so filters don't bleed across
workspaces.
## Files to Modify
- lib/local-storage.ts: Add getWorkspaceFilters / setWorkspaceFilters
- components/filter-bar.tsx: Read initial state from localStorage,
write on every change
- hooks/use-workspace.ts: Trigger filter restore on workspace switch
## Acceptance Criteria
1. Select workspace A, set filters to status=open + type=bug
2. Switch to workspace B. Filters reset to defaults.
3. Switch back to workspace A. Filters restore to status=open + type=bug.
4. Close the browser tab, reopen. Filters for the active workspace
are still applied.
5. bd list --status=open --type=bug output matches the filtered table.
## Out of Scope
- Server-side filter persistence
- Filter presets / saved filter combinations
- URL-based filter state (query params)
## Test Plan
- Unit test: getWorkspaceFilters returns stored value for matching
workspace path
- Unit test: setWorkspaceFilters writes correct key format
- Manual test: steps 1-5 from acceptance criteria above
Beachte, was diese Spec enthaelt und was nicht. Sie erklaert nicht, wie localStorage funktioniert. Das weiss Claude Code. Sie begruendet nicht, warum wir localStorage statt URL-Params gewaehlt haben. Das passierte in der Brainstorming-Phase. Sie listet jede Datei auf, die der Agent aendern soll. Wenn der Agent also anfaengt, Dateien ausserhalb dieser Liste zu aendern, ist das ein Warnsignal. Sie enthaelt einen Out-of-Scope-Abschnitt, der verhindert, dass der Agent Gold Plating betreibt.
Die Akzeptanzkriterien sind der wichtigste Teil. Jedes ist eine konkrete Aktion mit einem beobachtbaren Ergebnis. Nicht "Filter sollen persistiert werden." Das ist mehrdeutig. "Wechsle zu Workspace A, verifiziere, dass die Filter status=open + type=bug sind" ist testbar. Ein Agent kann das ausfuehren. Ein QA-Reviewer kann es verifizieren.
Das Plan-vor-Code-Muster
Hier ist ein Protokoll, das die Mehrheit der Implementierungsfehler abfaengt, bevor sie zu Code werden: Verlange vom Agent, seinen Implementierungsplan zu posten, bevor er irgendetwas schreibt.
In der Praxis sieht das wie ein strukturierter Kommentar zum Task aus. Bevor der Agent eine Datei oeffnet, schreibt er genau auf, was er vorhat.
PLAN: Persist filter state per-workspace using localStorage.
1. Add workspace-scoped storage helpers to lib/local-storage.ts:
- getWorkspaceFilters(dbPath: string): FilterState | null
- setWorkspaceFilters(dbPath: string, filters: FilterState): void
- Key format: "beadbox:filters:{dbPath}"
2. Update components/filter-bar.tsx:
- On mount, read stored filters for current workspace
- On every filter change, write to localStorage
- Accept databasePath as prop (currently not passed)
3. Update hooks/use-workspace.ts:
- On workspace switch, emit event that filter-bar listens for
- Filter-bar reads new workspace's stored filters on event
4. Threading databasePath:
- filter-bar needs databasePath from the workspace context
- Will add it to the WorkspaceProvider context value
Files: lib/local-storage.ts, components/filter-bar.tsx,
hooks/use-workspace.ts, components/workspace-provider.tsx
Test: Unit tests for storage helpers, manual test of all 5
acceptance criteria.
Das dauert beim Agent etwa 30 Sekunden. Es dauert bei dir etwa 2 Minuten zum Lesen. Und in diesen 2 Minuten kannst du Probleme erkennen, deren Behebung nach der Implementierung 20 Minuten dauern wuerde:
- Aendert der Agent Dateien ausserhalb der Spec? (workspace-provider.tsx hinzuzufuegen stand nicht in der Spec. Ist das OK oder Scope Creep?)
- Ergibt der Ansatz Sinn? (Einen Event-Emitter fuer Workspace-Wechsel zu verwenden koennte ueberengineert sein. Eine einfachere Prop-Aenderung koennte genuegen.)
- Fehlen Schritte? (Was ist mit dem Aufraeumen veralteter localStorage-Eintraege, wenn ein Workspace entfernt wird?)
Der Plan ist ein Checkpoint. Wenn er richtig aussieht, sag dem Agent, er soll weitermachen. Wenn er falsch aussieht, korrigiere den Plan. In jedem Fall hast du 2 statt 20 Minuten investiert.
