How to Write Better Prompts for Claude Code

Most developers send Claude Code vague requests and wonder why the output needs three rounds of clarification. The fix is not writing essays — it is five patterns that take ten seconds to apply, and knowing when to let automatic optimization handle it for you.

Why Vague Prompts Cost You More Time Than They Save

There is a counterintuitive asymmetry at the heart of AI-assisted coding. Writing a vague prompt feels fast: five words, hit enter. But you pay for that speed downstream. The output misses a constraint, handles the wrong error case, or returns the wrong format — and you spend two minutes correcting it, re-prompting, and waiting again.

A prompt that takes fifteen seconds to write well can eliminate two rounds of back-and-forth. That is not a philosophical claim about prompt engineering; it is arithmetic.

The hidden cost of prompt ambiguity

Consider the difference between two prompts for the same task:

Vague

write middleware that validates tokens and handles refresh

Specific

Write a FastAPI middleware function that (1) validates JWT tokens from the Authorization header, (2) calls /auth/refresh if the token is expired, (3) returns HTTP 401 if the refresh also fails. Return the request.state.user dict. Use httpx for the refresh call.

The specific version takes maybe twenty seconds to write. It eliminates: which framework, which token standard, which header, what refresh behavior, what error response, and what the middleware should set on the request state. That is six clarifying questions Claude Code will not need to ask, and six ways the output cannot diverge from what you actually want.

~40%

of prompts in a typical Claude Code session score 38 or above on PrePrompt's heuristic classifier — indicating ambiguity, multi-requirement density, or missing context that makes them candidates for optimization.

The Five Prompt Patterns That Work in Claude Code

1. Specify the output format, not just the task

Claude Code cannot infer what shape of output you want unless you tell it. "Write a function to handle OAuth" is a task. "Write a Python async function named exchange_code_for_token that takes a code: str parameter and returns a TokenResponse dataclass" is a task with a specified output format.

The output format signals: language, sync vs async, function vs class vs module, return type, parameter names and types, and naming conventions. All of these are guesses if you do not specify them. Each guess is a source of a follow-up prompt.

2. State technical constraints upfront

Claude Code has no idea you are using FastAPI with Pydantic v2 and Python 3.11 unless you say so. Even if it has read your files, it does not know which version constraints matter to you, which library is already in scope, or what your existing code style requires.

Put constraints at the top of your prompt: language version, framework, libraries already imported, coding style expectations, deployment target. Ten words of context at the start prevents twenty words of correction at the end.

3. Number multi-step requirements: (1), (2), (3)

When a prompt contains more than one requirement, prose runs them together in a way that is easy for the model to conflate or partially miss. Numbered requirements force the model to treat each item discretely.

Compare: "handle token refresh with proper error handling and logging" versus "(1) attempt token refresh by calling POST /auth/refresh, (2) log the refresh attempt with the user ID and timestamp, (3) raise AuthTokenExpiredError if the refresh returns 401 or 403". The second version cannot be partially implemented — each requirement is checkable.

4. Include the error scenarios you care about

Claude Code will handle exceptions — but which ones? Network timeouts? Invalid tokens? Rate limiting? Database unavailability? If you do not specify, it will make reasonable defaults that may not match your actual requirements. Listing the error scenarios you care about takes five seconds and eliminates an entire category of follow-up.

Useful phrasing: "Handle the following error cases: (a) network timeout after 5 seconds, (b) 401 from the upstream API, (c) malformed JSON response." You do not need to be exhaustive — just explicit about the cases that matter to your deployment.

5. Reference the existing file or function name when modifying code

"In auth/middleware.py, the function validate_token currently returns a boolean — change it to return the decoded payload dict or raise AuthError" is unambiguous. "Update the token validation function" requires Claude Code to find the right file, find the right function, guess the current signature, and guess what you want changed.

Grounding your prompt in the actual file and function name costs nothing and makes the output immediately correct for your codebase rather than a generic template.

What Your Prompt Score Means (and When to Optimize)

PrePrompt's heuristic classifier scores prompts on a 0–100 scale. The signals driving the score are:

Q: How do I know if my Claude Code prompt needs optimization?

A: A prompt likely needs optimization if it contains vague action verbs ("handle", "manage", "deal with"), combines multiple requirements without numbering them, or omits the output format and error scenarios. The PrePrompt heuristic classifier scores these signals automatically in under 1ms on every prompt submitted through Claude Code.

Q: What is the threshold score for prompt optimization in PrePrompt?

A: PrePrompt uses a threshold of 38 on a 0–100 heuristic scale. Prompts scoring below 38 — short questions, already-structured requests, lookup queries — pass through untouched with zero latency and zero API cost. Only prompts at or above 38 trigger a Claude Haiku rewrite, adding approximately 1–2 seconds.

How Automatic Prompt Optimization Works

PrePrompt sits between your keyboard and Claude Code. Every time you submit a prompt, four things happen in sequence:

  1. The UserPromptSubmit hook intercepts the prompt before it reaches the LLM.
  2. The heuristic classifier scores the prompt locally in under 1ms — no API call, no latency.
  3. If the score is below 38, the prompt passes through untouched.
  4. If the score is 38 or above, Claude Haiku rewrites it, adding specificity and structure while preserving your original intent exactly.

The rewritten prompt is what Claude Code receives. You see the annotation in your terminal. The original and optimized versions are both logged locally in ~/.preprompt/history.db.

To install:

# Install
pip install preprompt
 
# Register hooks for Claude Code (and Cursor if installed)
preprompt-install

Before and After: Three Real Examples

Example 1 — middleware prompt score: 48 — intercepted

Original

write me a middleware that validates tokens and handles refresh

Optimized by PrePrompt

Write a FastAPI middleware that: (1) extracts the Bearer token from the Authorization header and validates it as a JWT, (2) if expired, calls POST /auth/refresh with the refresh token from the cookie and updates request.state.user with the new payload, (3) returns HTTP 401 with {"error": "unauthorized"} if validation and refresh both fail. Use python-jose for JWT parsing.

What changed: framework specified (FastAPI), token standard specified (JWT), refresh mechanism specified (POST endpoint + cookie), error response format specified, library specified. Six sources of ambiguity removed.

Example 2 — rate limiter prompt score: 70 — intercepted

Original

implement a rate limiter that tracks requests, manages quotas, handles bursts

Optimized by PrePrompt

Implement a token bucket rate limiter in Python as a class RateLimiter with: (1) per-user quota tracking using Redis INCR with TTL, (2) burst allowance up to 3x the per-minute rate for up to 10 seconds, (3) a check_rate(user_id: str) -> bool method that returns False and logs a warning when quota is exceeded. Include a reset_quota(user_id: str) method. Use redis.asyncio.

What changed: storage backend specified (Redis), algorithm specified (token bucket), class structure specified, method signatures specified, burst parameters made concrete, async specified. The original "manages quotas" and "handles bursts" are now unambiguous.

Example 3 — factual lookup (correctly passes through) score: −35 — pass

Original

what is jwt

Unchanged — passes through

what is jwt

The classifier correctly identifies this as a factual question. It starts with "what", scores −35, and passes through to Claude Code untouched. No API call, no latency. This is the correct behavior — not every prompt needs optimization.

The Prompt Quality Checklist

Before sending any complex prompt to Claude Code, run through this list:

You will not need this checklist for simple prompts — "what is jwt", "fix the syntax error on line 42", "add a docstring to this function" are already specific enough. The checklist is for the complex, multi-requirement prompts that tend to produce mediocre first attempts.

If you install PrePrompt, the classifier handles the checklist automatically for prompts you submit quickly. You end up with well-structured prompts even when you did not have time to write them carefully.

Frequently Asked Questions

Does better prompt writing replace tools like PrePrompt?

They are complementary. Manual techniques help you write better prompts consciously and build good habits over time. PrePrompt catches the prompts you write quickly or under deadline pressure — the ones where you forgot to specify the framework, or lumped three requirements into one sentence. The classifier only fires when a prompt actually needs it.

How much does a Haiku rewrite add to my workflow time?

Approximately 1–2 seconds per optimized prompt. The classifier itself adds under 1ms on every prompt. Haiku is only called when the score is 38 or above, which is roughly 40% of prompts in a typical coding session. Simple prompts — questions, short commands, already-structured tasks — are never delayed.

Can I see what PrePrompt changed in my prompt?

Yes. PrePrompt prints the original and optimized prompt in a rich annotation box in your terminal. The original is always logged alongside the optimized version in ~/.preprompt/history.db. Running preprompt-history shows recent prompts with their scores and routes.

Is PrePrompt suitable for all programming languages?

Yes. The classifier scores prompts based on linguistic signals, not code syntax. The Haiku rewrite is language-agnostic. PrePrompt works with Python, TypeScript, Go, Rust, and any other language you use in Claude Code or Cursor. The stack memory system learns your preferred language and framework from your prompts over time, injecting that context into future rewrites automatically.

Further Reading