Foundations · Prompt Fundamentals

Prompt Engineering Fundamentals

Before you reach for tools, agents, or RAG, most quality problems are solved by writing a better prompt. This page covers the five techniques that account for the majority of that improvement.

☺ Explain it like I'm 10

Imagine you just started a new job and your manager hands you a task with almost no detail — no examples, no explanation of why it matters, no sense of how far to go. You'd have to guess, and you might guess wrong. Prompt engineering is just writing the kind of instructions you'd give a brand-new coworker who is extremely fast but has never worked with you before: the clearer, more example-filled, and better-organized your instructions, the less guessing they have to do — and the better the work you get back.

🦊Your host for this topic: Foxy (asks the exact question you're thinking right before you hit send on a vague prompt).

Prompt engineering assumes you already have a way to judge whether an output is good — you'll build that muscle properly on the evaluation page. For now, treat every example below as something you can run yourself and compare against the "before" version. The techniques compose: by the end of this page you'll see how to combine all five into one much more reliable prompt.

Be clear and direct

☺ Like you're 10: Telling Claude "build a dashboard" is like asking someone to "make dinner" without saying who's eating, what's in the fridge, or when it's due — they'll guess, and you might not like the guess.

Claude has no access to your team's conventions, your product's edge cases, or the context sitting in your head. Anthropic's own guidance frames Claude as a new hire on day one: capable, but unable to read your mind about what "good" looks like unless you say so. A useful test before sending any prompt: would a new colleague, handed only this text, produce the output you want? If they'd have to guess, so will Claude.

◆ Pattern

Spell out exactly what you want: "Create an analytics dashboard. Include as many relevant features and interactions as possible. Go beyond the basics to create a fully-featured implementation."

⚠ Anti-pattern

A bare "Create an analytics dashboard" leaves scope, feature depth, and ambition entirely up to guesswork — Claude has no way to know how far you want it to go.

Being direct also means being direct about wanting action, not commentary. If you want Claude to actually change something rather than describe how it might, say so:

◆ Pattern

"Change this function to improve its performance." Claude will make the changes.

⚠ Anti-pattern

"Can you suggest some changes to improve this function?" Claude will only suggest — asking for a review gets a review, not a rewrite.

It also helps to explain why an instruction matters, not just what it is. A bare rule like "never use ellipses" gets followed too literally or missed in edge cases; telling Claude the output will be read aloud by a text-to-speech engine that can't pronounce ellipses lets it generalize the rule correctly on its own.

⌁ Note

Phrase constraints as what Claude should do, not what it shouldn't. "Your response should be composed of smoothly flowing prose paragraphs" is followed more reliably than "Do not use markdown" — positive instructions give Claude a target to hit instead of an infinite space of things to avoid.

Show, don't just tell: multishot prompting

☺ Like you're 10: Showing 3-5 examples is like folding three loads of laundry together with a kid before handing them the basket — they pick up the pattern far faster than from a one-paragraph rulebook.

Examples are one of the most reliable levers for steering Claude's output format, tone, and structure — often more effective than a longer written description of what you want. The current guidance is to use somewhere between 3 and 5 examples: diverse enough to cover the edge cases you care about, but not so many that they start teaching patterns you didn't intend. Wrap each one in <example> tags, nested inside an outer <examples> block, so Claude can clearly tell your examples apart from your instructions.

Good examples are relevant (they mirror your real use case), diverse (they cover the range of inputs you'll actually see), and clearly structured. Here's a ticket-triage prompt with three examples, followed by the real input to classify:

Python
import anthropic

client = anthropic.Anthropic()

system_prompt = (
    "You are a support-ticket triage assistant. Classify each ticket "
    "into exactly one category: billing, bug, feature_request, or other. "
    "Respond with only the category name."
)

user_prompt = """<examples>
<example>
<ticket>I was charged twice for my subscription this month.</ticket>
<category>billing</category>
</example>
<example>
<ticket>The export button does nothing when I click it in Safari.</ticket>
<category>bug</category>
</example>
<example>
<ticket>It would be great if I could schedule reports to send automatically.</ticket>
<category>feature_request</category>
</example>
</examples>

<ticket>My invoice total doesn't match what the pricing page shows.</ticket>"""

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=20,
    system=system_prompt,
    messages=[{"role": "user", "content": user_prompt}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)
JavaScript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const systemPrompt =
  "You are a support-ticket triage assistant. Classify each ticket " +
  "into exactly one category: billing, bug, feature_request, or other. " +
  "Respond with only the category name.";

const userPrompt = `<examples>
<example>
<ticket>I was charged twice for my subscription this month.</ticket>
<category>billing</category>
</example>
<example>
<ticket>The export button does nothing when I click it in Safari.</ticket>
<category>bug</category>
</example>
<example>
<ticket>It would be great if I could schedule reports to send automatically.</ticket>
<category>feature_request</category>
</example>
</examples>

<ticket>My invoice total doesn't match what the pricing page shows.</ticket>`;

const message = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 20,
  system: systemPrompt,
  messages: [{ role: "user", content: userPrompt }]
});

for (const block of message.content) {
  if (block.type === "text") {
    console.log(block.text);
  }
}

Without the examples, Claude has to guess at your taxonomy — is a billing dispute "billing" or "other"? With three labeled examples it has a pattern to match against, and the output becomes far more consistent across ambiguous inputs.

⌁ Note

An advanced variant of this technique: put a <thinking> tag inside each few-shot example showing the reasoning you want Claude to follow, not just the final output. Claude tends to generalize that reasoning style into its own responses — a useful bridge into the next technique.

Let Claude think: chain-of-thought reasoning

☺ Like you're 10: It's like asking someone to show their work on a math test instead of just writing the final number — seeing the steps is what catches the mistake, for them and for you.

Asking Claude to reason before answering reduces errors on math, multi-step logic, and decisions with several interacting factors — and gives you a trace you can read to figure out where a prompt itself is ambiguous. There are three escalating ways to ask for it:

LevelWhat you do
BasicSimply add "think step-by-step" to your prompt.
GuidedOutline the specific steps you want Claude to work through.
StructuredSeparate the reasoning from the final output using XML tags, typically <thinking> and <answer>, so you can parse out just the part you need.

The structured version is useful whenever you need to programmatically extract just the answer:

import anthropic

client = anthropic.Anthropic()

prompt = """A SaaS product costs $49/month billed monthly, or $470/year billed annually.

Work out how many months a customer needs to stay subscribed before the
annual plan is cheaper overall, and how much they will have saved after
18 months on the annual plan instead of the monthly plan.

Show your reasoning inside <thinking> tags, then give only the final
numbers inside <answer> tags."""

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=500,
    messages=[{"role": "user", "content": prompt}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)

The important caveat here, straight from Anthropic's documentation: "Without outputting its thought process, no thinking occurs." Asking Claude to "think carefully" without giving it anywhere to put that thinking doesn't help — the value comes specifically from the tokens Claude gets to spend reasoning out loud before committing to an answer.

⌁ Note

Claude's current top-tier models (Sonnet 5, Opus 5, Fable 5) use adaptive thinking by default, deciding on their own when and how much to reason based on the query and an effort parameter, covered on a later page. Manual chain-of-thought prompting like the example above is still useful as an explicit fallback: for models like Haiku 4.5 that don't have adaptive thinking, or whenever you specifically need a visible, parseable reasoning trace rather than reasoning the model does internally.

Structure prompts with XML tags

☺ Like you're 10: XML tags are like labeled moving boxes — "KITCHEN," "BOOKS," "FRAGILE" — nothing gets lost, and whoever unpacks the truck instantly knows what goes where.

Once a prompt mixes instructions, background context, examples, and the actual input you want processed, plain prose makes it easy for the model — and for you, six months later — to lose track of which part is which. XML tags fix this: wrap each kind of content in a consistently named tag (<instructions>, <context>, <examples>, <document>) and refer back to those tag names elsewhere in the prompt. Tags nest naturally too — several <document> blocks can sit inside one outer <documents> block.

This buys you three things: clarity (the model parses structure instead of inferring it), accuracy (fewer misread boundaries between your instructions and your data), and flexibility (you can add, remove, or reorder a section without rewriting the whole prompt).

import anthropic

client = anthropic.Anthropic()

prompt = """<document>
<source>vendor_contract.pdf</source>
<document_content>{{CONTRACT_TEXT}}</document_content>
</document>

<instructions>
Identify every auto-renewal clause in the document above and quote the
exact sentence that triggers it. If there is no auto-renewal clause,
say so explicitly instead of guessing.
</instructions>"""

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1000,
    messages=[{"role": "user", "content": prompt}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)

Notice the document comes before the instructions, not after. For long inputs, Anthropic's guidance is to put the bulk content near the top of the prompt and the actual question or instructions at the end — placing the query last has been shown to meaningfully improve response quality on long, multi-document inputs. The {{CONTRACT_TEXT}} placeholder above is the same double-curly-brace convention used in the Console's prompt templates, marking a variable slot that gets filled in per request rather than hardcoded.

Give Claude a role with the system prompt

☺ Like you're 10: It's the difference between asking "is this a good deal?" and asking a mechanic, a lawyer, and an accountant the same question about the same car — three very different, more useful answers.

Setting a role in the system parameter is described in Anthropic's own docs as the single most powerful use of system prompts — it can turn a general-purpose assistant into a domain expert with as little as one sentence. The pattern is simple: put the persona in system, and put the task-specific instructions in the user turn. Under the hood, system is its own top-level field that sits directly alongside messages in the request — a sibling parameter, not something buried inside your prompt text.

◆ Pattern

system: "You are a senior security engineer reviewing pull requests for a fintech company." user: "Review this code." The same request now surfaces auth and data-exposure risks a generic reviewer would miss.

⚠ Anti-pattern

No system prompt, just "Review this code." — generic, surface-level feedback that could apply to almost any codebase.

Different roles surface different insights from identical input — a data scientist and a marketing strategist reading the same sales numbers will each notice different things. Choose the role based on what kind of insight you actually need out of the response, not just for flavor.

Putting it together

None of these techniques are exclusive — the biggest gains usually come from combining clarity, structure, and role in a single prompt. Compare a typical first-draft request for a code review against a version that stacks direct instructions, a role, and XML-tagged input:

◆ Pattern

system: "You are a senior backend engineer reviewing code for a payments team." user: "<diff>{{DIFF}}</diff> Review the diff above for security issues, N+1 queries, and missing error handling. For each issue found, quote the exact line and explain the risk in one sentence. If you find no issues in a category, say so explicitly rather than omitting it."

⚠ Anti-pattern

"Can you look at this diff and tell me what you think?" with the raw diff pasted below — no tags, no role, no output format — leaves the lens, the input boundaries, and the shape of a good answer all undefined.

The second version leaves far less to chance: the role sets the lens, the XML tag makes the diff unambiguous input rather than part of the instructions, and the direct, specific ask ("quote the exact line," "explain the risk in one sentence") removes the guesswork about what a good answer looks like.

Rolesystem persona Direct askspecific, action-oriented XML tagsunambiguous input Reliable outputlittle left to guess
⚠ Careful

More technique is not always better. Stacking every trick in this page onto a simple, low-stakes prompt adds length and latency without improving the output — and current models are more literal about following instructions than older ones, so leftover urgent phrasing like "CRITICAL: you MUST..." from an earlier prompt version can now cause the opposite problem, over-triggering behavior you didn't want. Start simple, add a technique only when a prompt is actually failing in a way that technique fixes, and prefer plain phrasing over forceful language.

🎬 At the Claude Crew
🦊

Foxy: Wait — so I should XML-tag everything, give it a role, ask for step-by-step thinking, AND drop in five examples, every single time?

🦉

Professor Owl: Only if the prompt is actually failing. Every technique adds length and latency — piling cures onto a prompt that isn't sick doesn't make it healthier.

🦉

Professor Owl: And watch leftover urgency — "CRITICAL: you MUST..." left over from an older prompt can now over-trigger behavior you never wanted, because today's models follow instructions very literally.

🦊

Foxy: So the real question is never "which techniques exist" — it's "which one fixes the specific way this prompt is failing right now." Start simple, add on demand.

✎ Try it yourself

Pick a prompt you already send to Claude regularly — summarizing an email, drafting a commit message, extracting data from a document. Write three versions: (1) the original, (2) the same task with a one-sentence role added via system, and (3) version 2 plus 2-3 <example> pairs showing your exact desired output format. Run all three with the same input using the Python or JavaScript SDK and compare the outputs side by side.

🦊 Foxy's checkpoint

You should now be able to explain why vague prompts produce vague output, when to reach for examples versus chain-of-thought versus XML structure, and why a role belongs in system while the task belongs in the user turn. Next up: push these techniques further on advanced prompting.

Check your answers
  1. Why does a vague instruction like "Create an analytics dashboard" produce inconsistent results? Claude has no access to your intended scope, so without stated criteria for features, depth, and ambition it has to guess — the same way a new colleague handed only that sentence would.
  2. Why doesn't telling Claude to "think carefully" help unless you also give it somewhere to write that thinking? Per Anthropic's own documentation, no visible thinking means no thinking occurs — the benefit comes specifically from the tokens Claude spends reasoning out loud before it commits to an answer, so a hidden instruction to "think" has nowhere to do that work.
  3. Where do the persona and the task-specific instructions go, and why does it matter? The persona goes in the system parameter, a top-level field sibling to messages; the task goes in the user turn. Separating them means even a one-sentence role change can shift what the same task surfaces, without touching the task itself.