You can talk to the AI all day. It won’t remember what you meant yesterday.


The Problem with Pure Vibe Coding

Vibe coding feels like magic — until it doesn’t.

“Add a dark mode toggle.” Nailed it. “Write a function that validates email addresses.” Perfect. “Create a REST endpoint for user profiles.” Done in seconds.

Then you try to build something real, and around iteration 3-5, the wheels come off:

Context drift: Each new prompt builds on the AI’s understanding of your conversation, but that understanding is a lossy compression of everything you’ve discussed. By iteration 5, the AI’s mental model of your project has drifted from yours in ways neither of you can see.

Assumption divergence: When you say “add error handling,” you mean one thing. The AI interprets it based on patterns it’s seen in training data. Both interpretations are reasonable. They’re just different. These small divergences compound.

Architecture erosion: Without explicit architectural constraints, each iteration makes locally sensible decisions that gradually erode global consistency. The API endpoint follows one pattern, the database layer follows another, the error handling is different in every file.

The Ratchet Effect: Once the AI generates code you accept, it becomes the context for future generations. Mistakes in iteration 2 shape iterations 3 through 10. By the time you notice the problem, it’s load-bearing.

Spec-driven development solves all four. The core idea: write the specification before the code, then tell the AI to implement against the spec. You do the thinking. The AI does the typing.


Your Project’s Constitution: CLAUDE.md

What It Is

CLAUDE.md is a special file that Claude Code reads automatically at the start of every session. Think of it as your project’s constitution — the set of non-negotiable facts, conventions, and constraints that should inform every action the AI takes.

It’s not a prompt. It’s not a conversation. It’s persistent project context that survives across sessions.

Where It Lives

Claude Code reads CLAUDE.md files in a hierarchy, from most general to most specific:

  1. ~/.claude/CLAUDE.md — Your personal defaults. Things that apply to every project: your coding style preferences, your preferred testing framework, your name and contact info for generated comments.

  2. ./CLAUDE.md — Your project root. The main constitution for this project. This is the one you’ll spend the most time on.

  3. ./src/CLAUDE.md or any subdirectory — Directory-specific overrides. Useful for monorepos or projects with distinct subsystems (e.g., different conventions for the frontend vs. the API).

More specific files override more general ones, but all are loaded. You can also import external files using @path/to/file syntax.

What Belongs in CLAUDE.md

The goal is to be comprehensive enough that Claude Code rarely makes wrong assumptions, but concise enough that it doesn’t waste context window space. Target under 200 lines per file.

Here’s a template with annotations:

# Project: [Name]

## Overview
[2-3 sentences. What is this project? Who is it for? What's its current state?]

Example:
Personal CRM for freelance consulting. Tracks clients, projects, invoices,
and follow-up reminders. Currently in active development, ~60% complete.
SQLite backend, React frontend, deployed on a personal VPS.

## Tech Stack
- Runtime: Node.js 20 LTS
- Backend: Express 4.x with TypeScript
- Frontend: React 18 + Vite + Tailwind CSS
- Database: SQLite via better-sqlite3 (NOT Prisma, NOT Sequelize)
- Testing: Vitest for unit, Playwright for E2E
- Package manager: pnpm (NOT npm, NOT yarn)

## Project Structure

src/ api/ # Express route handlers. One file per resource. db/ # Database schemas, migrations, query functions. components/ # React components. One component per file. lib/ # Shared utilities. Pure functions only. types/ # TypeScript type definitions. Shared across api/ and components/. tests/ unit/ # Mirrors src/ structure e2e/ # Playwright tests. One file per user flow.


## Architecture Decisions
- All API endpoints return JSON. No server-side rendering.
- Database queries live in db/ as plain functions, NOT in route handlers.
- Error handling: all routes use the errorHandler middleware in src/api/middleware.ts.
  Never add try/catch in individual route handlers.
- Auth: JWT tokens stored in httpOnly cookies. Auth middleware in src/api/middleware.ts.

## Coding Conventions
- Functions: named exports, camelCase, JSDoc on public functions
- Components: PascalCase, functional only, props typed inline
- SQL: raw queries via better-sqlite3, NOT an ORM
- Imports: absolute paths from src/ root (configured in tsconfig)
- No default exports except React components

## Commands
- `pnpm dev` — Start dev server (frontend + backend)
- `pnpm test` — Run all tests
- `pnpm test:unit` — Unit tests only
- `pnpm test:e2e` — E2E tests only
- `pnpm build` — Production build
- `pnpm db:migrate` — Run pending migrations

## Do Not Touch
- src/api/middleware.ts — Auth and error handling. Critical path. Do not modify
  without explicit instruction.
- src/db/migrations/ — Never modify existing migration files. Create new ones.
- .env and .env.example — Contains secrets and config. Never log or expose.

## Current Focus
Working on the invoicing module. Next up: PDF generation for invoices
and email sending via Resend API.

What Does NOT Belong in CLAUDE.md

Entire API docs or library documentation. Claude already knows how Express, React, and SQLite work. You’re providing your project’s specific choices, not teaching fundamentals.

Generated code or examples. This wastes context on things the AI can figure out by reading your actual codebase.

Aspirational architecture you haven’t built yet. CLAUDE.md should describe what is, not what you wish it were. If you’re planning a migration from SQLite to Postgres, note it in a spec file, not here.

Anything over ~200 lines. If your CLAUDE.md is growing beyond this, split into directory-level files or use the @import syntax to pull in focused sub-documents.

Progressive disclosure matters. You don’t need everything in CLAUDE.md — just enough for Claude to orient itself, plus pointers to where it can find more. “Database schema is in src/db/schema.sql” is better than pasting the entire schema into your CLAUDE.md. Think of it as a table of contents, not an encyclopedia.

Quick Start: The /init Command

If you have an existing project and want a starting point, Claude Code’s /init command analyzes your codebase and generates a draft CLAUDE.md. It detects build systems, test frameworks, code patterns, and project structure. Use it as a foundation, then edit heavily — the generated version is a starting point, not a finished product.


Writing Specs That Actually Work

The Spec-Driven Workflow

A spec is a document you write before asking Claude to generate code. It describes what you want built, how it should work, what constraints it must satisfy, and what edge cases it must handle.

The workflow:

  1. You write the spec — in markdown, in a file called spec.md or embedded in your CLAUDE.md under a “Current Task” section
  2. You give it to Claude — “Implement the spec in spec.md” or “Build the feature described in the Current Focus section of CLAUDE.md”
  3. Claude implements against the spec — with explicit requirements to reference, not vibes to interpret
  4. You review against the spec — not “does this look right?” but “does this satisfy every requirement in the spec?”

One study found that a single structured iteration (spec, implement, review) matched the accuracy of 8 unstructured iterations (vibe, fix, vibe, fix…). The upfront time writing the spec is recovered many times over.

Spec Template

Here’s a template for a feature spec. You don’t need every section for every task — scale it to the complexity of what you’re building.

# Feature: [Name]

## Summary
[1-2 sentences. What is this feature and why does it exist?]

## Requirements
### Must Have
- [ ] [Concrete, testable requirement]
- [ ] [Another concrete requirement]
- [ ] [Be specific: "validates email format using RFC 5322" not "validates email"]

### Nice to Have
- [ ] [Things you'd like but won't block acceptance]

## API Contract (if applicable)
### POST /api/invoices
Request:
```json
{
  "client_id": "string (UUID)",
  "line_items": [{ "description": "string", "amount": "number (cents)" }],
  "due_date": "string (ISO 8601)"
}

Response (201):

{
  "id": "string (UUID)",
  "status": "draft",
  "total": "number (cents)",
  "created_at": "string (ISO 8601)"
}

Error responses:

Data Model

CREATE TABLE invoices (
  id TEXT PRIMARY KEY,
  client_id TEXT NOT NULL REFERENCES clients(id),
  status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','sent','paid','overdue')),
  due_date TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE TABLE invoice_line_items (
  id TEXT PRIMARY KEY,
  invoice_id TEXT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
  description TEXT NOT NULL,
  amount INTEGER NOT NULL CHECK(amount > 0)
);

Edge Cases

Testing Requirements

Out of Scope


### Why This Works

When Claude Code receives a spec like this, it doesn't have to guess:
- What the API should look like (you defined it)
- How errors should be handled (you specified each case)
- What the database schema looks like (you wrote it)
- What "done" means (you provided testable requirements)

Every decision that previously happened implicitly during vibe coding — often incorrectly — now happens explicitly during spec writing — under your control.

### Spec Anti-Patterns

**The Vague Spec:** "Build an invoicing system." This isn't a spec, it's a wish. Claude will make 50 decisions you didn't authorize.

**The Novel:** 2000 lines covering every possible future feature. This wastes context and buries the important parts. Spec the *current* task, not the entire roadmap.

**The Micromanager:** "Create a file called invoiceController.ts with a function called createInvoice that calls db.run()..." If you're specifying implementation this precisely, you might as well write the code yourself. Specs define *what* and *why*, not *how*.

**The Untestable Spec:** "Make the UI feel modern and clean." Claude can't verify this, and neither can you in code review. Every requirement should be testable — either with automated tests or concrete acceptance criteria.

### Spec-Driven Development Has Legs

When this course was first written, spec-driven development was a practical technique we'd found useful. Since then, it's become an industry movement: Thoughtworks added it to their Technology Radar, GitHub released an open-source Spec Kit toolkit, AWS launched Kiro (a spec-first development environment), and there's even an arXiv paper formalizing the approach.

You don't need any of those tools — the workflow in this module works fine with Claude Code and a markdown file. But it's worth knowing you're learning a pattern with real traction, not a niche technique.

---

## Context Engineering (The Real Skill)

### Forget "Prompt Engineering"

"Prompt engineering" got all the hype in 2023-2024. The real skill turned out to be *context engineering* — managing what information the AI has access to before you ask it anything.

The reason is simple: response quality is bounded by context quality. A perfect prompt with garbage context produces garbage. A mediocre prompt with excellent context produces good output. Context is the bottleneck. Not phrasing.

### How the Context Window Works

Claude Code operates within a context window — a fixed amount of text it can "see" at any given moment. Think of it as working memory. Everything in the conversation (your messages, Claude's responses, file contents, command outputs) occupies space in this window.

**The key facts:**
- The context window has a hard limit (varies by model, but typically 200K tokens, with 1M-token extended context in beta for some models)
- As it fills, response quality degrades — not suddenly, but gradually
- The status bar in Claude Code shows a fill indicator
- Quality starts slipping noticeably around 60% full
- At 75%, start a fresh session. Seriously.
- Sessions that stop at 75% produce less total output but higher-quality, more maintainable code
- Research on the "lost in the middle" effect confirms this empirically: information at the start and end of context gets 85-95% accuracy, while middle sections drop to 76-82%. Front-load what matters.

### Practical Context Management

**Watch the meter.** Check the context fill indicator regularly. It's in the Claude Code status bar. Don't wait until you notice quality degrading — by then you've already wasted tokens on subpar output.

**Use `.claudeignore`.** Create a `.claudeignore` file (same syntax as `.gitignore`) to prevent Claude Code from loading irrelevant files:

.claudeignore

node_modules/ dist/ build/ *.min.js *.min.css package-lock.json *.sqlite *.db coverage/ .next/


This keeps Claude from wasting context on dependency code, build artifacts, and binary files it can't meaningfully reason about.

**One session per domain.** For larger projects, run separate terminal sessions:
- Terminal 1: Backend development
- Terminal 2: Frontend development
- Terminal 3: Testing and debugging

Each session maintains focused context without cross-pollution. The backend session doesn't need to hold React component code in memory, and vice versa.

**Start fresh rather than pushing through.** When context is filling up and you have more work to do, start a new session with a clear, focused task description. Your CLAUDE.md provides continuity — the new session picks up your project's constitution automatically. This is almost always better than grinding through a degraded session.

**Use `/compact` strategically.** The `/compact` command triggers server-side summarization of earlier conversation. It buys you more room but it's lossy — details from early in the conversation get compressed. Use it when you need more room for a specific task, not as a crutch.

### The Context Budget

Think of your context window as a budget:

| Action | Approximate Cost |
|---|---|
| Reading a small file (50 lines) | Low |
| Reading a large file (500+ lines) | Medium-high |
| Running a command with verbose output | Medium-high |
| A back-and-forth debugging conversation | High (cumulative) |
| Claude's own reasoning and responses | Medium per response |

Plan your sessions like you'd plan a budget. Front-load the important context (CLAUDE.md + the specific files you need), spend the middle on productive work, and wrap up before you hit the wall.

---

## The Interview Pattern

There's a useful middle ground between pure vibe coding and full spec writing: have Claude interview you.

Instead of writing a complete spec upfront, you ask Claude Code to *interview you* about the feature before implementing it:

Before implementing anything, interview me about this feature. Ask me questions about requirements, edge cases, and constraints until you have enough information to write a spec. Then show me the spec for approval before writing any code.


This works well when:
- You know *what* you want but haven't thought through all the details
- The feature is complex enough that you'd miss things writing a spec solo
- You want Claude to surface edge cases you haven't considered

The AI asks questions like "What should happen when the user submits an empty form?" or "Should deleted invoices be soft-deleted or permanently removed?" — questions that force you to make decisions upfront rather than discovering them during code review.

The interview produces a spec. You review and approve it. Then implementation proceeds against an agreed-upon blueprint. It's spec-driven development with collaborative spec writing.

---

## A Session That Actually Works

Here's what a well-structured Claude Code session looks like, putting it all together:

1. **Start with CLAUDE.md already in place.** Claude reads it automatically.

2. **State your task clearly.** "I'm working on the invoicing feature. The spec is in `specs/invoicing.md`."

3. **Let Claude read the spec and relevant code.** It will pull in the files it needs.

4. **Implement in focused chunks.** Don't ask for the entire feature at once. "Implement the database migration and query functions first. We'll do the API routes next."

5. **Review each chunk against the spec.** "Does this migration match the schema in the spec?"

6. **Monitor context.** Past 60%? Consider whether remaining work should happen in a fresh session.

7. **Test before moving on.** "Run the tests for the invoice module."

8. **Commit at natural boundaries.** Each spec section completed = a good commit point.

The cadence is: spec, implement chunk, review, test, commit, next chunk. Not: vibe, debug, vibe, debug, wonder why nothing works, start over.

A disclosure: I use Claude Code for most of this course's examples — that's my bias, and I want to be upfront about it. The spec-driven workflow translates to other AI coding tools, but the specific mechanics (CLAUDE.md, subagents, `/compact`) are Claude Code features.

---

## Further Reading

| Resource | Why You'd Read It |
|---|---|
| [Using CLAUDE.md Files (Official Anthropic Blog)](https://claude.com/blog/using-claude-md-files) | The authoritative guide from Anthropic with examples |
| [Writing a Good CLAUDE.md (HumanLayer)](https://www.humanlayer.dev/blog/writing-a-good-claude-md) | Practical advice with real-world CLAUDE.md examples |
| [How to Write a Good CLAUDE.md (Builder.io)](https://www.builder.io/blog/claude-md-guide) | Another practical guide with annotated examples |
| [Spec-Driven Development (Agent Factory)](https://agentfactory.panaversity.org/docs/General-Agents-Foundations/spec-driven-development) | Deep tutorial on the spec-driven workflow with Claude Code |
| [Spec-Driven Development (Thoughtworks)](https://www.thoughtworks.com/en-us/insights/blog/agile-engineering-practices/spec-driven-development-unpacking-2025-new-engineering-practices) | Thoughtworks' analysis of why this became 2025's key practice |
| [Context Management Guide (CodeAgents)](https://codeagents.app/guides/context-management) | Detailed strategies for context window optimization |
| [Claude Code Best Practices (Official Docs)](https://code.claude.com/docs/en/best-practices) | Anthropic's own best practice recommendations |

---

*Previous: [Module 02 — The Vibe Coding Landscape](/guide/02-vibe-coding/)* | *Next: [Module 04 — Multi-Agent Workflows](/guide/04-multi-agent/)*