Prompt patterns for coding agents
Coding agents like Claude Code, Cursor, and Codex don't fail because the model is dumb — they fail because you gave them a wish instead of a spec. This is the craft of turning "make it work" into instructions an agent can actually execute, verify, and not wander off from. Learn these patterns and your hit rate goes from "roll the dice" to "ship it."
The core mental model: you're writing a spec, not chatting
A coding agent is a very fast junior engineer with no memory of your intentions and infinite confidence. Every ambiguity you leave is a decision it makes for you — usually the most generic one. The single highest-leverage shift is to stop typing requests ('add auth') and start writing specs: what the change is, where it lives, what 'done' looks like, and what it must NOT touch. The prompt is the contract. If the outcome is wrong, 90% of the time the contract was underspecified, not the model. Treat your first prompt like a ticket you'd be embarrassed to hand a contractor without acceptance criteria.
Weak: "Add login to the app."
Strong: "Add email+password login using Clerk (already installed).
- Route: /login, server component + client form island
- On success redirect to /dashboard; on failure show inline error
- Protect /dashboard/* via middleware.ts
- Do NOT touch the existing marketing pages or the Stripe code
- Done when: I can sign up, log out, log back in, and hitting
/dashboard while logged out redirects to /login"Anchor every task in real files before asking for changes
Agents hallucinate architecture when they don't know yours. The fix is cheap: name the files. 'Read src/db/schema.ts and src/lib/auth.ts, then...' forces the agent to ground itself in your actual code instead of inventing a plausible-but-wrong structure. This one habit kills the most common failure mode — the agent writing a beautiful feature against a database schema or API that doesn't exist. If you don't know which files matter, make that the first task: 'Find where user sessions are created and list the files involved. Don't change anything yet.' Reconnaissance before surgery.
"Read these before writing anything:
- drizzle/schema.ts (the users + orgs tables)
- src/lib/stripe.ts (existing customer creation)
Then add a `plan` column to users and backfill it to 'free'.
Match the existing migration style in drizzle/ — don't invent a new one."Plan-first: make the agent think before it writes
For anything non-trivial, split the work into two turns. First: 'Give me a plan — the files you'll change, the approach, and anything ambiguous you need me to decide. Do not write code yet.' Read it. The plan is where you catch the agent about to rip out your caching layer or add a redundant dependency, and it costs you 20 seconds instead of a 400-line diff you have to unwind. Then: 'Good, but use Resend instead of nodemailer, and skip the retry logic for now. Proceed.' This is the biggest quality lever most people skip because it feels slower. It's not — reviewing a plan is far faster than reviewing wrong code, and correcting a paragraph is far cheaper than correcting a commit.
Give it a way to check its own work
An agent that can run your tests, typecheck, and lint will self-correct in a loop you don't have to babysit. An agent that can't is guessing. Always tell it how to verify: 'After each change, run `npm run typecheck` and `npm test -- auth` and fix what breaks before moving on.' Better yet, give it an end-to-end way to observe reality — a Playwright script, a curl command, a script that hits the endpoint. The magic words are 'don't tell me it works, show me the passing output.' Models are trained to sound done; the terminal doesn't lie. If your project has no fast feedback loop, building one is the highest-ROI thing you can do for agent-driven development, full stop.
"Implement the /api/webhook handler for Stripe.
Verify by running: `npm run dev` then
`stripe trigger checkout.session.completed` against localhost.
Show me the logged event and the DB row it created.
If nothing is written to the DB, keep debugging — don't stop."Constrain the blast radius explicitly
Left unbounded, agents refactor. They'll 'improve' adjacent code, rename things, upgrade a dependency, reformat a file — and now your one-line fix is a 30-file diff you can't review. Fence the work: 'Change only src/lib/pricing.ts. Do not touch any other file, do not reformat, do not add dependencies without asking.' The negative constraints matter as much as the positive ones. Pair this with small commits: 'Make this one change, then stop so I can review before we continue.' Scope creep in an agent is silent and fast — the diff is the only place you'll catch it, so keep the diff small enough to actually read.
Persistent context beats repeating yourself: use CLAUDE.md / rules files
Every serious agent tool reads a project instructions file — CLAUDE.md for Claude Code, .cursor/rules for Cursor, AGENTS.md as the emerging cross-tool convention. Put the things you'd otherwise retype every session there: the package manager (pnpm not npm), the test command, 'we use Drizzle, never raw SQL', 'server components by default', 'no default exports'. This is how you stop fighting the same battles daily. Keep it short and imperative — it's a system prompt, not documentation. A bloated rules file gets ignored; ten sharp lines get followed. Update it the moment you catch the agent making the same wrong assumption twice — that's the signal it belongs in the file.
# CLAUDE.md
- Package manager: pnpm. Never use npm/yarn.
- Stack: Next.js App Router, Drizzle ORM, Clerk, Tailwind.
- Tests: `pnpm test`. Typecheck: `pnpm typecheck`. Run both before finishing.
- Prefer server components. Client components only when they need state/effects.
- Never write raw SQL — use Drizzle query builder.
- Never commit .env or touch billing code without asking.Debugging: give evidence, not adjectives
'It's broken' makes the agent guess. Paste the actual error, the stack trace, the failing input, and what you expected versus what happened. Then constrain the method: 'Find the root cause before proposing a fix. Add a log line if you need to confirm your hypothesis, then show me why it fails.' Agents love to pattern-match a plausible fix onto a symptom and declare victory — the counter is forcing a diagnosis step. For heisenbugs, tell it to reproduce first: 'Write a failing test that captures this bug, confirm it fails, then fix it, then confirm it passes.' A bug that isn't reproduced isn't fixed, it's just quiet.
"This 500s on checkout. Error:
TypeError: Cannot read 'id' of undefined at pricing.ts:42
Input: cart with a deleted product still in it.
Expected: skip missing products, don't crash.
Find the root cause first, explain it, THEN fix. Add a regression test."When it goes sideways, restart clean — don't argue
There's a point where a conversation is poisoned: the agent has a wrong idea baked into context and every correction just adds more contradictory instructions on top. Once you've corrected the same thing twice with no improvement, stop. Start a fresh session with a better first prompt that bakes in everything you learned. Long, thrashing threads produce worse code than a clean start with a sharp spec — the context window fills with dead ends the model keeps re-reading. Think of it as cheap: you're not losing work, you're distilling three messy turns into one good opening. The best vibe coders bail early and re-spec rather than negotiate with a confused agent.
Takeaways
- →A prompt is a spec, not a wish — ambiguity becomes the agent's decision, and it picks the generic option.
- →Name real files before asking for changes; grounding kills the biggest failure mode (coding against imaginary structure).
- →Ask for a plan first — reviewing a paragraph is far cheaper than unwinding a wrong diff.
- →Give the agent a way to verify (tests, typecheck, e2e) and demand shown output, not claims of success.
- →Fence the blast radius with explicit negative constraints and small, reviewable commits.
- →Put durable rules in CLAUDE.md / AGENTS.md; when the agent misfires twice, restart clean instead of arguing.