Skip to content

Prompt Engineering for Claude Code

“Fix the bug” produces wildly different results than “The WebSocket reconnection in src/realtime/socket.ts fails after the third retry because the backoff timer resets on partial connections. Fix the retry logic to use exponential backoff with jitter, and add a test that simulates three failed reconnection attempts.”

The difference is not about being verbose. It is about giving Claude the right constraints, context, and verification criteria to produce exactly what you need on the first try.

  • A structured prompt framework that works for any task
  • Plan mode and thinking keywords for complex multi-file changes
  • Context priming techniques that reduce hallucination
  • Prompts for the ten most common development tasks

Every effective Claude Code prompt answers four questions:

  1. What specifically needs to change?
  2. Where in the codebase?
  3. Why (the constraint or requirement driving the change)?
  4. How should Claude verify the result?
What: Add rate limiting to the /api/users endpoint
Where: src/api/routes/users.ts and src/middleware/rate-limit.ts
Why: We are getting 10k requests/minute from a single IP and the DB is overloaded
How: The existing test suite should pass, and add a new test that verifies 429 responses after 100 requests/minute

Press Shift+Tab to cycle permission modes until you reach Plan Mode (the cycle is normal then Auto-Accept then Plan; a Delegate mode is added when an agent team is active). In Plan Mode, Claude analyzes the codebase and creates a plan before making any changes. This is essential for:

  • Changes spanning more than three files
  • Refactoring where the order of changes matters
  • Tasks where you are unsure of the approach
[Shift+Tab to enable plan mode]
Refactor the authentication system from session-based to JWT. The current implementation
uses express-session with Redis storage across 12 route files. I need:
1. A migration plan that does not break existing sessions during deployment
2. Backward compatibility for the mobile app (version 2.3 and below uses session cookies)
3. Token refresh logic that handles concurrent requests
Read the current auth implementation in src/auth/ first, then create the plan.
Do not start implementing until I approve the plan.

Extended thinking is now on by default. With current models (Fable 5, Opus 4.8, Sonnet 4.6), Claude uses adaptive reasoning: it dynamically allocates how much it thinks before responding, scaled by the effort level you set. The old keyword ladder (“think” then “think hard” then “think harder” then “ultrathink”) no longer buys progressively larger thinking budgets — those phrases are now read as ordinary prompt instructions and do not allocate thinking tokens.

The durable lever is the effort level, not a magic word:

  • Set effort in /model — choose low, medium, high, xhigh, or max (high is the default). This is the recommended way to tune the speed-versus-depth tradeoff for the whole session.
  • CLAUDE_CODE_EFFORT_LEVEL — set the same control via environment variable for scripts and headless runs.
  • Option+T (macOS) / Alt+T — toggle thinking on or off for the current session.
  • MAX_THINKING_TOKENS — cap the thinking budget for non-adaptive models (it is ignored on Opus 4.8 unless you set it to 0, which still disables thinking on any model).
  • ultrathink is no longer a lever. Phrases like “think”, “think hard”, “ultrathink”, and “think more” are now read as ordinary prompt instructions and allocate no thinking tokens — the old four-tier budget ladder is gone. Use the effort level or the thinking toggle instead.

When you want maximum reasoning on one hard problem, raise the effort level rather than reaching for a keyword:

Set effort to high in /model first, then:
Analyze the race condition in our payment processing pipeline. Three services
(OrderService, PaymentService, InventoryService) communicate via Redis pub/sub, and we are
seeing duplicate charges when two payment confirmations arrive within 50ms of each other.
Walk through the timing sequence and propose a solution using distributed locks.

Raise effort when the problem requires multi-step reasoning, when you are seeing shallow analysis, or when the task involves concurrent systems, security analysis, or architectural decisions. Drop it back to low for routine edits so you are not paying for reasoning you do not need.

Before asking Claude to make changes, prime its context with the right information:

Read src/auth/middleware.ts, src/auth/jwt.ts, and src/auth/session.ts.
Then read the test files for each.
Now tell me: what would break if I changed the token expiry from 1 hour to 15 minutes?

This is more effective than asking the question directly because Claude has the actual code in context, not its assumptions about what the code might look like.

Look at how error handling works in src/api/routes/orders.ts.
Now apply the same error handling pattern to src/api/routes/products.ts.
Every endpoint should have the same try/catch structure, the same error response
format, and the same logging calls.
CONSTRAINTS:
- Do not modify any file in src/core/ (these are generated)
- Keep backward compatibility with the v1 API
- All new code must have 80%+ test coverage
- Use the existing Logger, not console.log
TASK: Add a new /api/v2/analytics endpoint that aggregates user activity data
from the events table.
The /api/users/:id endpoint returns 500 when the user ID contains special characters.
Steps to reproduce: GET /api/users/abc%20def
Expected: 400 with validation error
Actual: 500 with unhandled Prisma error
Fix the input validation in src/api/routes/users.ts and add a test case for special characters in IDs.
Review the changes in the current git diff (git diff HEAD). For each finding:
1. Explain the issue
2. Rate severity: CRITICAL / HIGH / MEDIUM / LOW
3. Suggest a specific fix with code
Focus on: error handling, type safety, and performance.
Skip: style issues (our formatter handles those).
Read src/services/payment.service.ts and generate a comprehensive test file.
Follow the patterns in src/services/__tests__/order.service.test.ts for:
- Test structure (describe/it blocks)
- Mocking approach (jest.mock for external services)
- Assertion style (expect().toEqual for objects, toBe for primitives)
Cover: happy path, validation errors, external service failures, and edge cases
(empty arrays, null values, maximum limits).

Claude ignores constraints: Put constraints at the beginning of your prompt, not the end. When context gets long, the end of the prompt gets less attention. Also consider adding critical constraints to your CLAUDE.md file.

Plan mode still makes changes: Make sure you toggled plan mode with Shift+Tab (not just asked Claude to plan). Check the status indicator in the REPL to confirm plan mode is active.

Extended thinking does not improve results: Not every problem benefits from deep reasoning. Simple file edits, formatting changes, and straightforward bug fixes run fine at low effort. Raise the effort level (in /model) only for problems with multiple interacting components or subtle correctness requirements — and remember that typing “ultrathink” no longer increases the budget on its own.

Claude misunderstands the codebase: Prime context by having Claude read the relevant files first. If it misunderstands, correct it with “No, look at line 45 of src/auth.ts — the token is stored in Redis, not in the session.”