The most important thing you write when working with an AI coding agent isn't code. It's the spec.
A tight spec produces clean, focused work. Claude reads it, understands the boundaries, and executes within them. A vague spec produces wandering — the agent guesses at your intent, makes assumptions, and builds something that looks right but misses the point.
I've run hundreds of tasks through Claude Code at this point. The pattern is clear: the quality of the output is a direct function of the quality of the input. And the input isn't a prompt. It's a spec.
The three parts of a good spec
Every spec I write has three sections. No more, no less.
What
The end result you want. Describe the outcome, not the implementation steps. Let the agent figure out the how — that's what it's good at.
Constraints
What NOT to touch. What patterns to follow. What to avoid. This is where you prevent the agent from making "creative" decisions you'll have to undo.
Acceptance criteria
How to verify it's done. Concrete, testable conditions. "The build passes." "The test covers edge case X." "The page renders Y." No subjective language.
That's it. What, Constraints, Acceptance criteria. If your spec has these three things and nothing else, it's probably good enough.
Bad spec vs good spec
The difference is night and day. Here's a real example.
Add authentication to the app.
What kind of auth? Where does it go? What provider? What pages need protection? What happens after login? Claude will answer all of these questions by guessing. And its guesses won't match your intent.
Add magic link auth via Supabase.
What:
- Users enter email on /auth/login
- They receive a magic link via Supabase Auth
- On click, they're redirected to /admin
Constraints:
- Don't modify existing components
- Use @jd/ui components for the login page
- Use @jd/db/server for the Supabase client
- Follow the existing middleware pattern in middleware.ts
Acceptance criteria:
- /admin/* routes redirect to /auth/login when not authenticated
- After magic link click, user lands on /admin
- Build passes with no type errors
- Login page matches existing design system
The good spec is 15 lines. It took me two minutes to write. It saved me 30 minutes of corrections, undone changes, and frustrated re-prompting.
Search first, spec second
This is an ECC principle I follow religiously: before writing a spec, research first.
Does a solution already exist in the codebase? Is there an npm package that handles this? An MCP server? A Claude Code skill? The best spec starts with understanding what you already have.
I've watched developers spec entire features from scratch, only to discover that 80% of the work was already done in a utils file they didn't know about. Or that there's a Supabase feature that handles the exact use case. Or that a teammate already built the component three months ago.
> "Search the codebase for any existing auth-related code.
Check middleware.ts, any auth/ directories, and the
Supabase client setup. Don't change anything — just
report what exists."
[Claude reports: middleware.ts has route protection logic,
@jd/db/server has createClient(), no login page exists yet]
> "Good. Now here's the spec..."
The research took 30 seconds. The spec was better for it. Claude didn't have to reinvent patterns that already existed — it could follow them.
A search-first skill can formalize this with a decision matrix. After research, you choose one of four paths:
- Adopt — an existing library or package handles it. Install and configure.
- Extend — existing code handles 60%+. Build on top of what is there.
- Compose — combine 2-3 existing pieces into the solution. Glue code only.
- Build — nothing exists. Spec from scratch.
Most tasks land on Extend or Compose. Building from scratch is the exception, not the default. The research step ensures you know which path you are on before you write a single line of spec.
Plan before execute
For complex features, don't jump from spec to implementation. Use the planner pattern.
Write the spec. Enter plan mode. Let Claude propose a phased breakdown. Review the breakdown. Refine the spec if needed. Only then approve the implementation.
This is the ECC planner agent pattern: the plan includes risk assessment, dependency mapping, and a worked example of expected output. It's overkill for a 5-minute task, but for anything that touches multiple files or critical systems, it's essential.
1. You write the spec (2 min)
2. Claude proposes a plan (30 sec)
3. You review and adjust (2 min)
4. Claude implements the approved plan (10 min)
5. You review the output (5 min)
Total: ~20 min for a feature that would take 2 hours manually
The spec is the interface between your intent and the agent's execution. The plan is the checkpoint between the spec and reality. Together, they give you control without typing every line.
Spec evolution
Your first spec is rarely perfect. And that's fine.
Write it. Let Claude plan. Review the plan. If the plan reveals gaps in your spec — things you forgot to constrain, acceptance criteria you missed — update the spec and re-plan. This loop is normal and fast.
I typically go through 1-2 refinement cycles on complex tasks. The first spec captures 80% of what I want. The plan surfaces the remaining 20%. The second spec captures everything.
The spec is a living document for the duration of the task. Once the task ships, the spec doesn't matter anymore. What matters is that it was tight enough to produce clean work while the agent was running.
This is the fundamental skill of agentic coding. Not prompting. Not coding. Speccing. Get this right and everything else follows.