Post

Prompt Engineering for Developers: Writing Clear Instructions for AI

Write clearer coding prompts with explicit scope, useful context, testable requirements, and repeatable prompt-evaluation exercises.

Prompt Engineering for Developers: Writing Clear Instructions for AI

A request like “build a login screen” leaves several decisions open: which framework to use, how authentication works, what errors to show, and what counts as finished. A coding assistant may fill those gaps with reasonable choices that still do not fit your project.

Prompt engineering is the practice of writing and refining instructions to make the intended task clearer. It improves the odds of a useful result; it does not make generated code deterministic or correct by construction.

This is the first of six posts about coding agents: assistants that can inspect files, use tools, and propose or make code changes. It begins with prompt writing and then moves through connected topics: instructions, model routing, context, tools, specifications, and feedback. The running example throughout is adding a discount during online checkout.

Four elements of a useful coding prompt

1. Goal and scope

Describe the behavior you want and the boundary of the change. “Fix duplicate submissions in the checkout form” is easier to assess than “improve checkout.” For a larger feature, ask the agent to inspect the repository and divide the work into reviewable steps.

A role such as “backend reviewer” can clarify perspective, but it does not substitute for requirements or give the model expertise it does not have.

2. Relevant context

Point to the implementation, a similar feature, and the relevant contract. Include the exact error and the command that produced it when reporting a failure.

An agent with repository tools can retrieve files itself. A chat interface without those tools needs the relevant excerpts. Neither should be expected to know private project details that have not been made available.

3. Constraints and expected behavior

Name the constraints that affect the solution: supported runtime, allowed dependencies, public interfaces, and compatibility requirements. Prefer observable behavior over vague adjectives such as “robust” or “secure.”

For example, “return the existing result when the same request ID is submitted again” gives the agent a concrete rule to implement and test. Avoid prescribing an algorithm before establishing that it fits the data and requirements.

4. Acceptance criteria and delivery format

Acceptance criteria describe what must work. Delivery format describes how to present the result. They are different concerns.

Ask an agent to run the relevant checks and report what it actually ran, any failures, and remaining assumptions. In a chat-only workflow, ask for an implementation and tests, while recognizing that the model may be unable to execute them.

Example: make a validation policy explicit

Consider this request:

1
Write a function that validates emails in TypeScript.

It leaves the input type, whitespace policy, and accepted address formats unspecified. A request for a “fully compliant regex” still leaves a complicated policy decision hidden inside an implementation detail.

For an application that deliberately accepts a limited format, a more useful prompt is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Task: Add isValidEmail(input: unknown): boolean as a named export.

Context:
- Inspect the existing utilities and test setup before editing.
- Reuse the project's TypeScript configuration and test runner.

Product policy:
- Return false for non-string inputs.
- Reject empty strings and any whitespace; do not trim the input.
- Accept only ASCII letters, digits, dots, underscores, plus signs,
  and hyphens in the local part.
- Reject a local part that starts or ends with a dot, or has consecutive dots.
- Require exactly one @ and at least two nonempty domain labels.
- Domain labels may contain ASCII letters, digits, and hyphens,
  but must not start or end with a hyphen.
- Reject all other characters. Do not perform network requests.

Acceptance examples:
- true: alex@example.com, alex+shop@sub.example.com
- false: null, 42, "", " alex@example.com", alex..lee@example.com,
  alex@localhost, alex@-example.com

Delivery:
- Implement the policy and add tests for its boundaries.
- Run the relevant tests if execution tools are available.
- Report changed files, commands, and results, and outstanding assumptions.

This is an illustrative product policy, not a claim of full email-standard compliance or mailbox ownership. A real signup flow should choose its accepted formats deliberately and verify ownership separately. The improvement is that a reviewer can now compare the implementation with explicit decisions.

Common mistakes

  • Conflicting instructions: “Return only code” conflicts with “explain the trade-offs.” Decide which output you need.
  • Too much unrelated work: A broad task may need a plan and checkpoints. Split it where behavior can be reviewed independently.
  • Missing examples: Boundary cases often communicate intent better than another paragraph of adjectives.
  • Treating the first output as verified: Review the diff and run checks. A convincing explanation is not execution evidence.

A prompt such as “use Clean Architecture, CQRS, and event sourcing” selects a solution before explaining the problem. Unless those choices are established requirements, ask the agent to compare alternatives against concrete constraints and future changes. The spec-driven development post covers the maintenance cost of easily generated architecture.

Which language should I use?

Use the language in which you can state the goal, constraints, and nuance clearly. You do not need to translate your thoughts into English first. For Brazilian work, ask for Brazilian Portuguese (pt-BR) and name the target audience and register when they matter. A coding prompt can use Portuguese prose while keeping code, identifiers, API fields, error messages, and source documentation exactly as written.

Model results can vary by language, but a score is specific to a model and task. In OpenAI’s 2025 zero-shot MMLU evaluation, o1 scored 92.3% on the original set of English questions and 89.5% on the professionally translated Brazilian Portuguese questions. That 2.8 percentage-point difference is one result on a multiple-choice knowledge benchmark; it does not isolate the language of the instruction, measure conversational idiom, or show that English is always better.

For an important or repeated task, compare equivalent English and Brazilian Portuguese prompts with the same model, context, tools, and review criteria. Check correctness as well as natural, locally appropriate phrasing. Use the language that works for your task, and specify the desired response language explicitly. Context Engineering discusses the limits of public language-share estimates and token costs.

Revise a prompt around an observed failure

For the running example, begin with “Add discount-code support to checkout.” Suppose a trial implementation calculates discounts in the browser and persists the original total. This is a hypothetical failure to diagnose, not a measured model result. More emphatic wording would leave the same ownership decision unresolved.

After confirming the intended behavior with the product owner and the existing pricing contract, revise the request:

1
2
3
4
5
6
7
8
9
10
11
12
13
Task: Add an optional discount code to checkout.
Context: Locate the current pricing contract and trace the quote flow
through web, orders, and pricing. Record the revisions you used.
Behavior:
- Pricing owns eligibility and calculation; web displays its quote.
- Orders persists the accepted quote rather than recalculating a discount.
- No code preserves the current checkout behavior.
- An invalid or expired code shows the agreed error and cannot create
  an order with a discounted total.
Verification:
- Derive boundary tests from these rules and the pricing contract.
- Exercise quote display and persisted totals through checkout.
- Report actual checks, excluded boundaries, and unresolved decisions.

The revision addresses the observed ownership gap. It also leaves rounding and eligibility details with the authoritative contract. If that contract is missing or contradictory, resolve the decision before asking the agent to implement a guess. A failing command caused by missing dependencies calls for a harness fix, not another pricing instruction.

Reuse a working example and a decision record

Rahul Garg’s Patterns for Reducing Friction in AI-Assisted Development proposes onboarding the assistant with project examples and preserving decisions across sessions. He presents the expected benefits as hypotheses, not measured productivity gains.

For checkout, extend the prompt with a small orientation step:

1
2
3
4
5
Find an existing checkout change that follows our current conventions.
Identify the relevant handler, pricing client, and test fixture by path.
Explain which parts apply to discounts and where the behavior differs.
Before implementing, flag any conflict with the current pricing contract.
Keep confirmed decisions and open questions in the feature change record.

A reference implementation is useful only after checking that it still matches the contract. Preserve reusable conventions in project guidance; keep the discount policy and temporary investigation notes with the feature. When resuming, verify recorded paths and revisions against the checkout instead of treating a previous session’s summary as current evidence.

Improve prompts with evidence

Version prompts, keep evaluation data and metrics consistent, and evaluate changes in the whole system. For more on prompt iteration and evaluation, see Chip Huyen’s Chapter 5, “Prompt Engineering” and Chapter 3, “Evaluation Methodology” in AI Engineering. Apply that approach to the checkout example with this small exercise:

  1. Prepare test cases for a valid code, an expired code, no code, and a mismatch between displayed and saved totals. Record the starting commits and independent acceptance checks.
  2. Use some cases to revise prompt A into prompt B. Set aside other cases until evaluation; both prompts must receive the same product requirements when tested.
  3. Run A and B from clean copies of the same starting revisions, with the same model, tools, permissions, and time budget. Change only the prompt. Where routing cannot be fixed, record the actual model choices and treat them as a possible confounder.
  4. Repeat each task for each version where practical, alternating execution order. Report the number of tasks and runs; a single successful attempt does not establish reliability.
  5. Evaluate both versions with the same acceptance checks and review criteria. Include failed and timed-out runs, not just delivered changes.

Record one row per run. This is a blank template, not benchmark data:

Task / starting commits Prompt / model / harness version Acceptance cases passed / required Regressions Repair attempts Review minutes Elapsed seconds Total billed cost
Fill from run record Fill from configuration and execution log Fill from independent checks Fill from regression checks Fill from execution log Record reviewer time Measure elapsed time Include failed attempts

Inspect outcomes by task type before combining them: better handling of valid codes can hide worse handling of expired ones. Keep quality, review effort, latency, and cost separate. If a small sample produces mixed results, report the uncertainty and collect more evidence instead of declaring a winning prompt.

For repeated prompt evaluations, write down the review criteria and score each one separately. A structured scorer such as TypeSafe Jev’s Score primitive can rate requirement coverage, verification quality, and ease of review using the same task summary, acceptance criteria, code change, actual check results, and completion report. Keep the evaluator version and criteria fixed when comparing prompts. Combine scores in code only when their weights are clear, following the composite-scoring pattern. A high average must not hide a failed acceptance case or replace human review.

The loop-engineering protocol extends this exercise to changes in tools and runtime behavior.

Clear instructions give the task direction. Next, AI Gateways and Model Routing explains how infrastructure selects eligible models and applies configured policies. Then Context Engineering follows the checkout feature into its source files and contracts.

This post is licensed under CC BY 4.0 by the author.