NEW: Claude Code Security — research preview →

Plan-Driven Development

Use PLAN.md, task decomposition, and checkpoint commits to keep AI agents on track for large features

Read time: 13 min

title: "Plan-Driven Development" description: "Use PLAN.md, task decomposition, and checkpoint commits to keep AI agents on track for large features" section: "Workflows" readTime: "13 min"

Plan-Driven Development

Large features fail when you hand an AI agent a vague goal and walk away. Plan-driven development solves this: you create a structured plan file first, the agent uses it as a grounding document, and every checkpoint is a commit that can be rolled back.

Why Plans Matter for AI Agents

AI agents are excellent at executing well-defined steps but poor at decomposing ambiguous requirements autonomously. A plan file:

  • Forces you to think through the design before any code is written
  • Gives the agent clear acceptance criteria for each step
  • Creates natural review points so you don't end up reviewing 2,000 lines of unreviewed AI code
  • Serves as a shared artifact that any agent (Claude, Copilot, Cursor) can pick up

The PLAN.md Pattern

Create a PLAN.md in your repo root or feature branch:

# Feature: User Authentication
 
## Goal
Add email/password auth with JWT tokens and refresh rotation.
 
## Constraints
- Use existing Prisma schema — do not modify users table
- All endpoints must be protected by the existing rate limiter
- No new dependencies without approval
 
## Steps
 
### Step 1: Password hashing utilities [ ]
- Create `src/lib/auth/password.ts`
- Export: `hashPassword(plain: string): Promise<string>`
- Export: `verifyPassword(plain: string, hash: string): Promise<boolean>`
- Tests: `__tests__/auth/password.test.ts`
 
### Step 2: JWT utilities [ ]
- Create `src/lib/auth/jwt.ts`
- Export: `signToken(payload: JWTPayload): string`
- Export: `verifyToken(token: string): JWTPayload | null`
- 15min access token, 7-day refresh token
 
### Step 3: Auth endpoints [ ]
- POST /api/auth/register
- POST /api/auth/login → returns { accessToken, refreshToken }
- POST /api/auth/refresh → rotates refresh token
- POST /api/auth/logout → invalidates refresh token
 
### Step 4: Middleware [ ]
- `src/middleware/auth.ts` — validates JWT, attaches user to request
- Apply to all /api/protected/* routes
 
## Acceptance Criteria
- [ ] All tests pass
- [ ] No secrets logged
- [ ] Rate limiting applies to /register and /login

Running a Plan with Claude Code

Option 1: Step-by-step with checkpoints

claude "Read PLAN.md. Execute Step 1 only. 
After completing it, run the tests, then stop and wait for my approval 
before moving to Step 2. Mark the step [x] in PLAN.md when done."

After review:

claude "Step 1 looks good. Proceed to Step 2."

Option 2: Autonomous execution with commits

claude "Read PLAN.md. Execute each step in order. 
After each step: run tests, commit with message 'feat: PLAN step N — <description>', 
then continue. If any step fails tests after 2 attempts, stop and report."

Option 3: Plan mode first

Use Claude Code's built-in plan mode. In plan mode Claude reads files and runs exploratory commands but does not edit your source until you approve a plan. Enter it any of these ways:

# Start a session in plan mode
claude --permission-mode plan
 
# Or, inside a session: press Shift+Tab until the status bar shows "⏸ plan mode on"
# Or prefix a single prompt with /plan
/plan Add authentication as described in the requirements below: [paste requirements]

When Claude presents the plan, you choose how to proceed:

OptionEffect
Yes, and use auto modeApprove and execute with auto mode's classifier reviewing actions (reads Yes, auto-accept edits if auto mode isn't available)
Yes, manually approve editsApprove and review each edit individually
No, keep planningStay in plan mode and tell Claude what to change

Press Ctrl+G to open the proposed plan in your text editor and edit it directly before approving. Approving exits plan mode and Claude starts implementing; press Shift+Tab (or use /plan again) to plan the next phase.

To make plan mode the default for a project, set "defaultMode": "plan" under permissions in .claude/settings.json. The opusplan model alias uses Opus while planning and Sonnet for execution.

Planning adds overhead. Skip it when the change is small and clear — if you could describe the diff in one sentence, ask Claude to do it directly. Plan when you're unsure of the approach, the change touches multiple files, or you're unfamiliar with the code.

Option 4: Let Claude interview you into a spec

For larger features, have Claude draft the plan document with you:

I want to build [brief description]. Interview me in detail using the AskUserQuestion tool.
Ask about technical implementation, UI/UX, edge cases, concerns, and tradeoffs.
Keep interviewing until we've covered everything, then write a complete spec to SPEC.md.

Then start a fresh session (/clear or a new claude) to implement from SPEC.md with clean context.

GitHub Copilot Plan-Driven Flow

VS Code's Copilot Chat has a built-in Plan agent: pick Plan from the agents dropdown, or type /plan followed by your task, to get an implementation plan before any code is written. A committed plan file still works with any agent:

  1. Create PLAN.md manually or ask Copilot to generate it first:

    Before writing any code, create a PLAN.md for adding authentication. 
    Break it into atomic steps, each with files to create/modify and test requirements. 
    Wait for my approval before implementing.
    
  2. Execute step by step with the Agent:

    Implement only Step 1 from #PLAN.md. 
    Run the tests when done. Don't touch Step 2 yet.
    

Cursor Plan-Driven Flow

Cursor has a built-in Plan Mode (press Shift+Tab in the chat input or use the mode picker): the agent asks clarifying questions, researches the codebase, and produces a plan you can edit before building. Plans are saved in your home directory by default; click Save to workspace to keep one in the repo.

To run an existing plan in Agent mode:

Read @PLAN.md. Implement Step 1 only. 
After each file change, run npm test.
When Step 1 is complete and tests pass, update the checkbox in PLAN.md and stop.

@-mentioning a file attaches it to the chat as context.

Checkpoint Commits

Commit after every completed step — not at the end of the feature:

# Good: atomic, reviewable commits
git commit -m "feat: auth — password hashing utils (PLAN step 1/4)"
git commit -m "feat: auth — JWT sign/verify (PLAN step 2/4)"
git commit -m "feat: auth — register+login endpoints (PLAN step 3/4)"
git commit -m "feat: auth — middleware + route protection (PLAN step 4/4)"
 
# Bad: one 800-line commit
git commit -m "add authentication"

Benefits: if Step 3 goes wrong, git reset --hard HEAD~1 gets you back to a working Step 2 state.

Checkpoints vs Commits

Claude Code also checkpoints automatically: every prompt you send creates a checkpoint, and pressing Esc twice or running /rewind lets you restore the conversation, the code, or both to an earlier point (or summarize from a selected message). This is handy for trying a risky step and backing out.

Checkpoints only track changes made through Claude's file-editing tools — not changes from Bash commands or external processes. They complement git commits; they don't replace them.

Verify Against the Plan

Before marking the feature done, have a fresh-context reviewer check the work against the plan:

Use a subagent to review the diff against PLAN.md. Check that every requirement
is implemented, the listed edge cases have tests, and nothing outside the task's
scope changed. Report gaps, not style preferences.

For a pure correctness pass, run /code-review.

Plan Quality Checklist

When Plans Break Down

SymptomFix
Agent skips to Step 4 without finishing Step 2Add explicit "stop and wait" instructions between steps
Plan becomes stale mid-implementationAsk agent to update PLAN.md with what actually changed
Agent rewrites the plan to make it easierVersion-control the plan; tell agent it's read-only
Feature scope creepsAdd a "Non-goals" section to PLAN.md