Zurück zum Blog

Spec-getriebene Entwicklung mit Claude Code

Spec-getriebene Entwicklung mit Claude Code

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.

Das ist das Problem, das Beadbox loest.

Echtzeit-Ueberblick ueber alles, was Ihre Agent-Flotte gerade tut.

Kostenlos testen waehrend der Beta →

Verifikation ist nicht "es kompiliert"

Der haeufigste Fehlermodus, den ich bei Claude Code sehe, ist nicht, dass Agents schlechten Code schreiben. Es ist, dass niemand den Output gegen die Spec verifiziert hat.

Der Agent sagt "DONE." Der Entwickler ueberfliegt den Diff, findet ihn vernuenftig und merged. Zwei Tage spaeter entdeckt jemand, dass das Feature Randfall Nr. 3 aus den Akzeptanzkriterien nicht abdeckt. Jetzt debuggst du in Produktion statt es beim 5-minuetigen Verifikationsschritt abzufangen.

Verifikation bedeutet, jedes Akzeptanzkriterium mechanisch zu pruefen. Nicht "scheint zu funktionieren." Nicht "die Tests laufen" (Tests koennen falsch oder unvollstaendig sein). Jedes Kriterium aus der Spec bekommt einen konkreten Check.

So sieht ein ordentlicher Abschlussbericht aus:

DONE: Filter bar now persists selected filters across workspace
switches using per-workspace localStorage.

Changes:
- lib/local-storage.ts: Added getWorkspaceFilters/setWorkspaceFilters
  with key format "beadbox:filters:{dbPath}"
- components/filter-bar.tsx: Reads stored filters on mount, writes
  on change. Accepts databasePath prop.
- hooks/use-workspace.ts: Triggers filter restore on workspace switch
  via callback prop.

QA Verification:
1. Open http://localhost:41420, select workspace A
2. Set filters to status=open, type=bug
3. Switch to workspace B via header dropdown
4. Switch back to workspace A
5. Verify filters are still status=open, type=bug
   -> Confirmed: filters restore correctly
6. Close tab, reopen. Filters persist.
   -> Confirmed: localStorage key present, filters applied on mount
7. Run: bd list --status=open --type=bug
   -> Output matches filtered table contents (14 beads)

Acceptance criteria:
- [x] Filters persist across workspace switches (steps 2-5)
- [x] Filters survive browser restart (step 6)
- [x] Filtered view matches bd CLI output (step 7)
- [x] Filters don't bleed between workspaces (step 3: workspace B
      shows defaults)

Unit tests: 3 added (storage read/write/key format). All passing.

Commit: a1b2c3d

Der Unterschied zwischen diesem Bericht und "DONE: Fixed the filter bar" ist der Unterschied zwischen einem 5-minuetigen QA-Check und einer 30-minuetigen Untersuchung. Jede Behauptung im DONE-Kommentar ist durch einen spezifischen Check belegt. Jedes Akzeptanzkriterium ist einem Verifikationsschritt zugeordnet. Der Reviewer weiss genau, was gebaut wurde, wie es verifiziert wurde und wo er nachschauen muss, wenn etwas nicht stimmt.

Beads als Spec-Container

Der gerade beschriebene Lebenszyklus braucht einen Ort zum Leben. Die Spec, der Plan-Kommentar, die Implementierung, der Abschlussbericht, die Verifikationsergebnisse. Alles an einem Task, an einem Ort.

Dieses Problem loest beads. Beads ist ein Open-Source, Local-first Issue-Tracker, der genau fuer diesen Workflow konzipiert ist. Jeder "Bead" ist ein Task mit einer Beschreibung (deine Spec), einem Kommentar-Thread (Plaene und Abschlussberichte), einem Status (open, in_progress, ready_for_qa, closed) und Metadaten wie Prioritaet, Abhaengigkeiten und Zuweisungen.

So sieht der Spec-getriebene Lebenszyklus in der Praxis mit dem bd CLI aus:

Erstelle den Bead mit deiner Spec:

bd create --title "Persist filter state across workspace switches" \
  --description "## Problem
The filter bar resets when switching workspaces...

## Acceptance Criteria
1. Select workspace A, set filters...
2. Switch to workspace B..." \
  --type feature --priority p2

Agent beansprucht die Arbeit und postet einen Plan:

bd update bb-a1b2 --claim --actor eng1
bd comments add bb-a1b2 --author eng1 "PLAN: Persist filter state
per-workspace using localStorage.

1. Add workspace-scoped storage helpers...
2. Update filter-bar component...
3. ..."

Agent schliesst die Arbeit ab und postet einen Abschlussbericht:

bd comments add bb-a1b2 --author eng1 "DONE: Filter bar now persists
selected filters across workspace switches.

QA Verification:
1. Open http://localhost:41420...

Acceptance criteria:
- [x] Filters persist across workspace switches
- [x] Filters survive browser restart
...

Commit: a1b2c3d"

bd update bb-a1b2 --status ready_for_qa

QA uebernimmt und verifiziert:

bd show bb-a1b2  # Spec und DONE-Kommentar lesen
# Verifikationsschritte ausfuehren
bd comments add bb-a1b2 --author qa1 "QA PASS: All 5 acceptance
criteria verified. Filters persist, restore, and match bd CLI output."

Der gesamte Lebenszyklus steckt im Bead. Die Spec ist die Beschreibung. Der Plan ist ein Kommentar. Der Abschlussbericht ist ein Kommentar. Das QA-Ergebnis ist ein Kommentar. In sechs Monaten, wenn jemand fragt "Wie funktioniert die Filter-Persistenz und warum haben wir localStorage statt URL-Params gewaehlt?", steht die Antwort im Kommentar-Thread des Beads.

Wenn du eine einzelne Spec durch diese Pipeline fuehrst, reichen ein Terminal und bd show. Aber dieser Workflow zeigt seinen wahren Wert, wenn du mehrere Specs parallel bearbeitest.

Spec-getriebene Entwicklung skalieren

Stell dir das reale Szenario vor: Du hast drei Claude Code Agents, die jeweils eine andere Spec implementieren. Agent A baut ein Filter-Persistenz-Feature. Agent B fuegt einen neuen API-Endpoint fuer Workspace-Statistiken hinzu. Agent C behebt einen WebSocket-Reconnection-Bug. Jeder ist irgendwo im Spec-getriebenen Lebenszyklus.

Im Terminal muestest du bd list ausfuehren, um alle aktiven Beads zu sehen, dann bd show fuer jeden einzelnen, um Status und letzten Kommentar zu pruefen. Das sind sechs Befehle fuer einen Snapshot von drei parallelen Arbeitsstroemen. Multipliziere das mit fuenf oder zehn Agents und du verbringst mehr Zeit mit Statusprüfung als mit Plan-Reviews.

Hier passt Beadbox ins Bild. Beadbox ist ein Echtzeit-Dashboard, das dir den Status jedes Beads in deinem Workspace zeigt. Welche Specs offen sind und auf einen Agent warten. Welche Plaene gepostet haben, die dein Review brauchen. Welche in Arbeit sind. Welche fuer QA-Verifikation bereit sind. Alles aktualisiert sich live, waehrend Agents Kommentare schreiben und Status aendern ueber das bd CLI.

Du brauchst Beadbox nicht fuer Spec-getriebene Entwicklung. Das CLI deckt den gesamten Lebenszyklus ab. Aber wenn du mehrere Spec-getriebene Workflows parallel laufen hast, aendert die Moeglichkeit, die Pipeline auf einen Blick zu sehen statt jeden Agent einzeln abzufragen, wie schnell du Plaene reviewen, Agents entblocken und stockende Arbeit erkennen kannst.

Beadbox ist waehrend der Beta kostenlos, und das beads CLI, auf dem es laeuft, ist Open Source.

Was unabhaengig vom Tooling gilt

Ob du beads, GitHub Issues, Linear oder reine Textdateien verwendest: Das Spec-getriebene Muster funktioniert, weil es eine fundamentale Asymmetrie in der Arbeitsweise von Agents adressiert: Sie sind schnell in der Ausfuehrung und schlecht im Urteilen. Jede Minute, die du in eine klare Spec investierst, spart mehrere Minuten beim Review falschen Outputs, beim Debuggen stiller Annahmen und beim Nacharbeiten von Code, der in die falsche Richtung ging.

Die Prinzipien:

  1. Definiere "fertig" vor "Start." Akzeptanzkriterien sind nicht optional. Sie sind das Einzige, was Verifikation ermoeglicht.

  2. Plaene sind Checkpoints, keine Buerokratie. Ein 30-Sekunden-Plan-Kommentar spart 20-Minuten-Rewrites. Reviewe den Plan, nicht den Code.

  3. Verifikation ist ein Protokoll, kein Gefuehl. "Sieht gut aus" ist keine Verifikation. Jedes Akzeptanzkriterium einem konkreten Check zuzuordnen ist Verifikation.

  4. Die Spec ist die einzige Quelle der Wahrheit. Wenn Implementierung und Spec nicht uebereinstimmen, ist die Implementierung falsch. Diese Regel existiert, weil Agents einen schlechten Plan nicht hinterfragen. Sie fuehren ihn treu aus und produzieren treu falschen Output.

  5. Scope-Grenzen verhindern Drift. Eine explizite Liste zu aendernder Dateien und ein Out-of-Scope-Abschnitt hindern den Agent daran, Dinge zu "verbessern", die du nicht verbessern lassen wolltest.

Die Investition ist gering: 10-20 Minuten fuer eine Spec bei einem Feature, dessen Implementierung eine Stunde dauert. Der Ertrag ist gross: konsistente Ergebnisse, reviewbarer Output und eine permanente Aufzeichnung davon, was gebaut wurde und warum.

Wenn du solche Workflows aufbaust, gib Beadbox einen Stern auf GitHub.

Like what you read?

Beadbox is a real-time dashboard for AI agent coordination. Free during the beta.

Share