AI Tools
Prompting Code Models for Reliable, Reviewable Output
Code generation is a different beast from chat. These prompting patterns keep generated code small, testable, and honest about its uncertainty.

Generating code with a language model is easy. Generating code you would merge is not. The difference is almost always in the prompt — specifically, in the constraints the prompt imposes on the output.
This article collects prompting patterns that consistently produce reviewable, low-risk code.
Give the model a contract, not a wish
The single most effective change is to replace open-ended requests with a concrete contract. Compare:
// Vague
Write a function to validate a user.
// Better
Write a TypeScript function with this signature:
export function validateUser(input: unknown): ValidationResult
Where:
type ValidationResult =
| { ok: true; user: User }
| { ok: false; errors: string[] }The second prompt removes a universe of reasonable-but-wrong interpretations. The model now has a target type to satisfy, which constrains both the logic and the return shape.
Ask for the tests alongside the code
When you ask for code and tests in one response, the model is forced to make its own assumptions explicit. Anything it cannot test, it tends not to invent.
// Request: implement parseConfig AND three tests,
// including one for malformed input.A useful side effect: if the model produces tests that do not actually exercise the code, you have learned something important about that particular interaction — and you should not trust the implementation either.
Forbid what you do not want
Negative constraints are underrated. Explicitly forbid the failure modes you have seen before:
- "Do not add new dependencies."
- "Do not use
any; narrow types instead." - "Do not add error handling beyond re-throwing with context."
Models follow explicit prohibitions more reliably than they infer implicit preferences.
Make uncertainty cheap
The worst output is confident nonsense. You can reduce it by giving the model permission to refuse:
If any requirement is ambiguous, list the ambiguity at the top and implement the most conservative interpretation. Do not guess silently.
This single instruction converts silent hallucinations into visible questions — which you can then answer.
Keep generation small
Large generations drift. A 300-line blob is far more likely to contain a subtle bug than three 80-line functions generated separately with explicit types between them. Treat each prompt as a function call: one responsibility, a clear input and output.
Takeaways
- Specify the type signature before the behaviour.
- Request tests in the same response to surface assumptions.
- Forbid known failure modes explicitly.
- Give the model permission to say "I am not sure."
Reliable code generation is less about cleverness in the prompt and more about engineering discipline applied to the request itself.