How to prompt Claude Code for a large feature (without losing the thread)
A practical workflow for prompting Claude Code on features that span multiple files, sessions, and steps — without hallucinations, half-implementations, or context drift.
Claude Code is remarkably capable on small, well-defined tasks. Ask it to add a search endpoint, refactor a component, or fix a specific bug — and it usually gets there in one or two passes.
Ask it to implement a full feature — say, a real-time collaboration layer, a billing integration, or a multi-step onboarding wizard — and something different happens. It starts well. Then it starts making assumptions. Then it starts working on a subtly different feature than the one you described. By the time it's "done," you're looking at output that's 60% right and 100% committed to patterns that don't match your codebase.
This isn't a model failure. It's a context failure. Here's how to fix it.
Why large features go sideways
The core problem is that large features have more state than a single context window can hold clearly:
- Scope state: what exactly needs to be built, and what's explicitly out of scope
- Codebase state: what patterns exist, what's established, what's off-limits
- Progress state: what's done, what's pending, what changed during implementation
When you prompt a large feature in one shot, all three of these are underspecified. The agent fills in the gaps with its training data — which means generic patterns instead of your patterns, scope drift as it makes judgment calls, and no awareness of decisions made three steps back.
The fix is simple: structure the work so the agent never has to guess.
The workflow
Step 1: Write the spec before you open Claude Code
The most common large-feature mistake is prompting from intent instead of from a spec. "Build me an OAuth integration" is an intent. A spec describes the exact endpoints, data models, user flows, error states, and edge cases — before any code gets written.
Writing a spec first forces you to resolve ambiguity on paper rather than mid-implementation. Every decision you make in the spec is a decision the agent doesn't have to make by guessing.
Your spec doesn't need to be formal. It needs to answer:
- What does this feature do? (user-facing behavior, not implementation)
- What are the exact inputs and outputs? (API contracts, data shapes, UI states)
- What does success look like? (acceptance criteria, testable behaviors)
- What is explicitly out of scope? (prevents gold-plating)
- What constraints apply? (existing patterns to follow, things not to touch)
Tools like ClearSpec can generate this structure from a plain-language description — useful when you know what you want but need to translate it into something precise enough for the agent to follow.
Step 2: Break the spec into tasks before you prompt
A 500-line spec is not a prompt. It's reference material. Before you open a session, decompose the feature into discrete, independently-testable tasks.
Good tasks have two properties:
- A single output: one file, one endpoint, one component, one migration — not all of them
- A clear done condition: you can verify it's complete without running the whole feature
Bad decomposition:
Task: Implement the billing integration
Good decomposition:
Task 1: Create the Stripe customer record on signup
- Input: user_id, email
- Output: stripe_customer_id stored in users table
- Done: new user has a stripe_customer_id after signup
Task 2: Render the plans page with pricing
- Input: current user plan from database
- Output: /app/plans page listing Free and Pro tiers
- Done: page renders, shows correct plan as "current"
Task 3: Wire the upgrade button to Stripe Checkout
- Input: click on "Upgrade to Pro"
- Output: redirect to Stripe Checkout session
- Done: clicking upgrade redirects to Stripe's hosted checkout page
Task 4: Handle the webhook on successful payment
- Input: Stripe checkout.session.completed event
- Output: user plan updated to "pro" in database
- Done: webhook endpoint updates plan, returns 200
Task 5: Show gated UI to Pro users
- Input: user.plan value from session
- Output: certain UI elements hidden or disabled for free users
- Done: feature gating works, can be verified with test accounts
Five independent tasks. Each one can be prompted separately, verified completely, and committed before moving to the next.
Step 3: Give each prompt three things
When you sit down to prompt a task, every prompt needs exactly three components:
1. The relevant context
Not the whole codebase. Not the full spec. The specific files, patterns, and constraints that are directly relevant to this task.
Context:
- User authentication: src/lib/auth.ts — getAuthenticatedUser() returns the current user
- Database client: src/lib/supabase/server.ts — createClient() for server-side queries
- API route pattern: src/app/api/specs/route.ts — use this as the template for new routes
- Constraint: all database queries must include .eq('user_id', userId) — RLS is active
2. The task from your decomposition
Paste the exact task description. Not a summary. The task as you wrote it in step 2.
3. A done condition
Tell the agent what you'll check to know it's finished.
Done condition: I can run the existing test suite and it passes. The new endpoint
returns a 400 if the amount is missing and a 201 with the record ID if successful.
Full prompt structure:
Context:
[paste relevant file paths and patterns]
Task:
[paste the task from your decomposition]
Done condition:
[paste what you'll check]
Do not touch anything outside the scope of this task. If you need to make a decision
about something outside this scope, note it but don't implement it.
The last line is critical. Large-feature prompts drift because the agent starts to see the whole picture and starts improving things outside the task boundary. That last sentence is the fence.
Step 4: Verify before you continue
Don't start the next task until the current one is verified. This sounds obvious but most people skip it under the assumption that "it looks right."
The verification checklist:
- [ ] Build passes (
next build,cargo build, whatever your stack uses) - [ ] The done condition from your prompt is actually met
- [ ] No new patterns were introduced that contradict your existing codebase
- [ ] No TODO comments left in the code
- [ ] Commit cleanly, with a descriptive message
If the task isn't verified, don't proceed. Continuing on an uncertain foundation compounds the uncertainty — and by task 4, you'll be debugging a tangled mess instead of making progress.
Step 5: Update your CLAUDE.md when patterns emerge
As you implement, you'll discover things: a subtle constraint you didn't capture, a pattern the agent defaulted to that you need to override, a footgun you hit that shouldn't be repeatable.
Add these to your CLAUDE.md immediately. Not after the feature. Right now, while it's fresh.
## Discovered during billing integration
- Stripe webhook signature must be verified with the raw request body, not the parsed body
- The webhook handler must be excluded from the CSRF middleware — see src/middleware.ts
- Test mode: use STRIPE_WEBHOOK_SECRET_TEST in local env
Your CLAUDE.md is a living document that makes every future session more accurate. Here's how to write one that actually controls the agent.
Step 6: Start a fresh session between major phases
Context windows don't just fill up — they degrade. Early messages receive less attention as the window grows. A decision you made in message 3 may effectively be invisible by message 30.
For large features, treat each major phase as a new session:
- Phase 1 (data layer): one session
- Phase 2 (API layer): new session, briefed from the committed code
- Phase 3 (UI layer): new session, briefed from the API contracts
When you start a new session, don't summarize the previous session from memory. Brief the agent from the committed code and the spec:
We're continuing implementation of the billing integration. Here's where we are:
- Task 1 (Stripe customer on signup): done — see src/lib/stripe.ts
- Task 2 (plans page): done — see src/app/app/plans/page.tsx
- Task 3 (checkout session): done — see src/app/api/billing/create-checkout/route.ts
Next task: [paste task 4]
Brief from reality (the committed code), not from memory.
What this looks like in practice
Here's the full sequence for a medium-sized feature:
- Before opening Claude Code: write the spec, break it into 5–8 tasks
- Task 1: context + task + done condition → verify → commit
- Task 2: context + task + done condition → verify → commit
- New session (if context is getting long): brief from code → continue tasks
- Task N: context + task + done condition → verify → commit
- After each session: update CLAUDE.md with anything discovered
The overhead of this workflow is real — about 20–30 minutes of upfront structure for a medium feature. The payoff is also real: implementations that match your codebase, no scope drift, no half-built abstractions, and a clean git history that tells a coherent story.
The underlying principle
Every shortcut in this workflow is a transfer of decision-making from you to the agent. And the agent makes decisions based on its training data, not your codebase, your constraints, or your intent.
The workflow above is essentially a way of structuring context so the agent never has to guess. The better your spec, the cleaner your task decomposition, the more precise your prompts — the less the agent has to fill in, and the closer the output is to what you actually wanted.
ClearSpec automates step 1 — generating structured specs from plain-language descriptions, so you can move straight from "here's what I want to build" to a task decomposition. If you're finding that the spec-writing step is the bottleneck, it's worth trying.
If you're using Cursor instead of Claude Code, the same workflow applies — just replace CLAUDE.md with .cursorrules. Here's a practical guide to writing .cursorrules that actually work.