The unit of work with Claude Code is not a prompt or a diff but a contract of the result. The agent works the better the less the request dictates the algorithm of typing code and the more clearly it describes what the observable behavior should become and how to verify it. A vague "make auth better" gives neither a goal, nor boundaries, nor a criterion - and almost guarantees drift: the agent changes a lot, and there is nothing to check.
A minimal contract consists of five parts. The goal - what observable behavior should change. The context - where the problem shows and how to reproduce it. The boundaries - what must not change and which interfaces to preserve. The verification - a test, a command, a UI scenario or a success metric. The report - changed files, commands, exit codes, risks. These five points turn a wish into a task with an unambiguous sign of completion.
The difference between a weak and a working request is visible in an example. "Make auth better" is not a task. "Fix the refresh-token race in src/auth without changing the public API; first reproduce the bug with a test of two parallel requests, keep the cookie format, after the fix run the focused test, the whole auth suite and typecheck, at the end list the files, commands with exit codes and what remained unchecked" is a contract with a goal, boundaries and verification.
It helps to see the minimal-contract template once, to keep it before your eyes when posing a task. Below are five lines from which any working request is assembled. You return to this template each time your hand reaches to write one vague phrase: five minutes on framing a contract is cheaper than evaluating a beautiful but wrong diff produced by a request with no criterion.
An important rule is not to dictate an unconfirmed solution as fact. If the root cause is unknown and you have already dictated the implementation, the agent will dutifully carry it out even if it misses. It is better to first request exploration without changes: build the chain from the entry point to the problem, name a file and symbol for each transition, find where a race is possible and propose a couple of options with their effect on compatibility. And only then choose.
Discovery and implementation are worth separating, keeping a point of control for yourself. A long request may contain phases - exploration without writing, a plan with affected files, confirmation, implementation, verification, diff review - but the transition between them must stay with you. Plan mode technically limits edits but does not replace a clear task; on the contrary, a good contract is useful in ordinary mode too, where no agent switch holds it back.
The agent is worth giving access to the source of truth rather than its retelling. A reproducible failing test beats the words "sometimes fails", a precise log beats a retelling of the error, an API schema or a type definition beats guesses about the format. A screenshot helps a UI task, but it needs the viewport and the expected behavior. The closer the agent is to the primary data, the less it guesses - and guesses are the main source of beautiful but wrong solutions.
Hence a practical rule and typical failures. If you cannot frame the verification - ask Claude to first propose a checkable criterion and change nothing; that is cheaper than taking apart an unverifiable diff. The typical mistakes: a vague goal with no boundaries, because of which the agent drifts; a wrong solution dictated as fact; a task with no verification criterion, where a fix cannot be told from its appearance. Set a contract, not a wish.
Goal: what observable behavior should change.
Context: where the problem shows and how to reproduce it.
Boundaries: what must not change, which interfaces to preserve.
Verification: a test, a command, a UI scenario or a success metric.
Report: changed files, commands, exit codes, risks.