A well-worded request for too much work produces a worse outcome than a clumsy request for the right amount. Scope dominates wording.
Here is what "the right amount" looks like in practice.
Name the files#
The single most effective constraint is telling the agent where the change lives.
Bad: Add rate limiting to the API.
Better: Add rate limiting to the API. It belongs in
internal/http/middleware.go; the config struct is ininternal/config/config.go. Do not touch the handlers.
The second version does three things. It removes a search step. It removes an architectural decision the agent would otherwise make silently. And it draws a boundary, which means the diff you get back is one you can read in five minutes.
You do not always know which files. When you do not, that is a separate step — ask for the investigation first, get an answer, then make the request. Do not merge the two.
Ask for one thing#
An agent given three tasks will do all three in one commit, and the failure of any one of them contaminates the review of the other two.
Bad: Add the endpoint, write tests for it, and update the OpenAPI spec.
Better: three requests, in that order, each verified before the next.
This feels slower. It is not, because you are measuring the wrong thing. The clock that matters runs from "I asked" to "it is merged and correct", and batched requests spend most of that clock in review and rework.
State the stopping condition#
Without one, the agent decides when it is done, and its criterion is "the task feels addressed".
Give it something checkable:
- Stop when
make checkpasses. - Stop when the new test fails for the right reason — do not implement it yet.
- Stop after the plan. Do not write code.
That last one deserves its own paragraph. Asking for a plan and nothing else is the highest-value scoping move available to you. It costs one cheap turn, it surfaces the wrong assumption before any code exists, and the correction takes one sentence instead of a revert.
Say what not to touch#
Agents are enthusiastic. Left alone, one will reformat a file, tidy an unrelated import, rename a variable it finds unclear, and add a comment above your function.
None of that is wrong on its own. All of it is noise in a diff you are trying to read for correctness.
Do not reformat. Do not rename anything I did not mention.
Do not add comments to existing code.Put this in the memory file once rather than in every prompt.
A worked example#
Here is a real-shaped request that follows all four rules:
The
/ordersendpoint returns 500 whensinceis an invalid date instead of 400.The handler is
internal/http/orders.go. Parsing happens inparseOrderQuery. Return a 400 with the existingErrBadRequestshape — seeinternal/http/errors.gofor the pattern.Write the failing test first in
orders_test.goand show me it failing before you fix it. Do not touch anything else.
Everything an agent needs to do this correctly is in five lines: the bug, the location, the expected behaviour, the existing pattern to follow, the sequence, and the boundary.
The diff that comes back will be perhaps thirty lines across two files, and you will be able to tell whether it is right by reading it once.
When to break the rules#
Exploratory work is different. If you genuinely do not know what the change should be, a wide, unbounded request is a reasonable way to see options — as long as you treat the output as a sketch and throw it away.
The mistake is letting an exploratory result become the implementation because it looks finished.
Next: how to read what came back — the review pass that catches the most, fastest.