A prompt template is a prompt with holes in it. The engineering — role, rules, examples, output format — is written once by someone who knows what they're doing; the holes — this quarter's report, this customer's ticket, this contract — are filled per use, by people or systems that never touch the engineering. Templates are how prompt quality scales past the person who wrote the prompt, and how a business workflow ("summarize every incoming RFP the same way") stops depending on whoever happens to run it.
Anatomy: fixed shell, tagged holes
A production template separates cleanly into a static shell and variable slots, and Anthropic's documented XML-tag convention is the natural slot syntax — wrap each variable input in its own descriptive tag so the boundary between engineering and data is explicit to Claude and to your code:
TEMPLATE_SYSTEM = """You are an RFP analyst for {company}.
<rules>...</rules>
<output_format>...</output_format>
<examples>...</examples>"""
def render_user_turn(rfp_text: str, deadline: str) -> str:
return (f"<rfp>{rfp_text}</rfp>\n"
f"<deadline>{deadline}</deadline>\n"
"Analyze <rfp> per the rules.")
Two design rules follow from how the platform works:
- Static content up front, variables at the end. Prompt caching is a strict prefix match: everything before the first changed byte can be served from cache at one-tenth the input price, and one changed byte invalidates everything after it. A template whose shell is genuinely static — same bytes every call — caches beautifully; one that interpolates a timestamp or user ID into the system prompt silently pays full price on every request. The official docs list exactly these as common silent invalidators, alongside non-deterministic JSON serialization.
- Long documents above, question below. When a slot holds 20k+ tokens of input, the documented long-context pattern applies: data at the top, instructions and query at the end — worth up to 30% response quality on long inputs.
Constrain the holes, not just the shell
What makes a template safe for non-engineers isn't the fixed part — it's that the variable parts are validated. Treat template variables like function parameters: type-check them, length-check them, and escape or reject input that contains your own tag names (a user pasting </rfp> shouldn't be able to close your tag early — see input validation). On the output side, pair the template with structured outputs where downstream systems consume the result, so a template user can't break the parser by phrasing a request oddly.
Templates are versioned artifacts
Once three teams depend on a template, editing it in place is a production change. The practices that keep this sane are ordinary engineering hygiene: keep templates in version control, not in a wiki; record which model and parameters each template targets (prompts are tuned to models — a template written for one model should be re-evaluated before pointing at another, and note that token counts themselves are model-specific); and gate changes on an eval run, not on "looks better to me." The official prompt-engineering overview is explicit about the prerequisite: success criteria and empirical tests come before prompt iteration. Prompt versioning and prompt library governance cover the organizational side.
Worth knowing: the Claude Console (on the first-party platform) ships tooling for exactly this workflow — a prompt generator, template/variable support, and a prompt improver — useful for drafting and iterating before a template graduates into your codebase.
One template, four platforms
Because a template is just strings assembled into Messages API calls, the same template runs unchanged against every Claude surface — only the client and model ID differ (for example, AnthropicVertex with claude-opus-4-8 on Google Cloud, or AnthropicBedrockMantle with anthropic.claude-opus-4-8 on Bedrock). Keep the template layer free of platform-specific assumptions and your prompt library becomes portable across clouds — a genuine procurement asset. One platform nuance: caching works on all four, but Amazon Bedrock supports explicit cache breakpoints only, so set cache_control markers deliberately rather than relying on automatic placement.
Where to go next
Start from system prompt design for what belongs in the shell, add few-shot examples to lock format, and see the platform overview for client setup on each cloud.