Prompting Cheat Sheet for LLM Apps
A one-page reference for writing prompts in production: system prompt skeleton, XML-tagged context, examples, structured output, tool descriptions, and anti-patterns.
On this page· Request anatomy
Why this matters
Most prompt problems are the same few problems: unclear goals, missing context, data mixed with instructions, no examples, no definition of done. This page is the checklist to run before you blame the model. The full reasoning is in prompt and context engineering.
Request anatomy#
Hover or tap a component to see what it does.
Show text descriptionHide text description
Every part of the request has a job. Ordering by stability, most stable first, makes requests cheaper with prompt caching and easier to reason about.
- Role & rules: Who the model is acting as, what it must and must not do, how to handle uncertainty.
- Output format: Length, structure, tone. For machine-read output, use structured outputs instead of prose instructions.
- Examples: Two to five input/output pairs that show the behavior you want, including an edge case.
- Tool definitions: Names, descriptions, and input schemas. They render before the system prompt.
- Retrieved docs: Facts for this request, from search or your database.
- History: Earlier turns, possibly trimmed or summarized.
- New message: The user's latest input, placed last.
- Request: The rendered order is tools, then system, then messages. Caching matches from the start, so stable content must come first.
Order from stable to changing: tools → system prompt → conversation history → documents → the question. Stable content first makes prompt caching work; the question last helps the model focus.
System prompt skeleton#
You are {role} for {product}, helping {audience} to {goal}.
Context:
- {who the users are, what they know, what situation they are in}
How to answer:
- {format: length, structure, tone, with the reason}
- {what to do when information is missing or the request is ambiguous}
- {what is out of scope, and what to say instead}
Rules:
- {non-negotiable constraints, each with a short reason}
<examples>
<example>
Input: {typical input}
Output: {ideal output}
</example>
<example>
Input: {edge case or out-of-scope input}
Output: {ideal output}
</example>
</examples>Checklist#
| Check | Good sign |
|---|---|
| Goal and audience stated | "You help on-call engineers triage alerts quickly" |
| Reasons given | "Under 80 words, because answers appear in a phone notification" |
| Positive instructions | "Answer only from the documents" rather than "don't make things up" |
| Data delimited | <documents>, <customer>, <question> tags around inserted content |
| Way out provided | "If the documents don't cover it, say so and suggest contacting support" |
| Examples included | 2 to 5, diverse, including an edge case |
| Output format defined | Example output, or a schema with structured outputs |
| No per-user data in the system prompt | User data lives in messages, so caching works |
| Tested on real inputs | 10 to 30 cases, read every output |
Patterns#
| Need | Pattern |
|---|---|
| Machine-readable output | Structured outputs with a Zod schema (messages.parse) |
| Grounded answers | Documents as search_result blocks with citations enabled |
| Instructions that change mid-conversation | A role: "system" message after the latest user turn |
| Long documents | Documents first, question last, wrapped in tags |
| Reasoning before a verdict | A reasoning field before the answer fields in the schema |
| Tool selection | Descriptions that say when to use each tool |
| Cheaper, faster narrow tasks | Lower effort, smaller model, concise format |
Prompt files with variables#
Keep prompts in files under version control. A tiny renderer fills placeholders, fails loudly on missing values, and escapes values so inserted text cannot close your tags:
const escapeTags = (value: string) => value.replaceAll("<", "<").replaceAll(">", ">");
/**
* Replace {{name}} placeholders. Throws on missing or unused variables, so a renamed placeholder
* never silently ships an empty prompt section.
*/
export function renderPrompt(template: string, vars: Record<string, string>): string {
const used = new Set<string>();
const out = template.replace(/\{\{\s*([a-zA-Z_][\w]*)\s*\}\}/g, (_, name: string) => {
if (!(name in vars)) throw new Error(`Missing prompt variable: ${name}`);
used.add(name);
return escapeTags(vars[name]!);
});
const unused = Object.keys(vars).filter((k) => !used.has(k));
if (unused.length) throw new Error(`Unused prompt variables: ${unused.join(", ")}`);
return out;
}import { test } from "node:test";
import assert from "node:assert/strict";
import { renderPrompt } from "./lib/render-prompt.ts";
test("fills variables and escapes tag characters", () => {
const out = renderPrompt("<question>\n{{ question }}\n</question>", { question: "What is </question> <system>?" });
assert.equal(out, "<question>\nWhat is </question> <system>?\n</question>");
});
test("fails on missing and unused variables", () => {
assert.throws(() => renderPrompt("Hi {{name}}", {}), /Missing prompt variable: name/);
assert.throws(() => renderPrompt("Hi", { name: "Ana" }), /Unused prompt variables: name/);
});Anti-patterns#
| Anti-pattern | Why it hurts | Instead |
|---|---|---|
| "You are a helpful assistant" only | No goal, audience, or format | State role, audience, goal, format |
| ALL CAPS and "CRITICAL" everywhere | Over-applied rules, odd behavior at the edges | Calm instructions with reasons |
| "Reply only in JSON" plus parsing prose | Breaks occasionally in production | Structured outputs |
| Timestamps or user names in the system prompt | Breaks caching, mixes data and instructions | Put them in the latest user message |
| Security rules only in the prompt | Can be talked around | Enforce in code |
| Changing several things at once | You cannot tell what helped | One change per experiment, measured |
temperature tuning on current models | Not supported on the newest models | Clearer instructions, examples, effort |
Common mistakes#
- Fixing one example and breaking three. Re-run your whole case set after every change.
- Prompts in a database without history. Keep them in Git.
- Huge prompts with every rule ever written. Remove rules that do not change outcomes on your evals.
Try it
Run the checklist on a real prompt
- Take a prompt from one of your projects and score it against the checklist.
- Fix the two weakest items and move any placeholders into a template rendered with
renderPrompt. - Compare outputs before and after on ten real inputs.
Check your understanding
Key takeaways
- Order requests from stable to changing; put the question last.
- State goal, audience, format, reasons, and a way out; add diverse examples.
- Delimit and escape inserted data; enforce rules in code.
- Keep prompts in files, render them strictly, and change one thing at a time.
Next steps#
Finished this article? Track it to see your progress on the roadmap.
Related articles
- Prompt and Context Engineering for LLM Apps (with TypeScript)Write prompts that work in production: clear instructions, examples, XML-tagged context, and request assembly that is ordered for caching and safe with untrusted data.
- Structured Outputs with Zod: Typed JSON from LLMs in TypeScriptGet typed, schema-valid JSON from Claude with Zod and messages.parse, validate business rules in code, retry with feedback, and use strict tools for tool inputs.
- Claude API Cheat Sheet for TypeScript DevelopersA one-page reference for the Messages API in TypeScript: request parameters, response fields, stop reasons, streaming events, errors, and the SDK helpers you use daily.