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.
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:
- 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. - 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:
- Store prompts in the repo (or a prompt registry) with version history, not in a dashboard textarea.
- Template variables explicitly and log the fully rendered prompt for debugging, with sensitive data redacted.
- Maintain an eval set: 50–500 real inputs with expected outputs or grading criteria.
- Run evals in CI on every prompt or model change, and compare scores to the previous version. See how to evaluate LLM applications.
- 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.