Write Clear Prompts

Su Suriya Ravichandran Updated 10 Nov 2026
9 min read ·Lesson 31 of 200

What a Prompt Is

A prompt is everything you send to Claude to describe what you want: the system prompt, the user message, any documents or examples, and the conversation history. Earlier lessons focused on how to send prompts with the SDK. This section, Lessons 31–40, focuses on what to write, because the quality of the prompt is the biggest factor in the quality of the answer.

Clear prompting is a practical skill, not a trick. The same principle applies to people: a vague request to a capable new colleague produces a guess, and a clear request produces what you actually need.


The Golden Rule

Anthropic's prompting guidance offers a simple test: show your prompt to a colleague who has minimal context on the task, and ask them to follow it. If they would be confused, Claude will be too.

Claude is highly capable, but it does not know:

  • your project, your users, or your company's conventions,
  • why you need the output, or what you will do with it,
  • what "good" looks like for this task, unless you say so, or
  • anything you have not written down.

Every clarity technique in this lesson follows from closing those gaps.


Principle 1: Be Explicit About the Task

State exactly what you want done. Do not leave Claude to infer the task from a topic.

Vague:     Python decorators.

Clear:     Explain what a Python decorator is to a developer who knows functions but
           has never used decorators. Include one example that times how long a
           function takes to run.

The clear version specifies the action (explain), the audience (knows functions, new to decorators), and a concrete deliverable (one timing example).

Ask Explicitly for Extra Effort

If you want a thorough, ambitious, or comprehensive result, say so. Anthropic's guidance notes that "above and beyond" behavior should be requested explicitly rather than expected from a short prompt:

Build a contact form page. Include validation, helpful error messages, accessible labels,
and a confirmation state after submission. Go beyond a minimal version.

Principle 2: Specify the Output

Describe what the answer should look like: its format, length, structure, and level of detail.

AspectExample instruction
Format"Answer as a numbered list." / "Return a Markdown table with columns Name, Purpose, Example."
Length"Use at most 3 sentences." / "About 200 words."
Structure"Start with a one-sentence summary, then give the details."
Detail level"Assume the reader is a beginner; define technical terms."
Exclusions"Return only the code, with no explanation."

Why length deserves explicit attention: current Claude models have a more concise, direct communication style than earlier models. If you need a long, detailed answer, ask for it. If you need a very short one, ask for that too. Do not rely on max_tokens to control length; it only cuts responses off (Lesson 13).


Principle 3: Explain Why

Giving the reason behind an instruction helps Claude apply it intelligently, including in situations your instruction did not anticipate.

Without reason:  Do not use abbreviations.

With reason:     Do not use abbreviations, because this text will be translated
                 automatically and the translation tool handles abbreviations poorly.

With the reason, Claude understands the real goal, which is translation-friendly text. It may also avoid other things that hurt translation, such as slang or idioms, without being told. The documentation describes this directly: Claude can generalize from the explanation.


Principle 4: Say What to Do, Not Only What to Avoid

Positive instructions are clearer than prohibitions. "Do not do X" leaves open what to do instead.

Instead ofTry
"Don't use Markdown.""Write your answer as plain paragraphs of prose."
"Don't be too technical.""Explain in everyday language, as you would to a non-programmer."
"Don't write long answers.""Answer in at most two sentences."

Prohibitions are still useful for real boundaries, such as "Never include API keys in examples". When possible, pair them with what you want instead.


Principle 5: Use Steps When Order Matters

When a task has several parts that must happen in order, or all of which must be completed, list them as numbered steps:

Review the function below.
1. Identify any bugs and explain each in one sentence.
2. Suggest a fix for each bug.
3. Rewrite the complete function with all fixes applied.
4. Add a docstring to the rewritten function.

Why it helps: numbered steps make the sequence and the completeness requirement explicit. Claude is much less likely to skip a step, and you can easily check whether each one was done.


Principle 6: Remove Ambiguity

Ambiguous words force Claude to guess. Replace them with specifics:

AmbiguousSpecific
"a short summary""a summary of at most 50 words"
"the latest version""Python 3.13"
"clean up this code""rename variables to be descriptive and split the function into smaller functions"
"make it better""make it more persuasive for small-business owners"
"recent changes""changes made after 1 October 2026"

Before and After: A Complete Example

Before:

Review my code.

After:

You are reviewing a pull request for a Django project used by about 500 students.
Review the view function below for security and correctness. This project has had
problems with unauthorized access, so prioritize those.

<code>
{code}
</code>

1. List any security problems first, then correctness problems.
2. For each problem, give the line, a one-sentence explanation, and a fix.
3. If you find no problems in a category, say "None found" for that category.

Keep the review under 250 words. Use a Markdown list.

The improved prompt adds:

  • context: a Django project with 500 students,
  • the reason for the priority: past unauthorized-access problems,
  • clearly separated data in <code> tags (Lesson 37),
  • ordered steps,
  • behavior for an edge case ("None found"), and
  • output constraints: length and format.

Practical Example: A Prompt Builder

Writing clear prompts consistently is easier with structure. This small builder turns the principles into fields, so a prompt cannot be sent without a task, and the other parts are added in a consistent order.

"""prompt_builder.py: assemble clear prompts from explicit parts."""

from dataclasses import dataclass, field

from anthropic import Anthropic
from anthropic.types import Message


@dataclass
class PromptSpec:
    task: str                                   # What to do (required)
    audience: str = ""                          # Who the output is for
    reason: str = ""                            # Why it matters
    steps: list[str] = field(default_factory=list)
    output_format: str = ""                     # Format and length requirements
    data: dict[str, str] = field(default_factory=dict)   # Tag name -> content

    def render(self) -> str:
        if not self.task.strip():
            raise ValueError("A prompt needs a task.")

        parts = []
        for tag, content in self.data.items():           # Data first (Lesson 18)
            parts.append(f"<{tag}>\n{content}\n</{tag}>")
        parts.append(self.task)
        if self.audience:
            parts.append(f"Audience: {self.audience}")
        if self.reason:
            parts.append(f"Why this matters: {self.reason}")
        if self.steps:
            parts.append("\n".join(f"{n}. {step}" for n, step in enumerate(self.steps, start=1)))
        if self.output_format:
            parts.append(f"Output format: {self.output_format}")
        return "\n\n".join(parts)


def get_text(message: Message) -> str:
    return "".join(block.text for block in message.content if block.type == "text").strip()


if __name__ == "__main__":
    spec = PromptSpec(
        task="Explain what the function below does and suggest one improvement.",
        audience="Beginner Python students who know loops but not comprehensions.",
        reason="Students will use this explanation to learn idiomatic Python.",
        steps=[
            "Explain the function's purpose in one sentence.",
            "Walk through the loop line by line.",
            "Rewrite it using a list comprehension and explain the change.",
        ],
        output_format="Markdown, under 200 words, with the rewritten code in a code block.",
        data={"code": "def squares(n):\n    result = []\n    for i in range(n):\n        result.append(i * i)\n    return result"},
    )

    prompt = spec.render()
    print(prompt, "\n" + "=" * 60)

    reply = Anthropic().messages.create(
        model="claude-opus-5-5",
        max_tokens=2048,
        messages=[{"role": "user", "content": prompt}],
    )
    print(get_text(reply))

The rendered prompt:

<code>
def squares(n):
    result = []
    for i in range(n):
        result.append(i * i)
    return result
</code>

Explain what the function below does and suggest one improvement.

Audience: Beginner Python students who know loops but not comprehensions.

Why this matters: Students will use this explanation to learn idiomatic Python.

1. Explain the function's purpose in one sentence.
2. Walk through the loop line by line.
3. Rewrite it using a list comprehension and explain the change.

Output format: Markdown, under 200 words, with the rewritten code in a code block.

How It Works

  • PromptSpec makes each principle a field: task, audience, reason, steps, output format, and data. Writing a prompt becomes filling in a checklist.
  • render() refuses an empty task, so a prompt always has a clear action.
  • Data comes first, wrapped in tags, followed by the instructions. This follows the "long data before the question" guidance (Lessons 18 and 33).
  • Steps are numbered automatically with enumerate(..., start=1).
  • Optional parts are included only when provided, so short prompts stay short.

The task says "the function below", but the code appears above it. Exercise 3 asks you to fix this kind of inconsistency. Reading your rendered prompts with fresh eyes catches such mistakes.


Common Mistakes

Writing a Topic Instead of a Task

What happens: "Python decorators" produces a generic overview that may not match what you need.

Fix: state the action, the audience, and the deliverable.

Assuming Claude Knows Your Context

What happens: answers follow general conventions instead of yours.

Fix: include the relevant context: project, users, constraints, and purpose (Lesson 33).

Relying Only on Prohibitions

What happens: "don't do X" is followed, but the alternative Claude chooses is not what you wanted.

Fix: state the desired behavior positively.

Vague Quality Words

What happens: "better", "short", and "clean" mean different things to different readers.

Fix: replace them with measurable or concrete criteria.

Packing Many Unrelated Tasks into One Prompt

What happens: some tasks get detailed answers and others are skipped.

Fix: use numbered steps for related parts, or split unrelated tasks into separate requests or turns (Lesson 28).


Best Practices

  • Apply the colleague test before sending important prompts.
  • State the task, audience, and deliverable explicitly.
  • Specify format and length, and request extra depth or brevity when you need it.
  • Explain the reasons behind important instructions.
  • Phrase instructions positively, and keep prohibitions for real boundaries.
  • Number the steps when order or completeness matters.
  • Replace vague words with concrete criteria.
  • Iterate: compare outputs, adjust the prompt, and test again (Lesson 39).

Exercise

  1. Rewrite each vague prompt into a clear one: "Write about SQL joins.", "Make my email better.", "Give me some project ideas."
  2. Take one of your rewritten prompts and add a reason to one of its instructions. Compare Claude's output with and without the reason.
  3. In the PromptSpec example, the task says "the function below", but render() places the data above the task. Fix the inconsistency in the task text, and explain why reading the rendered prompt helps catch this kind of mistake.
  4. Add an optional examples field to PromptSpec that renders each example inside <example> tags (Lesson 35).
Show answers for steps 3 and 4

Step 3: change the task to "Explain what the function above does and suggest one improvement." Reading the final rendered prompt, rather than only the code that builds it, shows exactly what Claude receives. Problems such as wrong references, missing data, or contradictory instructions are easy to miss in code and obvious in the rendered text.

Step 4:

examples: list[str] = field(default_factory=list)

# In render(), before the task:
if self.examples:
    rendered = "\n".join(f"<example>\n{example}\n</example>" for example in self.examples)
    parts.append(f"<examples>\n{rendered}\n</examples>")

Wrapping each example in <example> tags, and the group in <examples>, helps Claude distinguish examples from instructions and data.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Write Clear Prompts and get answers drawn from it.

Signed-in readers only.