6.2 Define constraints: language, runtime, libraries, and style
Overview and links for this section of the guide.
On this page
Why constraints are the secret sauce
If you want consistent, shippable code, constraints matter more than clever wording. Constraints tell the model what must be true, so it stops guessing:
- which language/runtime to target,
- what libraries are allowed,
- how code should be structured,
- what files are in scope,
- what “safe” means for this task.
Constraints are your contract with the model. Without a contract, it invents one.
Types of constraints (what to include)
You don’t need every constraint every time. But these are the ones that buy the most reliability:
1) Language and runtime
- Language + version (e.g., Python 3.11, Node 20, Go 1.22).
- Execution environment (CLI, browser, serverless, container).
- OS assumptions (Windows vs macOS/Linux path handling).
2) Dependencies (allowed and forbidden)
- Explicit allowlist (standard library only / specific packages only).
- Explicit forbidden list (no new deps; no frameworks; no network calls).
- Reasoning: “Why we’re restricting deps” (speed, audit, simplicity).
3) Style and code quality
- Naming conventions, formatting expectations, error handling style.
- “Readable over clever” is a useful constraint when paired with examples.
- Whether you want docstrings/comments (and how much).
4) Safety and security constraints
- No secrets in prompts/logs.
- No dynamic code execution (
eval/execon user input). - Least privilege for tools/APIs.
- Input validation rules (length limits, allowlists, schemas).
5) Verification constraints
- “Add tests for new behavior.”
- “Existing tests must continue to pass.”
- “Include commands to run locally.”
How specific should constraints be?
The model can only follow constraints you make explicit. The trick is to be specific where it matters, and intentionally vague where you want flexibility.
Be specific about:
- the runtime and dependency rules,
- file boundaries (“only change these files”),
- inputs/outputs and error behavior,
- acceptance criteria and examples.
Be flexible about:
- internal helper function names,
- small structure choices inside a module,
- implementation details that don’t affect the contract.
It’s easier to loosen a constraint later than to undo a sprawling, unconstrained first draft.
Allowlists beat vague “best practices”
“Use best practices” is not a constraint. It’s a request for the model’s opinion.
Instead, use allowlists and explicit rules:
- Good: “Standard library only; no external dependencies.”
- Good: “Only modify
src/foo.tsandsrc/bar.ts.” - Good: “Output diff-only changes.”
- Bad: “Make it production ready.”
- Bad: “Use modern architecture.”
Most “AI-generated code problems” are really “unexpected dependency/architecture problems.” Allowlists prevent that.
Scope control: files, modules, and boundaries
For code changes, scope is one of your strongest tools.
- File scope: “Only touch these files.”
- Module boundaries: “Do not move business logic into the CLI layer.”
- Public API stability: “Do not change exported function signatures.”
- Diff size: “Keep the diff under ~100 lines unless necessary.”
Unbounded scope leads to large diffs, ambiguous review, and fragile output. Constrain scope aggressively and expand only when needed.
A constraints-first prompt template
Task:
[1–3 sentence description of what to build/change.]
Constraints:
- Language/runtime: [...]
- Dependencies: allowed [...], forbidden [...]
- Files in scope: [...]
- Non-scope: [...]
- Style: [...]
- Security: [...]
Acceptance criteria:
- [...]
Output requirements:
- If changing existing code: output diff-only changes
- Keep changes minimal and explain tradeoffs
Before coding:
1) List assumptions and ask questions if needed
2) Propose a short plan
3) Wait for confirmation
Common mistakes (and fixes)
Mistake: not specifying runtime/version
Fix: always include language + version. It prevents subtle incompatibilities.
Mistake: letting the model pick dependencies
Fix: explicitly allowlist dependencies, especially for prototypes that will become real.
Mistake: asking for “clean code” without a definition
Fix: constrain structure (modules, responsibilities) and verification (tests). That produces “clean” as a side effect.