AI Prompts

Prompt Engineering for Coding Agents: Make Claude Code / Codex a Reliable Collaborator

Getting a coding agent right on the first try is not about the model but the instruction quality. This guide explains context constraints, minimal-change rules, test-first practice and review prompts, so you can craft reproducible prompts with fewer hallucinations.

By LaoHand Team·8 min read·Updated 2026-09-06

Separate the Goal from the Constraints Instead of a One-Liner Requirement

A one-line requirement is behind most failures: "optimize this code" invites the tool to improvise. The mental model is to split the prompt into goal (the final behavior to achieve) + constraints (invariants, what is off-limits, which tests must pass) + acceptance (what counts as done).

Constraints are your main hallucination defense: state clearly "introduce no new dependencies", "only touch src/foo.ts", "keep the existing function signature" and "must not break existing tests". The more concrete the constraints, the less the tool overreaches with refactors.

Acceptance criteria matter too — "pnpm typecheck returns 0, eslint clean, all related tests green" — giving the agent a checkable endpoint rather than "looks fixed".

## Goal
Change the orders list to server-side pagination while keeping the current API response shape.
## Constraints
- Only change server/ and the matching interface-layer files; do not touch the front end
- Do not add new dependencies; reuse the existing SQLhelper
- Keep existing function signatures and error codes
## Acceptance
- pnpm typecheck returns 0
- Relevant API tests all green

Feed Enough Context, but Not the Whole Repo

An agent can only judge on what you feed it; too little context makes it start guessing. But pasting thousands of lines washes out attention and raises hallucination. The trick is "index plus key snippets": tell it the project layout, the data-flow entry points, and the path and rough responsibility of the file you want changed.

Fix a "project quick-view", such as a one-page CONTEXT.md covering: directory purpose, build command, test entry, and where common type definitions live. Refer to it on every task; it measurably cuts the odds of the agent landing in the wrong file.

If domain knowledge matters (cache strategy, auth flow), pull it out explicitly instead of letting the model search for it — search-style recall is more uncontrollable.

# CONTEXT.md (a one-page overview the team maintains)
## Layout
- src/api/   request layer, outbound calls use http-client
- src/domain/ business rules, including the order state machine
## Commands
- test: pnpm vitest run
- typecheck: pnpm run typecheck
Cite this at the start when assigning a task:
"Read CONTEXT.md first to understand the structure, then implement X."

Constrain Behavior with Test-First Instructions, Not Verbal Emphasis

To make agent-written code stand up, the most effective instruction is not "please write carefully" but baking verification in: "first write unit tests for this function, then implement until the tests pass". Test-first constrains both behavioral boundaries and acceptance criteria at once.

Give the exact test entry and the expected assertion style. In a Nuxt/Vite project using vitest, say organize with describe/it and prefer expect(..).toEqual. That keeps the produced tests stylistically uniform and easier to maintain later.

For large tasks, have the agent plan first: an implementation plan (which files to create/modify, dependency order), and only execute after you approve. The plan is an alignment step in advance, far cheaper than reworking mid-flight.

"First write unit tests for applyDiscount in src/domain/discount.ts
(describe/it, assert with toEqual); make them fail first;
then implement until all green. Finally paste the test output."

Give a Wrong-versus-Fixed Contrast to Train Accurately Rather Than by Trial

When you want to steer it away from a common code smell, abstract phrasing transfers poorly while a concrete counter-example lands. Feed both a wrong and a fixed snippet, clearly marking which is bad and why; the agent then tells the boundary apart better.

This also works for style constraints: give it a real sample of "this project’s accepted practice" as an anchor to follow, rather than the vague "keep the style consistent".

An annotated example usually beats a whole page of abstract rules. The reason is an agent reconstructs your preferences from concrete instances, rather than guessing at them.

# Anti-pattern: read the env var inside the callback, so tests cannot switch the value
const onReady = () => doLogic(process.env.FLAG === "1");
# Fix: inject the flag as a parameter, easy to test and replace
const onReady = (flag: boolean) => doLogic(flag);
# Instruction: "Avoid pattern A; follow pattern B."

Use Acceptance Self-Check and Minimal Change as the Delivery Gate

Delivery is not about lines changed but about passing acceptance. A standard delivery instruction should include the commands the agent runs itself — typecheck, lint, test — and demand it paste the raw output verbatim, so it cannot self-soothe with "should be fine".

Also hold the minimal-change line hard: if the diff shows unrelated refactors, suspiciously deleted code, or broad formatting reshuffles, have it revert and redo with tighter scope. Requiring a one-sentence explanation per change naturally flushes out motion-for-motion’s-sake.

The final gate is a change note/release note: ask it to add one line on "why changed, which modules affected, whether docs need updating". This thread runs through the whole task, ensuring it is not an isolated edit.

"Once done, run the acceptance checks: pnpm typecheck, pnpm lint, pnpm test,
paste the raw output. List each change + a one-line reason;
confirm the diff is minimal and remove unrelated refactors.
Add one line: affected modules + whether docs need updating."

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.