·

How to Write Better Prompts for AI Coding Agents

ai-coding prompt-engineering workflow best-practices tutorial

One effective prompt format for an AI coding agent is a small, complete task brief: a clear outcome, the context needed to make good decisions, explicit boundaries, and a verifiable definition of done.

A vague prompt such as “add caching” leaves the agent to infer the scope or ask clarifying questions. A stronger prompt says where caching belongs, which behavior must remain unchanged, what invalidates the cache, and how the result should be verified. That extra minute of specification can prevent avoidable correction rounds.

This guide gives you a reusable prompt structure, examples, and a checklist for writing AI coding tasks that produce focused, reviewable code.

The five parts of a strong coding-agent prompt

This template uses five useful elements:

  1. Outcome — what should be true for the user or system when the task is complete
  2. Context — the relevant files, patterns, decisions, and current behavior
  3. Constraints — what the agent may change and what it must preserve
  4. Verification — the tests, commands, or manual checks that demonstrate correctness
  5. Definition of done — the exact deliverables expected at the end

This structure is consistent with current vendor guidance: Anthropic recommends clear, explicit instructions and relevant context, while OpenAI recommends precise coding instructions and thorough testing.

These are not five paragraphs you must fill with prose. For a small task, each can be one sentence. The goal is to remove consequential ambiguity without dictating every line of code.

Here is the basic template:

Task: [one-sentence outcome]

Context:
- [where the relevant code lives]
- [existing pattern or behavior to follow]
- [why the change is needed]

Constraints:
- [files or interfaces that must not change]
- [compatibility, security, or UX requirement]
- [keep the change limited to this task]

Acceptance criteria:
- [observable behavior 1]
- [observable behavior 2]
- [important edge case]

Verification:
- Run: [focused test or check]
- Run: [broader regression check]

Deliverable:
- Implement the change, add or update tests, and summarize the files changed.

This plain-text template works across Claude Code, Codex CLI, Copilot CLI, Antigravity CLI, and supported Gemini CLI configurations, but adapt it to each tool’s repository-instruction and context mechanisms.

Start with the outcome, not the implementation

Describe the result before proposing code. “Add a Redis cache around getUser() sounds precise, but it silently commits to a particular solution. The actual need may be “keep p95 profile-page server response time below 300 ms in the agreed staging load test when identity-provider latency is simulated at 200 ms.” Once the agent can see the outcome, it can inspect the repository and determine whether Redis, an existing in-memory cache, request deduplication, or a smaller query fits the codebase.

A useful outcome is observable:

  • Weak: “Improve the settings page.”
  • Better: “Prevent the settings form from losing unsaved changes when the user switches tabs.”
  • Weak: “Refactor authentication.”
  • Better: “Remove duplicated token-refresh logic while preserving the public auth API and current error behavior.”

Implementation instructions still belong in a prompt when a decision is already settled. If the team has chosen Redis, say so. Just separate the required result from the chosen constraint so the agent knows which parts are fixed and which parts still require judgment.

Give context by pointing, not pasting

Agents need repository context, but copying thousands of lines into a prompt creates noise and can make the important instruction harder to find. Point to the smallest useful set of files instead:

Follow the validation pattern in src/routes/account/update-email.ts.
The shared error types are in src/lib/errors.ts; reuse them rather than adding a new error shape.
The regression is covered partially by tests/account/update-email.spec.ts.

This gives the agent a starting map while still letting it inspect imports and nearby conventions. Include the facts it cannot discover from code: product intent, a rejected design, a production constraint, or the reason backward compatibility matters.

Good context answers three questions:

  • Where should the agent begin reading?
  • Which existing pattern is authoritative?
  • What important decision is not visible in the repository?

Avoid a full history lesson. If a detail does not change implementation or verification, it probably does not belong in the task prompt.

Set boundaries that keep the diff reviewable

AI coding agents are capable of making broad changes quickly. That is useful only when the breadth matches the task. Tell the agent where the boundary is.

Useful constraints include:

  • Do not change the public function signature.
  • Limit edits to src/billing/ and its tests.
  • Use dependencies already in package.json.
  • Preserve the current database schema.
  • Do not rename unrelated variables or reformat untouched files.
  • Ask before adding a migration or a new runtime dependency.

File boundaries are especially important when you are running multiple AI agents on one repository. Two agents can edit separate working copies in separate git worktrees, reducing direct checkout collisions. Worktrees still share Git objects, most refs, and repository configuration by default, and their branches can conflict later if both prompts send them into the same central files. Good task boundaries reduce integration conflicts as well as prompt ambiguity.

Do not overconstrain the implementation. A prompt that specifies every function name, loop, and local variable turns the agent into a slow typist and transfers all design risk back to you. Fix the interfaces and invariants that matter; leave local implementation choices open unless you have a reason to control them.

Write acceptance criteria as observable behavior

“Make it robust” is not an acceptance criterion. Neither is “handle edge cases.” Name the behavior you expect, including the one or two edge cases most likely to break.

For example, instead of this:

Add CSV export and make sure it works well.

write this:

Add CSV export to the orders page.

Acceptance criteria:
- The export contains the rows visible under the current filters.
- Columns appear in the same order as the table.
- The file uses the project's CSV dialect; if none exists, it follows RFC 4180 by quoting fields that contain commas, double quotes, or line breaks and doubling embedded quotes.
- An empty result downloads a header-only CSV rather than failing.
- Exporting does not change the current page or filters.

That fallback follows RFC 4180’s documented CSV format; if the repository already defines a different dialect, the existing contract should win.

The second version gives the agent something it can implement and gives you something you can review. It also makes test generation far more useful because the assertions can match real behavior instead of merely checking that a function returns a value.

When requirements are uncertain, say so explicitly. “Choose the simplest behavior consistent with the existing import flow and call out the choice in your summary” is better than pretending an undecided edge case has one obvious answer.

Tell the agent how to verify its work

An agent should not stop when the code looks plausible. Include the narrowest relevant verification command and, when reasonable, one broader regression check:

Verification:
- Run npm test -- orders/export.spec.ts
- Run npm run typecheck
- Manually confirm the disabled state appears while a large export is being prepared

Use commands that actually exist in the repository. If you do not know them, ask the agent to inspect package.json, the build configuration, or the project guidance before changing code.

Verification instructions improve both the implementation and the final report. They prompt the agent to confront compilation errors, integration assumptions, and missing fixtures before handing the diff back to you, but they do not guarantee that every check ran or passed. Inspect the reported commands and output. You still need to review AI-generated code, but you begin that review with evidence instead of confidence.

A before-and-after prompt example

Suppose a checkout form sometimes accepts a double submission.

Vague prompt:

Fix duplicate orders in checkout.

Task-ready prompt:

Task: Prevent two orders from being created when a user submits checkout twice.

Context:
- The browser submits through src/checkout/CheckoutForm.tsx.
- POST /api/orders is handled in src/server/orders/create.ts.
- Follow the existing idempotency pattern used by subscription creation in
  src/server/subscriptions/create.ts, including key scope and retention,
  concurrent-request handling, and same-key payload comparison.

Constraints:
- Treat the server as the source of truth; a disabled button alone is not sufficient.
- Preserve the current POST response shape.
- Do not change the payment-provider integration.

Acceptance criteria:
- Repeated or concurrent requests with the same idempotency key and payload create one order and return the same order ID.
- Requests with different keys still create separate orders.
- Reusing a key with a different payload is rejected according to the existing subscription behavior.
- A failed attempt can be retried according to the existing subscription behavior.
- The submit button shows a pending state to reduce accidental double clicks.

Verification:
- Add focused request-level tests for duplicate, distinct, and failed requests.
- Run the checkout test suite and typecheck.

Deliverable:
- Implement the smallest complete fix, add tests, and report verification results.

The longer prompt is not better merely because it has more words. It is better because it establishes server-side enforcement, identifies an existing pattern, protects the API contract, and defines the expected failure behavior.

Adapt prompts for parallel agents

When several independently implemented branch or worktree tasks run at once, define an ownership boundary and stable dependency point for each one. Split the feature along codebase seams, then state which files each agent owns.

For example, after merging a shared SavedSearch type, you could dispatch three prompts:

  • API agent: implement CRUD handlers in src/server/saved-searches/; consume but do not modify the shared type.
  • UI agent: build the dropdown in src/components/saved-searches/; use the existing query abstraction and do not change server routes.
  • Test agent: add end-to-end coverage in e2e/saved-searches/; do not modify production code unless a test hook is essential and approved.

Each prompt should be independently understandable. If agent B cannot begin until it knows an implementation decision agent A will make, those tasks are sequential. Either define the shared contract first or combine them into one task.

This is also where shorter prompts can be easier to review and keep scoped. A focused task with one directory, three acceptance criteria, and one test command is easier to reason about than a giant prompt asking an agent to build an entire feature while three other branches are changing nearby code.

Common prompt mistakes

Asking for a whole feature in one sentence. The agent must guess product behavior, architecture, and scope at once. Break the work into a contract and a few reviewable tasks.

Pasting a solution without explaining the problem. The proposed solution may not fit the repository. State the outcome and identify which implementation decisions are genuinely fixed.

Using subjective acceptance criteria. Words such as “clean,” “robust,” and “production-ready” need observable definitions.

Forgetting non-goals. If a nearby refactor is tempting but out of scope, say so. This helps discourage a useful fix from arriving inside a risky rewrite.

Requesting tests without naming behavior. An agent can produce tests that only confirm its own implementation. Behavioral acceptance criteria give those tests an independent target.

Treating the first prompt as permanent. A prompt is a task brief, not a contract with the universe. If repository inspection reveals a conflicting architecture or missing requirement, pause, revise the brief, and then continue.

A compact prompt for everyday use

Not every task needs the full template. For a small, well-understood change, this is often enough:

In [file/module], change [current behavior] so that [desired behavior].
Follow the pattern in [reference file]. Preserve [important contract].
Cover [normal case] and [edge case] with tests. Run [verification command].
Keep the diff scoped to this change and summarize any assumption you had to make.

That format works because it still provides an outcome, context, a constraint, acceptance criteria, and verification.

Using the template in Parallel Code

In its standard isolated workflow, Parallel Code gives each task its own branch and worktree; Direct mode and non-Git folders are explicit exceptions. Create one task per independently mergeable outcome, paste the relevant prompt into the task, and choose the coding agent that fits the work. You can inspect each diff separately, discard a weak attempt without changing the tracked files in your main checkout, and merge only the result that passes review.

For difficult tasks where the implementation is genuinely uncertain, Arena mode lets you race several agents on the same prompt. A precise shared brief removes one source of variation. Where a controlled comparison matters, also standardize the base commit, environment, permissions, and resource budget before judging the solutions.

Prompt checklist

Before you start an AI coding agent, check that the prompt answers:

  • What observable outcome should change?
  • Where should the agent begin reading?
  • Which existing pattern or interface should it follow?
  • What must not change?
  • Which edge cases matter?
  • What commands or checks prove the task is complete?
  • Is the requested diff small enough to review confidently?
  • If other agents are running, does this task have a clear ownership boundary?

If you can answer those questions in a short brief, the agent has room to solve the problem without having to invent the assignment. That is the balance to aim for: enough precision to produce the right change, enough freedom to use the codebase intelligently.