Skip to content
Back to Blog
Agentic AI8 min read

Claude System Prompt Best Practices: Writing Instructions That Work

Master the art of writing effective system prompts for Claude. Covers structural patterns, role definition, constraint specification, output formatting, common mistakes, and advanced techniques for production-grade prompts.

Why System Prompts Are the Most Underrated Lever

The system prompt is the single most impactful parameter in any Claude API call. A well-crafted system prompt can transform a generic model into a domain expert. A poorly written one leads to inconsistent, off-topic, or unreliable responses that no amount of fine-tuning or temperature tweaking can fix.

Yet most teams treat system prompts as an afterthought -- a paragraph written in five minutes and never revised. This guide covers the patterns and principles that separate production-grade system prompts from amateur ones.

The Anatomy of an Effective System Prompt

Every effective system prompt contains four elements, in this order:

1. Role and Identity

Tell Claude who it is and what it does. Be specific about expertise level and domain.

You are a senior backend engineer specializing in Python, FastAPI, and PostgreSQL.
You have 10+ years of experience building high-traffic production systems.

Why it works: Claude calibrates its confidence, vocabulary, and depth of explanation based on the role. A "senior engineer" persona produces more concise, technical responses than a "helpful assistant" persona.

2. Task and Scope

Define what the system should do and, equally important, what it should not do.

Your job is to review code changes for bugs, security issues, and performance problems.
You do NOT refactor code, suggest style improvements, or rewrite implementations.
Focus exclusively on correctness and safety.

Why it works: Explicit scope boundaries prevent Claude from expanding into areas where you did not want it to go. The negative instructions ("do NOT") are as important as the positive ones.

3. Behavioral Constraints

Specify how the system should behave across all interactions.

Rules:
- Always cite the specific line number when identifying an issue
- Never suggest changes that would alter the public API
- If uncertain about a potential bug, flag it as "possible issue" rather than stating it definitively
- Respond in English only, regardless of the input language
- Never execute code or run commands -- analysis only

Why it works: Behavioral constraints create predictable, consistent behavior across thousands of interactions. Without them, Claude's behavior varies based on the user's phrasing.

4. Output Format

Define exactly how the response should be structured.

Format your review as:
## Summary
One paragraph overview of the change quality.

## Issues Found
For each issue:
- **File**: filename:line_number
- **Severity**: critical | warning | info
- **Description**: What the issue is
- **Suggestion**: How to fix it (code snippet if applicable)

## Verdict
APPROVE, REQUEST_CHANGES, or COMMENT

Why it works: Structured output is parseable, consistent, and easier for users (or downstream systems) to consume.

Structural Patterns

Pattern 1: The Persona Pattern

system_prompt = """You are Dr. Sarah Chen, a board-certified cardiologist with
20 years of clinical experience. You are reviewing patient intake forms
to identify cases that need urgent follow-up.

Your communication style: clinical, precise, evidence-based. You cite
medical literature when making recommendations. You never diagnose
directly from intake data -- you flag cases for physician review."""

Pattern 2: The Workflow Pattern

When the task involves a clear sequence of steps:

system_prompt = """You process customer support tickets through this workflow:

Step 1: CLASSIFY the ticket (billing, technical, account, general)
Step 2: ASSESS urgency (critical, high, medium, low)
Step 3: RETRIEVE relevant KB articles (search the knowledge base)
Step 4: DRAFT a response using the KB article as reference
Step 5: VERIFY the response addresses all points in the ticket

Always complete all five steps. Include your classification and
urgency assessment at the top of each response."""

Pattern 3: The Few-Shot Pattern

Include examples of ideal input-output pairs:

system_prompt = """You extract structured data from business emails.

Example input:
"Hi, I'd like to schedule a demo of your enterprise plan for our team of 50.
We're currently using Salesforce and need CRM integration.
Best time would be next Tuesday after 2pm EST. - John Smith, VP Engineering, Acme Corp"

Example output:
{
  "contact_name": "John Smith",
  "title": "VP Engineering",
  "company": "Acme Corp",
  "team_size": 50,
  "product_interest": "enterprise plan",
  "demo_requested": true,
  "preferred_time": "next Tuesday after 2pm EST",
  "integrations_needed": ["Salesforce", "CRM"],
  "current_tools": ["Salesforce"]
}

Now extract data from the provided email using the same JSON format.
Output only the JSON, no explanation."""

Pattern 4: The Guardrail Pattern

For safety-critical applications:

system_prompt = """You are a financial advisor chatbot for RetireWell.

HARD RULES (never violate):
- Never recommend specific stocks, bonds, or securities
- Never guarantee returns or use phrases like "guaranteed", "risk-free", "sure thing"
- Never provide tax advice -- redirect to "consult a tax professional"
- Never access, store, or reference specific account balances or SSNs
- If asked about anything outside retirement planning, respond:
  "I can only help with retirement planning questions."

SOFT GUIDELINES (prefer but can flex):
- Keep responses under 3 paragraphs
- Use simple language (8th grade reading level)
- Include a disclaimer when discussing projections"""

Common Mistakes

Mistake 1: Being Too Vague

# Bad
"You are a helpful assistant."

# Good
"You are a technical documentation writer for a Python web framework.
You write clear, concise docstrings and README sections.
Target audience: intermediate Python developers."

Mistake 2: Contradictory Instructions

# Bad (contradicts itself)
"Be concise. Provide thorough, detailed explanations for every point.
Keep responses short."

# Good (clear hierarchy)
"Default to concise responses (1-2 paragraphs).
When the user asks for detail or says 'explain more', provide thorough explanations.
Never exceed 5 paragraphs unless explicitly requested."

Mistake 3: Over-Constraining

# Bad (too rigid)
"Always respond in exactly 3 bullet points. Each bullet must be exactly one sentence.
Each sentence must be between 15 and 25 words."

# Good (flexible within boundaries)
"Use bullet points for key information. Keep each point to 1-2 sentences.
Aim for 3-5 bullets per response."

Mistake 4: Ignoring Edge Cases

# Bad (no edge case handling)
"Answer the customer's question using our product database."

# Good (handles unknowns)
"Answer the customer's question using our product database.
If the product is not in the database, say 'I don't have information about that product'
and suggest they contact support@example.com.
If the question is ambiguous, ask one clarifying question before answering."

Advanced Techniques

XML Tags for Complex Prompts

Claude is specifically trained to attend to XML-tagged content in prompts. Use XML tags to structure complex system prompts:

system_prompt = """<role>
You are a code migration specialist converting Java Spring Boot applications to Python FastAPI.
</role>

<knowledge_base>
Key mapping rules:
- @RestController -> @app (FastAPI router)
- @RequestMapping -> @app.get/post/put/delete
- @Autowired -> FastAPI Depends()
- ResponseEntity -> FastAPI Response classes
- JPA Repository -> SQLAlchemy/AsyncSession
</knowledge_base>

<output_format>
For each converted file:
1. Original Java code (commented for reference)
2. Equivalent Python/FastAPI code
3. Notes on any patterns that don't have direct equivalents
</output_format>

<constraints>
- Preserve all business logic exactly
- Use async/await for all database operations
- Add Pydantic models for all request/response bodies
- Include type hints on every function
</constraints>"""

Dynamic System Prompt Assembly

Build system prompts dynamically based on context:

def build_system_prompt(user_role: str, features: list[str]) -> str:
    base = "You are a customer support agent for TechCorp."

    role_context = {
        "free_tier": "This user is on the free plan. Do not discuss enterprise features.",
        "pro": "This user is a Pro subscriber. They have access to all standard features.",
        "enterprise": "This user is an Enterprise client. Offer white-glove support.",
    }

    feature_docs = {
        "billing": "Billing FAQ: [billing documentation here]",
        "api": "API documentation: [API docs here]",
        "integrations": "Integration guides: [integration docs here]",
    }

    parts = [base, role_context.get(user_role, "")]
    for feature in features:
        if feature in feature_docs:
            parts.append(feature_docs[feature])

    return "\n\n".join(parts)

Prompt Versioning

Track and version your system prompts like code:

PROMPT_VERSIONS = {
    "support_v1": {
        "prompt": "You are a helpful support agent...",
        "created": "2026-01-01",
        "notes": "Initial version",
    },
    "support_v2": {
        "prompt": "You are a customer support specialist...",
        "created": "2026-01-15",
        "notes": "Added billing FAQ, improved edge case handling",
    },
    "support_v3": {
        "prompt": "You are a senior customer support specialist...",
        "created": "2026-02-01",
        "notes": "Added integration troubleshooting, reduced response length",
    },
}

def get_prompt(name: str, version: str = "latest") -> str:
    if version == "latest":
        versions = [k for k in PROMPT_VERSIONS if k.startswith(name)]
        version = sorted(versions)[-1]
    return PROMPT_VERSIONS[version]["prompt"]

Testing System Prompts

Never deploy a system prompt change without testing. Create evaluation datasets:

test_cases = [
    {
        "input": "What's your opinion on Bitcoin?",
        "expected_behavior": "Declines to give investment advice",
        "must_contain": [],
        "must_not_contain": ["buy", "invest", "recommend"],
    },
    {
        "input": "My payment failed, help!",
        "expected_behavior": "Provides billing troubleshooting steps",
        "must_contain": ["payment method", "support"],
        "must_not_contain": [],
    },
    {
        "input": "Can you write me a poem?",
        "expected_behavior": "Redirects to support scope",
        "must_contain": ["help with", "support"],
        "must_not_contain": ["rose", "poem"],
    },
]

async def evaluate_prompt(system_prompt: str, test_cases: list) -> dict:
    results = {"passed": 0, "failed": 0, "failures": []}

    for case in test_cases:
        response = await client.messages.create(
            model="claude-sonnet-4-5-20250514",
            max_tokens=1024,
            system=system_prompt,
            messages=[{"role": "user", "content": case["input"]}],
        )
        text = response.content[0].text.lower()

        passed = True
        for term in case["must_contain"]:
            if term.lower() not in text:
                passed = False
                break
        for term in case["must_not_contain"]:
            if term.lower() in text:
                passed = False
                break

        if passed:
            results["passed"] += 1
        else:
            results["failed"] += 1
            results["failures"].append({
                "input": case["input"],
                "expected": case["expected_behavior"],
                "got": response.content[0].text[:200],
            })

    return results

The Iteration Cycle

The best system prompts are not written once. They are iterated through this cycle:

  1. Write the initial prompt based on requirements
  2. Test against 20-50 representative inputs
  3. Analyze failures to identify patterns
  4. Revise the prompt to address failure patterns
  5. Re-test to verify improvements without regressions
  6. Deploy with monitoring
  7. Monitor real-world performance and collect edge cases
  8. Return to step 3 with new failure data

Production teams typically go through 5-10 iterations before a system prompt stabilizes. Budget time for this process -- it is where the real quality improvement happens.

Share this article
N

NYC News

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.