How to actually work with a coding agent
A coding agent is not a vending machine you drop a wish into. It's a fast, literal, tireless junior engineer with no memory of yesterday and no instinct for your codebase. The difference between people who ship with agents and people who fight them all day is almost entirely about how you set up the work, not which model you pay for. This is the playbook.
The mental model: it's a brilliant amnesiac, not an oracle
The single most useful reframe is that the agent knows everything about programming in general and nothing about your project in particular until you tell it. It has read more Stripe integrations than any human alive, but it has not read yours. It will confidently invent a config option, a database column, or an npm package that sounds exactly right and does not exist. This isn't the model being dumb — it's pattern completion filling a gap you left open. Your job is to close those gaps before it guesses: point it at the real file, paste the real error, name the real version. Treat every confident claim about your specific code as a hypothesis to verify, not a fact to trust.
Give it a map before you give it a task
The highest-leverage thing you can do is write a CLAUDE.md (or AGENTS.md / .cursorrules — same idea) at the root of your repo. This is the file the agent reads first every session, and it's where you encode the things it would otherwise have to rediscover or get wrong: how to run the app, how to run tests, which package manager you use, what the folder structure means, and the three conventions you actually care about. Keep it short and specific — a wall of generic 'write clean code' platitudes wastes the context budget and gets ignored. The test of a good CLAUDE.md: a new human engineer could get the project running from it in five minutes. Bad rules are vague ('handle errors properly'); good rules are checkable ('all API routes return the { data, error } shape; never throw past the route boundary').
# CLAUDE.md
## Commands
- Dev: `pnpm dev` (Next.js on :3000)
- Test: `pnpm test` (Vitest), single file: `pnpm test path/to.test.ts`
- DB: Drizzle. Migrate with `pnpm db:push`. Never hand-edit migrations.
## Conventions
- Auth is Clerk. Get the user via `auth()` from `@clerk/nextjs/server`, never re-implement.
- All money is integer cents. Never floats.
- Server actions live in `app/**/actions.ts` and must call `assertAuthed()` first.Scope one task, verify, then move — don't dump the backlog
The reflex from delegating to humans is to hand over a big ambiguous goal and trust them to fill in judgment. Agents are the opposite: they do best with a tightly scoped task and a clear definition of done, and they degrade badly when a single request spans auth, database, UI, and deploy all at once. A sprawling ask produces a sprawling diff you can't review, and if one part is wrong the whole thing is suspect. Work in units you can actually verify in a minute or two: 'add the email column and migration,' then 'send the welcome email on signup,' then 'add the test.' Each step ends with you (or the agent) actually running something and confirming it works before the next step builds on it. Small verified steps compound; big unverified leaps collapse.
The context window is a desk, not a warehouse
Everything the agent 'knows' right now lives in a finite context window, and it's more like a cluttered desk than a filing cabinet — put too much on it and important things get buried or knocked off. A 200-file paste, three abandoned debugging attempts, and a giant log dump all compete with the actual task for the model's attention, and quality drops measurably as the window fills with noise. Practical consequences: start a fresh session when you switch to an unrelated task instead of letting one thread run for hours; let the agent read files itself rather than pasting entire files it may not need; and when a debugging session has gone in circles three times, don't keep pushing — reset, summarize what you learned in a sentence, and start clean. A stuck agent almost never un-sticks by trying harder in the same polluted context.
Make it prove the work — tests and running output are your steering wheel
Agents are graders of their own homework by default, and they're generous graders. 'I've updated the code and it should now work' is not evidence; a passing test or actual program output is. The most reliable pattern is to insist the agent runs what it wrote and shows you the result — the failing test going green, the curl response, the screenshot. When you have a bug, the agent will fix it far faster and more honestly if it can reproduce it: give it the exact command and the exact error, and better yet ask it to write a failing test first, then make it pass. This also flips the incentive — instead of the agent pattern-matching a plausible-looking fix, it has a concrete target it can check itself against. Tools like Playwright for UI flows and Vitest for logic turn 'trust me' into 'here's the green checkmark.'
Read the diff like you'll be on call for it — because you will
The most dangerous failure mode of vibe coding is accepting code you don't understand because it looks confident and the app didn't crash. When it breaks in production at 2am, the agent isn't on the hook — you are. So review every diff, and when the agent does something you don't understand, ask it to explain that specific choice in plain language before you accept it: 'why a transaction here?', 'what happens if this fetch fails?'. Watch specifically for the things agents get subtly wrong: missing error handling on the unhappy path, auth checks that got dropped in a refactor, secrets or keys hardcoded instead of read from env, N+1 database queries, and 'helpful' changes to files you didn't ask about. You don't need to be able to write the code from scratch, but you do need to understand what it does and be able to say why it's there.
When it goes sideways, change the setup, not just the wording
When an agent keeps getting something wrong, the instinct is to re-roll the same prompt with slightly angrier phrasing. That rarely works, because the problem is usually structural: it doesn't have the information it needs, or it's working against a context full of dead ends. Better moves, roughly in order: give it the missing fact (the real API docs, the actual type definition, the version number); reset the session if the thread is polluted; break the task smaller so the failure surface shrinks; and ask it to explain its current understanding so you can catch the wrong assumption directly. A useful trick is to have it write the plan before it writes the code — if the plan is wrong, you've caught it in ten seconds instead of after a 400-line diff. And know when to stop delegating: some 15-minute problems are faster to just edit by hand than to negotiate through three agent rounds.
Build the guardrails that let you move fast without looking
The reason experienced people can let an agent run with a long leash isn't blind trust — it's that they've set up automated safety nets so mistakes get caught cheaply. Commit constantly and in small units so any bad change is one `git revert` away and you can always see exactly what changed. Wire up a linter, type checking (TypeScript is a massive agent multiplier — it turns a whole class of hallucinated method calls into instant red squiggles the agent can see and fix), and CI so wrong code fails loudly before it merges. Never let an agent operate directly against production data or with live payment and email keys; use test keys and a throwaway database. The paradox is real: the more disciplined your guardrails, the less you have to micromanage the agent, because the environment itself rejects the bad outcomes you were worried about.
Takeaways
- →The agent knows programming in general but nothing about your project — close the information gaps before it guesses.
- →A short, specific CLAUDE.md is the highest-leverage thing you can write; encode commands and real conventions, not platitudes.
- →Scope small, verify each step by actually running something, then move on — never dump the whole backlog at once.
- →Passing tests and real program output are the only evidence that counts; 'it should now work' is not.
- →Review every diff as if you'll be on call for it, because you will be — understand what it does even if you couldn't write it.
- →When it's stuck, fix the setup — add the missing fact, reset the context, shrink the task — don't just re-word the prompt.