Prompt Engineering for Developers: Patterns That Hold Up in Production

A practical prompt engineering guide for software engineers — structure, few-shot examples, structured outputs, chain-of-thought, versioning and testing prompts like code.

By Danyal Ahmed - Data Scientist · · 5 min read

Prompt engineering has a reputation problem. It sounds like magic words and superstition. In production, it's closer to API design: you're writing an interface between your application and a probabilistic component, and the same disciplines apply. Be explicit, constrain outputs, test changes and version everything.

This guide focuses on patterns that survive contact with real users, not tricks that win screenshots on social media.

Key takeaways

  • Treat prompts as code: version them, review them, test them against an eval set.
  • The biggest wins come from clarity and context: who the model is serving, what good output looks like, and what to do when unsure.
  • Use structured outputs (JSON schemas, tool calls) whenever code consumes the result.
  • Examples beat adjectives. Two good few-shot examples outperform a paragraph of "be concise and professional".

Anatomy of a production prompt

Most reliable prompts contain the same building blocks:

text[Role & goal]        You are a support assistant for Acme's billing product.
[Context]            The user is on the {plan} plan. Today's date is {date}.
[Task]               Answer the user's question using the policy excerpts below.
[Constraints]        - Use only the excerpts. If they don't cover it, say so.
                     - Max 120 words. No legal advice.
[Output format]      Return JSON: {"answer": string, "sources": number[]}
[Examples]           <example>...</example>
[Input]              <excerpts>...</excerpts>
                     <question>{question}</question>

A few details matter more than they seem:

  • Delimit untrusted input with clear tags like <question> or <document>. It makes the boundary between instructions and data explicit and reduces prompt-injection risk (though it doesn't eliminate it; see AI guardrails).
  • Put stable content first (instructions, reference docs) and variable content last. This makes prompt caching effective, which cuts cost and latency.
  • State the audience and purpose. "Explain to a non-technical account manager" changes vocabulary and depth more reliably than "be simple".

Pattern 1: Be specific about what "good" means

Vague prompts produce average output. Compare:

❌ Summarize this meeting transcript.

✅ Summarize this sales call for the account executive who missed it. Include: (1) customer's main objection, (2) budget and timeline if mentioned, (3) agreed next steps with owners. Use bullet points. If something wasn't discussed, write "not discussed" rather than guessing.

The second prompt defines the reader, the required fields, the format and the fallback behavior. Each of those removes a decision the model would otherwise make arbitrarily.

Pattern 2: Few-shot examples

Examples communicate format, tone and edge-case handling faster than instructions. Guidelines:

  • Use 2–5 diverse examples, including at least one tricky or negative case.
  • Make examples representative. The model will copy their length and style closely.
  • Keep them consistent with your instructions. If the instructions say "one sentence" and an example has three, the example usually wins.
textClassify the support ticket's urgency as low, medium or high.

<ticket>Can you change my invoice address?</ticket>
<urgency>low</urgency>

<ticket>Our entire team is locked out since the SSO change this morning.</ticket>
<urgency>high</urgency>

<ticket>{ticket}</ticket>
<urgency>

Pattern 3: Structured outputs

If code parses the response, don't ask nicely for JSON. Enforce it. Most major LLM APIs support JSON-schema-constrained outputs or tool/function calling, which guarantee (or strongly enforce) valid structure.

pythonschema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "topics": {"type": "array", "items": {"type": "string"}, "maxItems": 5},
        "needs_follow_up": {"type": "boolean"},
    },
    "required": ["sentiment", "topics", "needs_follow_up"],
}

Then validate anyway with Pydantic, Zod or similar. Schema compliance doesn't guarantee semantic correctness: an enum value can still be the wrong one.

Pattern 4: Give the model room to think

For multi-step problems such as analysis, math, planning or complex classification, asking the model to reason before answering improves accuracy. Two production-friendly ways to do it:

  1. Separate reasoning from the answer: "Think through the problem inside <analysis> tags, then give the final answer inside <answer> tags." Your code extracts only the answer.
  2. Use a reasoning model or an extended-thinking mode, where the provider supports it, for genuinely hard tasks, and a faster model for simple ones.

Don't pay for reasoning you don't need. Extraction and simple classification rarely benefit.

Pattern 5: Decompose instead of mega-prompting

One prompt that extracts entities, classifies intent, drafts a reply and checks policy will do all four things mediocrely. Chain smaller prompts instead:

textextract_fields(email) → classify_intent(fields) → draft_reply(intent, fields) → policy_check(draft)

Each step is easier to test, can use a cheaper model where possible, and fails in a visible, debuggable place. This is the foundation of reliable AI agent workflows.

Pattern 6: Tell the model what to do when it can't comply

Models are trained to be helpful, so without an escape hatch they'll invent an answer. Always define the fallback:

  • "If the document doesn't contain the invoice number, return null."
  • "If the question is unrelated to billing, reply exactly: OUT_OF_SCOPE."

Sentinel values like these are also easy to monitor in production.

Anti-patterns to avoid

Anti-pattern Why it hurts Instead
ALL-CAPS threats ("YOU MUST NEVER…") Modern models over-apply them, causing rigid behavior Calm, specific instructions with reasons
Negative-only rules ("don't be verbose") Says what to avoid, not what to do "Answer in ≤ 3 sentences"
Giant prompts nobody owns Contradictions creep in Modular prompts with an owner and changelog
Tweaking by vibes Fixing one case breaks five others Change → run evals → compare
Secrets in prompts Prompts can leak Keep keys and sensitive logic in code

Prompts are code: version, review, test

A mature prompt workflow looks like normal software engineering:

  1. Store prompts in the repo (or a prompt registry) with version history, not in a dashboard textarea.
  2. Template variables explicitly and log the fully rendered prompt for debugging, with sensitive data redacted.
  3. Maintain an eval set: 50–500 real inputs with expected outputs or grading criteria.
  4. Run evals in CI on every prompt or model change, and compare scores to the previous version. See how to evaluate LLM applications.
  5. Roll out gradually. A/B test prompt versions on live traffic when stakes are high.

Model-specific tuning

Different model families respond differently to formatting (XML tags vs Markdown), system-prompt placement and verbosity. When you switch models, re-run your evals. Don't assume a prompt ports cleanly. Read the provider's prompting guide; they document what their models were trained to follow.

FAQ

Is prompt engineering still relevant as models get smarter?

Yes, but its focus shifts. Newer models need fewer tricks and follow instructions more literally, so clarity, context and precise specifications matter more, not less. The engineering around prompts (evals, versioning, structured outputs) matters regardless of model quality.

System prompt or user prompt: where should instructions go?

Put persistent role, rules and formatting in the system prompt, and per-request data and questions in the user message. This separation helps the model prioritize and makes caching more effective.

How long should a prompt be?

As long as it needs to be to remove ambiguity, and no longer. Long prompts with relevant context are fine; long prompts with contradictory or irrelevant instructions are not. Measure with evals rather than optimizing length for its own sake.

What temperature should I use?

Low (0–0.3) for extraction, classification and anything parsed by code. Moderate (0.5–0.8) for creative writing or brainstorming. Some reasoning models fix or ignore temperature, so check your provider's documentation.