Prompting for intent, not syntax
Describe the outcome, the constraints and the definition of done, and iterate on the prompt instead of discarding it and starting again.
Four parts of a prompt that works
A prompt that describes syntax is a prompt that fights the model on its strongest ground. Describing intent, constraints and the definition of done leaves the implementation to the part of the system that is good at implementation, and keeps the decision-making where you can see it.
| Part | Answers | Example |
|---|---|---|
| Outcome | What should be true afterwards | "A user can reset a password by email" |
| Constraints | What must not change | "Do not touch the auth middleware" |
| Context | Where the relevant code lives | "Look at src/auth and the User model" |
| Done | How we will both know it worked | "A test where an unknown email still returns 200" |
Weak:
"write a password reset function"
Better:
"Add password reset by email.
Outcome: POST /auth/reset accepts an email and always returns 200,
whether or not the account exists.
Constraints:
- Reuse the existing token helper in src/auth/tokens.ts.
- Do not change the login route or the users table shape.
- No new dependencies.
Context: look at src/auth and src/mail.
Done when: a test proves an unknown email returns 200 with an identical
body to a known one, and the reset link expires after 30 minutes."💡
The 'always returns 200 either way' clause is the kind of requirement a model will not infer. It also happens to be the security-relevant one. Anything you leave out of the prompt is a decision the agent makes for you, silently.
Give evidence, not adjectives
- Paste the real error text. 'It is broken' makes the agent guess; a stack trace makes it look in the right file.
- Name the file and the function. A path costs four tokens and saves a repository-wide search.
- Say what you already tried and what it did, so the agent does not repeat your last three attempts.
- Quote the exact expected output when the shape of the answer matters.
- If you have a screenshot or a failing test name, include it - both narrow the search enormously.
Bad:
"the checkout is broken, please fix"
Good:
"Checkout fails at the payment step.
Command: npm test -- checkout.test.ts
Output:
expect(received).toBe(200)
Expected: 200
Received: 422
at src/checkout/pay.test.ts:41
Tried: re-running, clearing the cart. Same failure.
The 422 comes from validateCart in src/checkout/validate.ts.
Figure out why a cart that passes the UI validator fails here,
then fix it. Do not change the test."The last sentence matters. Without it, the fastest route to a green test is to weaken the test, and the agent will take the fastest route.
Iterate on the prompt, do not restart
- Read what came back before re-prompting. Half of iteration is noticing which constraint was ignored.
- Correct the specific thing: 'you changed the users table, which I said not to touch - revert that part'.
- If two corrections have not converged, the prompt is ambiguous. Rewrite it completely rather than adding a third correction.
- Ask for the plan when the change is large, then approve it step by step.
- Save prompts that worked. A prompt that produced a good result is a reusable asset, not a throwaway.
> That is close, but two problems:
> 1. The token helper now takes a third argument. I said not to change
> the existing signature - add an overload instead.
> 2. The test asserts on the response body. Assert on status only;
> the body is deliberately identical.
> Keep everything else you did.| Signal | Meaning | Action |
|---|---|---|
| It ignored one constraint | Prompt was long, constraint got lost | Restate it last, alone |
| It invented a file | It never read the real one | Give the exact path |
| It solved a different problem | Outcome was ambiguous | Rewrite the outcome clause |
| It works but the design is wrong | Constraints were too weak | Add what must not change |
FAQ
How long should a prompt be?
As long as the constraints genuinely are, and no longer. A four-part prompt for a real feature is usually a paragraph. If you are writing three pages, the task is too large for one request - split it and prompt for the first step.
Should I tell the agent which library to use?
Yes, when the choice matters and is already made in your codebase. 'Use the existing fetch wrapper in src/lib/http.ts' prevents an invented second HTTP client. When the choice does not matter, leaving it open gives you a more conventional answer.
Related
The generate, run, correct loop Choosing a stack that agents handle well
Last refreshed 2026-09-18.