Stay Updated!

Get the latest posts and insights delivered directly to your inbox

Skip to content

Spec-Driven Development with Claude Code in Action

Published: at 

I’m building a simplified sync engine from scratch using Nuxt 4. My approach: study how production-grade frameworks solve the hard problems, then implement a minimal version myself.

Jazz is my primary reference a local-first framework with elegant patterns for persistence, conflict resolution, and cross-tab sync. Rather than reading through their codebase manually, I use Claude Code to research, extract patterns, and help me implement them.

This post documents the workflow I call Spec-Driven Development with Claude Code the exact prompts, tools, and patterns I used to migrate my storage layer from SQLite/WASM to IndexedDB in a single day.

The Problem#

My sync engine was using sql.js (SQLite compiled to WASM) for client-side storage. It worked, but had issues:

I wanted to migrate to IndexedDB, borrowing patterns from Jazz. But this was a significant refactor touching 15+ files. (For background on local-first web development What is Local-first Web Development? Explore the power of local-first web development and its impact on modern web applications. Learn how to build offline-capable, user-centric apps that prioritize data ownership and seamless synchronization. Discover the key principles and implementation steps for creating robust local-first web apps using Vue. local-first and why it matters, see my earlier post.)

The Workflow#

Instead of diving into code, I used Claude Code as an AI development teamwith myself as the product owner, Claude as the tech lead, and subagents as developers.

Important: I also cloned the source code of Jazz into my Project so Claude could reference it during research and implementation.

Spec-Driven Development workflow: Research Phase with parallel subagents, Spec Creation with written document, Refine with Q&A interview pattern, and Implement with task-per-subagent
The four phases of Spec-Driven Development with Claude Code

Phase 1: Research with Parallel Subagents#

The Prompt#

Prompt

you have access to jazz source repo explain to me how they use indexdb in the client to persist state our project is using sqlite but we want to change to indexdb with jazz your goal is to write a report spin up multiple subagents for your research task

What Happened#

Claude spawned 5 parallel research agents, each investigating a specific aspect of Jazz:

Research Phase: Five parallel agents (CRDT, WebSocket, Push/Pull, Storage, Architecture) each investigating independently, converging into a consolidated research report
Parallel research agents converging into a consolidated report

Each agent explored the Jazz codebase independently and reported back:

AgentFocusKey Findings
CRDTData structuresCoMap, CoList use operation-based CRDTs with LWW
WebSocketReal-time sync4-message protocol: load, known, content, done
Push/PullSync strategyHybrid model with known-state tracking
StoragePersistenceIndexedDB with coValues, sessions, transactions stores
ArchitectureOverall designMonorepo with platform adapters

Follow-up Prompt#

Prompt

research longer and improve the plan

This triggered deeper investigation into edge cases and implementation details.

Phase 2: Spec Creation#

After research, Claude wrote a comprehensive technical specification to docs/indexeddb-migration-spec.md: Full spec

# IndexedDB Migration Specification

## Part 1: How Jazz Uses IndexedDB
- Database schema (coValues, sessions, transactions stores)
- Transaction queuing pattern
- Entity caching layer
- Session-based conflict resolution

## Part 2: Current SQLite Architecture Analysis
- sql.js WASM setup
- Existing sync protocol
- Pain points and limitations

## Part 3: Migration Plan (4 Phases)
- Phase 1: Core IndexedDB utilities
- Phase 2: Composables layer
- Phase 3: Cross-tab sync
- Phase 4: Cleanup and testing

## Part 4: Implementation Checklist
- [ ] idb-helpers.ts
- [ ] useIndexedDB.ts
- [ ] useSessionTracking.ts
- ... (14 items total)

Key insight: The spec becomes the source of truth. It’s a document Claude can reference during implementation, ensuring consistency across all tasks. It also becomes a Pin that we can use if something went wrong during implementation.

Phase 3: Spec Refinement via Interview#

Before implementation, I wanted to ensure the spec was solid. I used Claude’s AskUserQuestion tool:

The Prompt#

Prompt

use the ask_user_question tool do you have any questions regarding @docs/indexeddb-migration-spec.md before we implement it we want to improve the specs

Claude asked clarifying questions:

After answering, I requested Vue-specific improvements:

Prompt

we want to use provide and inject you have access to the source code of pinia spin up multiple subagents how they do it so we can use same patterns

Claude researched Pinia’s patterns and updated the spec with:

Phase 4: Implementation with Task Delegation#

This is where the new Claude Code Task System shines. (If you’re unfamiliar with subagents and how they work in Claude Code, my customization guide Claude Code customization guide: CLAUDE.md, skills, subagents explained Compare CLAUDE.md, slash commands, subagents, and skills in Claude Code. Learn when to use each with practical Dexie.js examples. claude-codeaitooling +1 covers the fundamentals.)

The Prompt#

Prompt

implement @docs/indexeddb-migration-spec.md use the task tool and each task should only be done by a subagent so that context is clear after each task do a commit before you continue you are the main agent and your subagents are your devs

Understanding Claude Code’s Task System#

Claude Code’s task systeminspired by Beads, Steve Yegge’s distributed git-backed issue trackersolves two critical problems with AI coding agents:

Agent Amnesia: Starting a new session mid-task loses all progress unless you manually document remaining work.

Context Pollution: A full context window makes the agent drop discovered bugs instead of tracking them.

The previous todo list lived in session memory and vanished on restart. The new task system persists tasks to disk, making them shareable across sessions and subagents.

How Tasks Persist#

Tasks are stored in .claude/tasks/{session-id}/ as JSON files:

{
  "id": "task-1",
  "subject": "Create idb-helpers.ts",
  "description": "Implement IndexedDB promise wrappers...",
  "status": "pending | in_progress | completed",
  "blocks": ["task-3", "task-4"],
  "blockedBy": ["task-0"]
}

The Four Task Tools#

ToolPurpose
TaskCreateCreate a new task with subject, description, and dependencies
TaskUpdateUpdate status (pending → in_progress → completed) or modify dependencies
TaskListView all tasks, their status, and what’s blocked
TaskGetGet full details of a specific task including description

Task System Architecture#

Task Orchestration: Main Session orchestrator connects to Task List in .claude/tasks/, which delegates to Subagent 1, 2, and 3 each with fresh context. Below shows a dependency graph with Wave 1 (T1, T2, T3 in parallel), Wave 2 (T4, T5, T6 dependent), and Wave 3 (T7)
Task orchestration with dependency-aware parallel execution

Why Subagents + Tasks = Context Efficiency#

By delegating each task to a subagent, the main session stays leanit only handles orchestration (creating tasks, tracking progress, committing). Each subagent gets a fresh context window focused entirely on its specific task, reads what it needs, implements, and returns. This means the main agent won’t run out of context even for larger refactors with dozens of tasks.

For truly massive projects spanning days or weeks, a full autonomous agent like Ralph would be more appropriate. Ralph is elegantly simplea bash loop that feeds a markdown file into Claude Code repeatedly:

Ralph Architecture: A bash loop (while :; do cat PROMPT.md | claude-code ; done) where PROMPT.md feeds into Claude Code which produces output, and the loop continues indefinitely
Ralph's stateless architecture using markdown as persistent memory

The key difference: Ralph executes each iteration in a completely new Claude session, using the markdown file as the only persistent memory. This makes it truly stateless and capable of running for days.

This spec-driven approach hits a middle ground: subagents get fresh context but the main orchestrator maintains state within a single session. Structured enough to maintain coherence, flexible enough to handle complexity, without the setup overhead of a full autonomous system.

The Execution Flow#

Main Agent Orchestrator flow: TaskCreate spawns Subagent 1 which implements and returns, then TaskUpdate marks complete and commits, then TaskCreate spawns Subagent 2 with dependencies, repeating for all tasks
The orchestrator delegates each task to a subagent with atomic commits

Why This Pattern Works#

  1. Context isolation: Each subagent starts fresh, reading only what it needsno accumulated cruft
  2. Persistent progress: Tasks survive session restarts; pick up where you left off
  3. Dependency-aware parallelism: Claude identifies which tasks can run concurrently
  4. Atomic commits: Every task = one commit, making rollbacks trivial
  5. Spec as contract: Subagents reference the spec, ensuring consistency

Backpressure: Let the System Catch Mistakes#

One crucial element that makes atomic commits powerful: backpressure. Instead of manually reviewing every change, set up pre-commit hooks that run tests, linting, and type checking automatically.

# .husky/pre-commit
pnpm typecheck && pnpm lint && pnpm test-run

When a subagent commits, the hook runs immediately. If tests fail, the commit is rejected and the agent sees the error outputgiving it a chance to self-correct before moving on. This creates automated feedback that catches issues at the source rather than accumulating bugs across multiple tasks.

The result: you stop being the bottleneck for quality control. The system validates correctness while you focus on higher-level decisions.

When Things Go Wrong#

The first execution wasn’t perfectI started the project and hit some errors. But here’s where the spec pays off: I opened a new chat, pinned the spec document, pasted the error, and Claude fixed it immediately. No context rebuilding, no re-explaining the architecture.

The spec acts as a recovery point. When a session goes sideways or context gets polluted, you don’t lose everythingyou have a document that captures the full intent and design decisions.

The Results#

After ~45 minutes:

$ git log-oneline | head20

9dc1c96 refactor: clean up code structure
9fce16b feat(storage): migrate from SQLite to IndexedDB
835c494 feat: integrate IDB sync engine provider
d2cd7b7 refactor: remove SQLite/sql.js dependencies
2fb7656 feat: add browser mode test stubs
... (14 commits total)

14 tasks completed, 14 commits, 15+ files changed, one PR ready for review. See the full pull request (includes additional manual changes).

And despite orchestrating 14 subagents, the main session’s context stayed manageable:

Context Usage 71%
claude-opus-4-5-20251101 143k / 200k tokens
System prompt 2.8k (1.4%)
System tools 16.2k (8.1%)
MCP tools 293 (0.1%)
Custom agents 641 (0.3%)
Memory files 431 (0.2%)
Skills 1.6k (0.8%)
Messages 122.9k (61.4%)
Free space 22k (11.1%)
Autocompact buffer 33.0k (16.5%)

This proves the delegation pattern worksthe main agent handled orchestration while subagents did the heavy lifting in isolated contexts.

The Prompt Patterns#

Here are the key prompt patterns that make this workflow effective:

1. Parallel Research#

Prompt

spin up multiple subagents for your research task

Triggers Claude to spawn parallel agents, each investigating independently. Much faster than sequential research.

2. Spec-First Development#

Prompt

your goal is to write a report/document

Forces Claude to produce a written artifact before any code. This becomes the source of truth.

3. Interview Before Implementation#

Prompt

use the ask_user_question tool… before we implement

Surfaces ambiguities and design decisions before they become bugs.

4. Task Delegation with Commits#

Prompt

use the task tool and each task should only be done by a subagent after each task do a commit before you continue

Creates the orchestration pattern with atomic commits.

5. Role Assignment#

Prompt

you are the main agent and your subagents are your devs

Sets expectations for how Claude should behaveas a coordinator, not a solo implementer.

Comparison: Traditional vs Spec-Driven#

Traditional AI CodingSpec-Driven Development
FlowPrompt → Code → Debug → RepeatResearch → Spec → Refine → Tasks → Done
ContextFills up with failed attemptsEach task gets fresh context
MemoryNo persistence across sessionsSpec is persistent source of truth
Bug trackingDiscovered late, forgottenBugs become new tasks
CompletionNo clear stopping pointClear completion criteria

Advanced: Multi-Session Workflows#

The task system supports coordination across multiple Claude Code sessions. Set a shared task list ID:

CLAUDE_CODE_TASK_LIST_ID=myproject claude

Or add to .claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_TASK_LIST_ID": "myproject"
  }
}

One session acts as orchestrator; another becomes a checker that monitors completed tasks, verifies implementation quality, and adds follow-up tasks for anything missing.

When to Use This Workflow#

This pattern excels for:

It’s overkill for:

The Tools You Need#

  1. Claude Code CLI (latest version with task tools)
  2. A spec document (markdown works great)
  3. Reference codebases if learning from existing implementations
  4. Git for atomic commits

Further Reading#

Conclusion#

Spec-Driven Development with Claude Code mirrors real engineering workflows: parallel work, handoffs, blockers, and dependencies. Instead of treating Claude as a solo coder, you treat it as a team.

The key insight from Beads applies here:

“By having each task that you give a coding agent isolated into its own context window, you can now give it the ability to log any bugs for later.”

The SQLite to IndexedDB migration would have taken me 2-3 days manually. With this workflow, it took one afternoonand produced better code thanks to the research phase uncovering patterns from Jazz I wouldn’t have found on my own.


Try it yourself: Start your next significant feature with “write a spec for X, spin up subagents for research” and see how it changes your workflow.

Press Esc or click outside to close

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now