✍️ Prompt Engineering β€” Master Guide

🎯 AI Product Manager βš™οΈ Staff Engineer Mental models, techniques, structured outputs, anti-patterns, and evaluation-driven iteration

The Right Mental Model for Prompt Engineering
Prompt engineering is often taught as a bag of tricks. The better mental model: you are writing a job description for a brilliant, literal-minded contractor who has read almost everything but remembers nothing between assignments. Clarity, context, and constraints are more valuable than clever phrasing.
🎯 PM Perspective
Prompts are product. They determine quality, tone, safety, and behavior. A prompt change is a product change β€” it needs testing, versioning, and review. Investing in prompt quality and systematic iteration is one of the highest-ROI activities in an AI product team.
βš™οΈ Engineer Perspective
A prompt is a specification. The LLM is the executor. Ambiguous specs produce inconsistent output. The discipline is the same as API design: be explicit about inputs, expected outputs, error cases, and constraints. Test systematically, not by feel.

🧠 The Five Dimensions of a Good Prompt

1. Context
Who is the model acting as? What situation is it in? What does it know? Without context, the model defaults to generic behavior.
2. Task
What exactly should it do? One clear task per prompt. Vague tasks produce vague outputs. Use active verbs: classify, summarize, extract, generate, evaluate.
3. Format
What should the output look like? JSON, markdown, bullet list, a single word? Models will guess if you don't specify β€” and often guess wrong.
4. Constraints
What should it NOT do? Length limits, tone restrictions, things to avoid, what to do when uncertain. Negative constraints are as important as positive instructions.
5. Examples
One good example is worth 100 words of description. Show the model what good output looks like. Show what bad output looks like too.
Bonus: Persona
Who is the model? "You are a senior software engineer at a fintech company" activates different knowledge clusters than "you are a helpful assistant."

πŸ“Š Impact vs Effort β€” Which Techniques Move the Needle

TechniqueQuality LiftEffortWhen to Apply
Clear task descriptionVery HighLowAlways β€” foundation of everything
Output format specificationHighLowWhenever you need structured or consistent output
1-3 examples (few-shot)HighMediumClassification, formatting tasks, style matching
Chain-of-thought instructionHighLowMath, logic, multi-step reasoning, analysis
Persona definitionMediumLowDomain expertise, tone, user-facing products
Negative examplesMediumMediumWhen model keeps making a specific mistake
XML/delimited structureMediumLowLong prompts with multiple sections
Self-consistency (multiple runs)MediumHighHigh-stakes reasoning where accuracy matters most
πŸ”¬ Prompt Anatomy β€” Dissecting a Production Prompt
A production prompt is not a question. It's a structured specification with distinct sections. Understanding each section and its role lets you build prompts systematically rather than by intuition.

πŸ—οΈ Full Production Prompt Structure

## SECTION 1: PERSONA + ROLE (activates relevant knowledge) You are a senior product manager at a B2B SaaS company with 10 years of experience writing PRDs, user stories, and competitive analyses. ## SECTION 2: TASK (specific, active verb, one clear goal) Analyze the following customer feedback and: 1. Classify each piece of feedback into one of: Bug, Feature Request, UX Issue, Positive 2. Extract the core pain point in one sentence 3. Suggest the most impactful product change that addresses the feedback ## SECTION 3: CONTEXT / INPUT (delimited clearly) <feedback> {{CUSTOMER_FEEDBACK}} </feedback> ## SECTION 4: CONSTRAINTS (what NOT to do) - Do not infer intent beyond what is explicitly stated - If classification is ambiguous, choose the most specific category - Suggested changes must be implementable in under 2 weeks of engineering time - Do not suggest changes that require a new pricing tier ## SECTION 5: OUTPUT FORMAT (exact structure, leave nothing to chance) Respond in this exact JSON format: { "classification": "Bug | Feature Request | UX Issue | Positive", "pain_point": "one sentence, max 20 words", "suggested_change": "actionable description, max 50 words", "confidence": "high | medium | low" } Return ONLY valid JSON. No explanation, no markdown fences.
πŸ’‘
Use XML-style delimiters (<feedback>, <context>, <examples>) to separate sections clearly. Claude in particular handles XML delimiters extremely well. It prevents the model from confusing instructions with data.

πŸ“ Prompt Position Matters

PositionContentWhy Here
System prompt (top)Persona, role, global constraints, output formatHighest priority; sets the model's entire operating context
Beginning of user promptCore task, most important instructionsModels are biased toward early content; critical info first
MiddleExamples, context, background dataReferenced when needed; doesn't compete with task instruction
End of user promptThe actual data/content to processRecency bias helps the model focus on the immediate input
After data (final line)Re-state the specific instruction or output formatRecency bias: last thing seen before generating is the output reminder
⚠️
The "lost in the middle" problem: Models pay more attention to content at the beginning and end of long prompts. Critical instructions buried in the middle of a 5000-token prompt are often missed. Put the most important instructions first AND last if needed.
πŸ“š Few-Shot Prompting β€” Show, Don't Just Tell
One well-chosen example is worth a paragraph of instruction. Few-shot prompting shows the model exactly what you want through input-output demonstrations rather than abstract description.

Zero-Shot vs Few-Shot β€” When Each Wins

⚠️ Zero-Shot (for classification)
Classify this customer email as: Urgent, Normal, or Low priority. Email: "Hey, just wondering when my order might ship?" Classification:
Problem: Model doesn't know what "Urgent" means in YOUR context. Will guess. Inconsistent across runs.
βœ… Few-Shot (same task)
Classify customer emails: Urgent, Normal, or Low. Email: "Our entire team can't log in. Production is down." Classification: Urgent Email: "Can you update my billing address?" Classification: Normal Email: "Hey, just wondering when my order might ship?" Classification:
Examples calibrate what "Urgent" means. Model learns from YOUR definitions, not generic ones.

πŸ“‹ Few-Shot Best Practices

  • βœ“
    3-5 examples is the sweet spot for most tasks. More examples rarely add much but add tokens and cost.
  • βœ“
    Cover edge cases not just easy cases. Include examples of ambiguous inputs and how to handle them.
  • βœ“
    Match distribution to your actual inputs. If 20% of inputs are edge cases, include edge case examples.
  • βœ“
    Show negative examples for what NOT to do, especially if the model keeps making a specific mistake.
  • βœ“
    Consistent format across all examples β€” if one uses JSON, they all should.
  • βœ“
    Label your examples with XML tags: <example><input>...<output>...
  • βœ“
    Order matters slightly β€” put the most representative examples last (recency bias).
  • βœ“
    Real examples outperform synthetic ones β€” use actual production inputs when possible.

πŸ’‘ Dynamic Few-Shot β€” Retrieving Examples at Runtime

Static examples in the prompt work for stable tasks. For variable tasks, retrieve the most relevant examples from an example store based on the current input β€” similar to RAG but for demonstrations.

# Dynamic few-shot: retrieve examples similar to the current input def get_dynamic_examples(input_text: str, n: int = 3) -> list: # Embed the input input_emb = embed(input_text) # Find similar examples from your labeled example store similar = example_store.search(input_emb, k=n) return similar def build_few_shot_prompt(input_text: str) -> str: examples = get_dynamic_examples(input_text) example_block = "\n\n".join([ f"Input: {ex.input}\nOutput: {ex.output}" for ex in examples ]) return f"Here are examples:\n\n{example_block}\n\nNow classify:\nInput: {input_text}\nOutput:" # Result: examples are always semantically similar to the current task
πŸ’­ Chain-of-Thought β€” Making the Model Show Its Work
Chain-of-thought (CoT) prompting instructs the model to reason step-by-step before giving a final answer. For tasks requiring logic, math, analysis, or multi-step inference, CoT significantly improves accuracy.

Why CoT Works

LLMs generate tokens left-to-right. Without CoT, the model must "compress" all its reasoning into the answer token directly β€” error-prone for complex tasks. CoT externalizes the reasoning process, giving the model "scratch space" to work through the problem before committing to an answer.

❌ Without CoT β€” Direct Answer
Q: A store offers 15% off on orders over $100. A customer buys $85 of items and adds a $20 item. What is the final price? A: $89.25
Result: Wrong. Model jumped to answer without verifying the $100 threshold logic. ($85+$20=$105, qualifies for 15% off, so $105Γ—0.85=$89.25 is actually right here β€” but models make mistakes on variations without CoT.)
βœ… With CoT β€” Step-by-Step
Q: [same question] Let me work through this step by step. Step 1: Total order = $85 + $20 = $105 Step 2: Does $105 qualify for discount? Yes ($105 > $100) Step 3: Discount = $105 Γ— 15% = $15.75 Step 4: Final price = $105 - $15.75 = $89.25 A: $89.25
Each step is verifiable. Errors surface visibly instead of hiding in a single wrong answer.

πŸ“‹ CoT Trigger Phrases β€” Pick One That Fits Your Context

Zero-Shot CoT
"Think through this step by step." "Let's work through this carefully." "Reason step by step before answering." "Think before you respond."
Simplest. Just add to any prompt. Works surprisingly well.
Structured CoT
"Use this format: Analysis: [your reasoning] Answer: [final answer]" "First state your reasoning, then give the conclusion."
Ensures reasoning is separated from answer for parsing.
Scratchpad CoT
"Use <thinking> tags for your reasoning. Then give your final answer outside the tags."
Reasoning hidden from user but still happens. Claude native feature.

🎯 CoT Decision Guide

Task TypeUse CoT?Reason
Math / calculationsAlwaysDirect answers on arithmetic are error-prone without step-by-step
Multi-step logical reasoningAlwaysSteps expose where logic breaks down
Code debuggingAlwaysTrace through execution before diagnosing
Classification (simple)RarelyAdds latency and tokens; direct answer is fine for clear categories
Information extractionRarelyExtract is mechanical; CoT not needed
Judgment calls / analysisYesShows the factors considered; improves quality and trust
Latency-critical tasksSkipCoT adds 2-5x output tokens; use direct prompting
πŸ“ Structured Output β€” Getting Consistent, Parseable Responses
Unstructured prose is fine for conversations. For anything that feeds a downstream system (database, API, UI component), you need structured output. Getting reliable JSON, XML, or typed responses from LLMs is a learnable skill.

🎯 Techniques for Reliable Structured Output

Technique 1: Schema in the prompt
Show the exact JSON schema. Include field names, types, and allowed values. The more specific, the more reliable.
# Bad: vague format instruction "Return the result as JSON" # Good: exact schema "Return ONLY this JSON, no other text: { \"sentiment\": \"positive|negative|neutral\", \"score\": 0.0-1.0, \"key_phrases\": [\"string\", ...], \"reason\": \"max 20 words\" }"
Technique 2: Prefill the response (Claude-specific)
Start the assistant turn with {" β€” the model will continue the JSON from where you started.
# In Claude API, prefill the response messages = [ {"role": "user", "content": "Analyze this: ..."}, {"role": "assistant", "content": '{"'} ] # Model is "forced" to continue valid JSON # Because it already started it
Technique 3: JSON Mode / Tool Use
Modern models support native JSON mode or structured tool call schemas that enforce valid JSON at the API level β€” more reliable than prompt-based enforcement.
# OpenAI JSON mode response = client.chat.completions.create( model="gpt-4o", response_format={"type": "json_object"}, messages=[...] ) # Claude tool use for structured output tools = [{ "name": "output_result", "input_schema": { "type": "object", "properties": {"sentiment": {"type": "string"}} } }]
Technique 4: Validation + Retry
Always validate the output. If it fails schema validation, send it back with a correction prompt. Most failures correct on retry.
import json from jsonschema import validate def get_structured_output(prompt, schema, max_retries=2): for attempt in range(max_retries): raw = llm(prompt) try: data = json.loads(raw) validate(data, schema) return data except Exception as e: prompt += f"\n\nYour output was invalid: {e}\nPlease fix and return valid JSON only." raise ValueError("Max retries exceeded")
βš™οΈ System Prompts β€” The Persistent Foundation
The system prompt is evaluated once per session and sets the global operating context for the model. Getting system prompts right is the highest-leverage prompt engineering activity for product teams.

πŸ“‹ System Prompt Template (Production)

## Identity and Role You are Aria, an AI assistant for Acme Corp's enterprise software platform. You help users troubleshoot issues, navigate features, and get the most out of the product. You do NOT discuss competitor products or company financials. ## Tone and Style - Professional but warm. Use "you" not "the user." - Concise by default. Offer to expand if the topic is complex. - When uncertain, say so. Never make up product features. ## Knowledge Boundaries You know: Acme platform features, common troubleshooting steps, pricing tiers. You don't know: Individual account data (direct to support for that). If asked about things outside your knowledge, say: "I'd need to check on that β€” let me connect you with our support team at support@acme.com" ## Behavior Rules - Never claim to be human if sincerely asked - Always offer to escalate to a human agent for billing disputes - Do not provide legal or financial advice - If the user seems frustrated, acknowledge first before solving ## Output Format - For step-by-step instructions: numbered list - For comparisons: table format - For definitions: bold the term, then explain - Default response length: 2-4 sentences unless complexity demands more

πŸ” System Prompt Security Considerations

⚠️
System prompts are not secret. A determined user can often extract system prompt contents through persistent questioning. Design system prompts assuming they will be read β€” put nothing in a system prompt you wouldn't put in public documentation.
RiskWhat HappensMitigation
Prompt extractionUser asks "what's your system prompt?" and model reveals itInstruct model not to reveal verbatim; it can confirm it has instructions
JailbreakUser convinces model to ignore system prompt instructionsRobust instructions, model-level safety filters, monitoring for anomalous outputs
Injection via user inputUser crafts input that looks like a system prompt overrideUse XML delimiters to separate system instructions from user content
Sensitive data in system promptAPI keys, internal URLs, proprietary logic exposedNever put secrets in prompts; use environment variables and tool calls
πŸ”„ Evaluation-Driven Prompt Iteration
Prompt engineering without evaluation is guesswork. The teams that build the best prompts are the ones that treat prompt development like software development: version control, test suites, and metrics-driven iteration.

πŸ”„ The Prompt Development Lifecycle

1️⃣
Define
What does "good" look like? Write 5-10 test cases with expected outputs before writing the prompt.
2️⃣
Draft
Write v1 prompt. Use the anatomy framework: role, task, format, constraints, examples.
3️⃣
Evaluate
Run all test cases. Score each output against expected. Identify failure patterns.
4️⃣
Iterate
Fix specific failures. One change at a time. Re-run full suite after each change.
5️⃣
Version
Store prompts in version control. Tag with eval scores. Never replace without regression testing.

πŸ“Š Prompt Versioning β€” Treat Prompts Like Code

# prompts/classify_feedback.py PROMPT_VERSIONS = { "v1.0": { "prompt": "Classify this feedback...", "eval_score": 0.72, "date": "2025-01-15", "notes": "Baseline β€” struggles with ambiguous inputs" }, "v1.1": { "prompt": "Classify this feedback... [added 3 examples]", "eval_score": 0.84, "date": "2025-01-22", "notes": "Added few-shot; improved ambiguous cases" }, "v2.0": { # current production "prompt": "Classify this feedback... [full structured prompt]", "eval_score": 0.91, "date": "2025-02-01", "notes": "Added chain-of-thought; improved edge cases" } } # Never modify a prompt in production without running the full eval suite first
🚫 Prompt Anti-Patterns β€” What to Avoid
These patterns appear in prompts constantly. Each one either produces unreliable outputs, makes the model's job harder, or creates security risks. Recognizing them is as important as knowing the positive techniques.

❌ Anti-Pattern 1: Ambiguous Instructions

❌ Bad
"Summarize this article briefly."
What's "briefly"? 1 sentence? 1 paragraph? 5 bullet points? The model will guess β€” inconsistently.
βœ… Good
"Summarize this article in exactly 3 bullet points, each under 20 words. Focus on the main argument, key evidence, and conclusion."
Explicit format, length, and focus. Same output structure every time.

❌ Anti-Pattern 2: Negation Without Alternatives

❌ Bad
"Don't use jargon. Don't be too long. Don't be too formal."
Negation without alternatives forces the model to guess what you DO want. Often inconsistent.
βœ… Good
"Write at an 8th grade reading level. Keep responses under 100 words. Use a friendly, conversational tone β€” as if explaining to a smart friend."
Positive description of what you want, not just what to avoid.

❌ Anti-Pattern 3: Hallucination-Inviting Prompts

❌ Bad
"What are the statistics on customer satisfaction for our product?"
Model has no data about YOUR product. Will hallucinate numbers. This is the most dangerous anti-pattern in production.
βœ… Good
"Based ONLY on the data below, summarize customer satisfaction trends. If the data doesn't contain enough information to answer, say 'insufficient data.' <data>{{CUSTOMER_DATA}}</data>"
Ground the model in provided data. Explicit instruction to admit uncertainty.

The Full Anti-Pattern List

Anti-PatternProblemFix
"Be creative" without constraintsOutputs are wildly variable in tone, length, styleDefine what creative means: genre, tone, length, audience
Asking multiple questions in one promptModel answers some, skips others, or conflates themOne prompt, one task. Use multiple calls for multiple questions.
Assuming model knows your contextModel doesn't know your company, users, or domainProvide context explicitly in every prompt
Relying on ChatGPT-style "magic" phrases"Take a deep breath" worked on specific models; not transferableUse explicit, model-agnostic instructions
Never testing with adversarial inputsPrompt works on easy cases, breaks on edge casesInclude edge cases in your eval suite from the start
Modifying production prompts without eval"Small change" breaks other cases you didn't testRun full eval suite before any production prompt change
πŸ§ͺ

Lab 1 β€” Before/After Prompt Comparison

Write a "bad" prompt and a "good" prompt for the same task. Run both against Claude and see the output difference side by side. The best way to internalize the techniques is to observe the delta directly.

πŸ”¬

Lab 2 β€” Technique Comparison (Zero-Shot vs CoT vs Few-Shot)

Run the same task through three prompting strategies and compare outputs. See directly when each technique helps β€” and when the overhead isn't worth it.