Skip to content

Claude Code performance optimization means making each session cheaper and faster by controlling what the model re-reads: context size, prompt-cache hits, effort level, and model choice. Every request re-sends the whole conversation, so scoping file reads, keeping the cache warm, and tuning effort before switching model cut spend without lowering the bar the test suite enforces.

This page is for the developer whose sessions have become slow and expensive. One session read half the repository to fix a lint error; another has been open since morning, so a one-line question re-sends six hours of history; two model switches to “save” each made the next turn slower. The fix is to change what each request carries. If you are a tech lead who needs spend per team and hard caps, start at Claude Code cost control instead, and come back here for the per-session habits. If the slow thing is your application rather than the agent, you want application performance analysis.

What you get from tuning a Claude Code session

Section titled “What you get from tuning a Claude Code session”
  • A two-minute measurement routine that tells you which lever to pull
  • The actions that invalidate the prompt cache, and the ones that only look like they do
  • A decision rule for /clear, /compact, and /rewind, and effort and model settings in the right order
  • Four copy-paste prompts, and a way to show the cheaper session still passes the same gates

Where does a Claude Code session spend tokens and time?

Section titled “Where does a Claude Code session spend tokens and time?”

Claude Code sends your full conversation with every request: the system prompt, CLAUDE.md, tool definitions, every earlier message, and every tool result. Each tool call starts another request that carries the same history. Four things decide what that costs:

DriverWhat happensThe lever
Context sizeA long session re-bills its history on every turn, at the cached rate when the cache is warmScope reads, /clear between tasks, and delegate verbose work to subagents
Cache hitsA miss re-processes the whole context at the full input price, and the turn is slowerAvoid mid-task model switches, keep MCP and plugin sets stable, and mind the cache lifetime
Effort and modelThinking tokens bill as output tokens; higher effort buys more thinking on every requestTune effort first, switch model only at a task boundary
Agents you are not watchingSubagents, workflows, scheduled tasks, and goal check-ins each send their own requestsPin cheap models on helpers, cancel idle loops

Two facts from Anthropic’s prompt caching page for Claude Code (checked 2026-09-26) change the usual advice. First, the cache lifetime depends on how you pay. On a Claude subscription within its included usage, the main conversation gets a one-hour cache. With usage credits, an API key, or a cloud provider, it gets five minutes. Second, editing files in your repository, or editing CLAUDE.md mid-session, does not invalidate the cache. Reads append to the conversation; nothing earlier is rewritten. The advice to “avoid reordering files so the cache survives” is a myth.

For a baseline, Anthropic’s cost documentation (checked 2026-09-26) reports that enterprise deployments average “around $13 per developer per active day and $150-250 per developer per month, with costs remaining below $30 per active day for 90% of users”. Model prices live on the models hub, not on this page.

Measure one Claude Code session before you change anything

Section titled “Measure one Claude Code session before you change anything”
  1. Run /usage. The Session block shows the session’s estimated cost, a per-model token breakdown, and, from v2.1.251, a Prompt cache (main) line with the hit ratio, the number of misses, and whether the cache is warm now. From v2.1.260 the line also names the likely cause of the last miss, for example likely cause: tool definitions changed. /cost and /stats are aliases of /usage. On a subscription, the dollar figure is list price, not your bill.

  2. Run /context. It draws the window as a coloured grid: system prompt, memory files, tools, MCP servers, and conversation. If memory files or tools take a large share before you have typed anything, fix the setup (the next section). If the conversation dominates, decide between /clear, /compact, and /rewind.

  3. Put context and cache on the status line. Configure the status line to show context use and the prompt_cache fields, so you notice a growing window or a run of misses on the turn it happens, not at the end of the week.

  4. Paste both outputs into the prompt below. Only you can run these local commands; Claude reads their output. Per-server, per-skill, and per-loop attribution in /usage appears only on Pro, Max, Team, and Enterprise plans.

  5. Write down one number. Note the metric you want to move (cost per session, cache hit ratio, or seconds per turn). Without a before value, a quiet day looks like a saving.

The cheapest token is the one Claude never reads.

Name the entry point, not the whole tree. A vague prompt such as “review the app for security issues” triggers broad scanning. Name a file or directory and let Claude explore from there. --add-dir does the opposite of scoping: it adds directories Claude may access, for example a sibling package in a monorepo. Claude Code does not take directories as positional arguments; the positional argument is your prompt.

Terminal window
# Terminal: name the starting point in the prompt
claude "Trace session validation starting from src/auth/core.ts. Read only files it imports."
# Terminal: widen access on purpose when the task spans two packages
claude --add-dir ../shared-logger "Update the logger API and every call site in this app."

Listing before opening exposes the plan so you can trim it before tokens are spent.

Keep CLAUDE.md short and move workflows into skills. Your root and user CLAUDE.md files load into every session. Anthropic’s guidance is to keep CLAUDE.md under 200 lines. Put subtree rules in a CLAUDE.md inside that subtree, where it loads only when Claude reads a file there. Move step-by-step procedures (release checklist, migration runbook) into skills, which load only when invoked. The memory system guide shows how to split the files.

Delegate verbose work to subagents. Test runs, log analysis, and documentation lookups produce long output. A custom subagent keeps that output in its own context and returns a summary. Pin model: haiku in the subagent’s frontmatter for mechanical jobs, because a subagent that inherits your session model spends at that model’s rate.

Filter output before Claude sees it. A PreToolUse hook can rewrite a test command so only failures come back. This is a hardened version of the pattern in Anthropic’s cost documentation (the original’s unanchored regex would auto-approve chained commands). Save it as ~/.claude/hooks/filter-test-output.sh and made executable:

#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
safe='^(npm test|pytest|go test)([[:space:]][^;&|`$<>()]*)?$'
if [[ "$cmd" != *$'\n'* && "$cmd" =~ $safe ]]; then
filtered_cmd="( set -o pipefail; $cmd 2>&1 | { grep -A 5 -E '(FAIL|ERROR|[Ee]rror)' || true; } | sed -n '1,100p' )"
echo "$input" | jq --arg filtered "$filtered_cmd" \
'{hookSpecificOutput: {hookEventName: "PreToolUse", permissionDecision: "allow", updatedInput: (.tool_input + {command: $filtered})}}'
else
echo "{}"
fi

Register it under hooks.PreToolUse with "matcher": "Bash" in ~/.claude/settings.json (format in the hooks automation guide), then run /hooks to confirm it is listed. The pipefail subshell keeps the test runner’s exit code, so a failing run still reads as failed when no line matches. The anchored pattern rejects newlines and shell metacharacters, because permissionDecision: "allow" skips the permission prompt for whatever it matches: a looser ^(npm test|pytest) would also approve pytest && rm -rf build.

Prefer a CLI or a language server to extra MCP servers. MCP tool definitions are deferred by default (tool search), so only tool names and server instructions enter context until Claude calls a tool; gh, aws, or sentry-cli still add nothing at all. For typed languages, a code intelligence plugin replaces grep-then-read-five-files with one go-to-definition call. Install the one for your language at the start of a session, for example /plugin install typescript-lsp@claude-plugins-official (it needs typescript-language-server on your PATH). Run /mcp to disable servers you are not using this session.

Which actions invalidate the Claude Code prompt cache?

Section titled “Which actions invalidate the Claude Code prompt cache?”

A cache miss costs you once: one slower turn that re-reads the history at the full input price, after which the new prefix is cached. The expensive pattern is repeating a miss mid-task. This table summarises the Claude Code prompt caching reference as of v2.1.283:

Invalidates the cache on the next requestKeeps the cache
Switching model with /model, including each plan-mode toggle under opusplan, a skill whose frontmatter names another model, and an automatic safety fallback to another modelEditing files in the repository
Changing effort, except on Opus 5.5 and Fable 5.1 with an API key or a Claude subscriptionEditing CLAUDE.md (the edit applies after /clear, /compact, or a restart)
Turning fast mode on for the first time in a conversationChanging permission mode or output style; turning fast mode off or back on later
With tool search off: an MCP server connecting, a tool removed on purpose, a plugin that brings MCP servers, or a bare deny rule such as BashMCP servers connecting under tool search (the default); a plugin’s skills, commands, agents, and hooks
/compact (by design: the history is replaced)Invoking skills and commands, /recap, /rewind, or spawning a subagent
Upgrading Claude Code (the first session after a restart); many screenshots, when the oldest are dropped in a batchParallel sessions in the same directory, which read each other’s cache

So choose the model, effort, and fast mode at the start of a task, and install plugins and connect servers before you begin. Each worktree builds its own cache, because the working directory is part of the prefix. Subagents build their own cache too, and it lives five minutes even on a subscription unless you set subagentPromptCacheTtl.

On an API key with frequent breaks over five minutes, set "promptCacheTtl": "1h" (v2.1.242 or later). One-hour cache writes cost more than five-minute writes, so this pays only when idle gaps are common. To check which TTL a run used, run claude -p "hello" --output-format json and read usage.cache_creation.

All three shrink what the next request carries. They cost different amounts.

SituationCommandWhy
You finished a task and start an unrelated one/clear auth-refactor (the name labels the old conversation in /resume)Costs nothing; the next task starts with a clean, cacheable prefix
The task continues, but the history is full of dead ends/compact with focus instructionsKeeps continuity. While the cache is warm the summary request reads the prefix from cache; after a long break it re-processes the whole history, so compact before the break, not after
The last few turns went the wrong way/rewind (or Esc twice) to the turn before the detourTruncates to a prefix that is already cached, and can restore code checkpoints too
You only want a reminder of what happened/recapAppends a summary without touching the cached history

Auto-compaction still runs as a backstop. On models with a native 1M window on the Anthropic API, Claude Code compacts at about 967K tokens by default. Set a lower window with /autocompact 400k if long sessions degrade before then; the value is saved to your user settings. From v2.1.288 (latest channel only on 2026-10-12) it is saved for the current model, under modelSettings, instead of as one value for every model, and /autocompact auto returns that model to its tuned window. autoCompactWindow in a settings file still sets one window for every model.

Which models start with 1M depends on the provider. On the Anthropic API, Fable 5.1, Fable 5, Sonnet 5 and later, Haiku 5.5, and Opus 4.7 and later run with the 1M window and need no [1m] suffix. From v2.1.287 (both release channels on 2026-10-12), Opus 4.7 and later and the Fable models default to 1M on Amazon Bedrock, Google Cloud’s Agent Platform, Microsoft Foundry, and the Claude apps gateway too; before that, those sessions ran at 200K. Set CLAUDE_CODE_DISABLE_1M_CONTEXT=1 to keep a 200K window: models with a native 1M window then compact at the 200K boundary. Haiku 5.5 runs with 1M on every plan, but it is the one model whose price rises for prompts over 100,000 tokens, and Anthropic counts cache reads and writes toward that threshold, so a long Haiku session pays the higher tier; the models hub lists both prices (Claude Code model configuration page, checked 2026-10-12).

To make the same focus apply to auto-compaction, add a # Compact instructions section to your project CLAUDE.md with one or two lines, for example “When compacting, keep test output and code changes.”

The rule this site follows: start on the tool’s default model, tune effort before switching model, and switch model only when your own evals say so.

From v2.1.280 (both release channels on 2026-10-12), Claude Code defaults to Claude Opus 5.5 at medium effort on every paid plan and the Anthropic API. Anthropic states that “Opus 5.5 at medium matches or exceeds Opus 5 at high” on its coding evaluations. Run /model to see what you are on. Current names and prices are on the models hub.

  1. Set effort for the task, not for the day. Run /effort low for scoped, latency-sensitive edits; stay on medium for normal work; use /effort high or xhigh when a task fails at the default. On Opus 5.5 and Fable 5.1 with an API key or subscription, the change keeps the cache, so adjusting mid-task is cheap there. On Amazon Bedrock and Google Cloud’s Agent Platform, it costs a cache miss.

  2. Use ultrathink for one hard turn. The keyword adds an in-context instruction for deeper reasoning on that turn only; the effort level sent to the API does not change. “think” and “think hard” are ordinary prompt text.

  3. Switch model at a task boundary. Each model has its own cache, so /model mid-task re-reads the whole history. /model also saves the choice as your default; in the picker, press s on a row to switch for this session only. Pick Claude Sonnet 5.5 or Claude Haiku 5.5 (needs v2.1.293, latest channel only on 2026-10-12) when cost dominates, and Claude Fable 5.1 (/model fable) deliberately for the longest-horizon reasoning; it is never a default and can bill to usage credits, depending on plan and seat. For a fixed choice per terminal, launch with claude --model sonnet or set ANTHROPIC_MODEL.

  4. Pin helpers to a cheap model. Set model: haiku in subagents that run tests or summarise logs, or set CLAUDE_CODE_SUBAGENT_MODEL=haiku for subagents that have no model of their own. A definition’s own model field wins over the variable. From v2.1.293 (latest channel only on 2026-10-12) the haiku alias is Claude Haiku 5.5, which Anthropic prices higher for prompts over 100,000 tokens, so a helper that reads a long history costs more than the headline rate; the models hub has both tiers.

Thinking cannot be turned off on Opus 5.5 or the Fable models; lower the effort instead. MAX_THINKING_TOKENS applies only to models with a fixed thinking budget, for example Opus 4.6 and Sonnet 4.6 after you set CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1. Models running adaptive reasoning, including Opus 5.5, Sonnet 5, and the Fable models, ignore nonzero values.

Fast mode makes Opus “up to 2.5x faster at a higher cost per token” (Anthropic’s fast mode page, checked 2026-09-26). It runs on Opus 5.5, Opus 5, and Opus 4.8, only on the Anthropic API or a Claude subscription, where it bills to usage credits even when plan usage remains. Use it for live debugging where you wait on every reply, and turn it on with /fast at the start of a session: the first fast turn re-reads the history uncached at fast-mode rates. A ↯ icon shows while it is on, and the setting persists across sessions, so run /fast off when you finish.

Section titled “Batch related work into one Claude Code run”

Ten small prompts that each rebuild context cost more than one planned run. Enumerate first, then work in groups with a test gate, so a regression stops the batch before it spreads. Save a codebase analysis to a file such as docs/api-analysis.md once, and start later sessions from it.

For a large mechanical change across dozens of files, the bundled /batch skill splits the work into 5 to 30 units, each in its own git worktree; see batch operations.

For one-shot jobs, print mode skips the interactive session: claude -p "Convert this JSON to YAML" < config.json > config.yaml. Add --max-budget-usd 2.00 to stop at a spend cap and --output-format json to log usage and total_cost_usd; CI caps are on cost control.

How do you know the cheaper session did not cost quality?

Section titled “How do you know the cheaper session did not cost quality?”

A saving that fails your tests has only moved the cost into rework. Treat every tuning change as an experiment:

  1. Keep the gates fixed. The same test suite, type check, lint, and review agent must pass at the same rate before and after you lower effort or move a helper to Haiku. If a gate is missing, add it before you tune; tuning without one is guessing.
  2. Give Claude the verification target in the prompt. Name the test command and the acceptance condition (“all tests in src/api pass, no new lint warnings”), so Claude checks its own work and a failing gate shows up as a stopped batch, not a surprise in review.
  3. Compare on a fixed task set. Take five recent tasks with known-good outcomes. Run them at the old and new settings, and compare three numbers: gate pass rate, turns to green, and cost from /usage or total_cost_usd. Adopt the cheaper setting only if the pass rate holds.
  4. Watch cost per accepted change, not cost alone. Over two weeks, divide spend by merged, non-reverted pull requests. The method is on the economics of agent-built software; the telemetry that feeds it is on cost control.

You own your session settings. The tech lead owns the team’s model-and-effort policy and signs off on changing it, based on the comparison above.

What breaks when you optimize Claude Code sessions?

Section titled “What breaks when you optimize Claude Code sessions?”
  • Every turn after a pause is slow and expensive. The cache expired (five minutes on an API key). Set promptCacheTtl to 1h if your breaks are regular, or /clear and start fresh after long breaks instead of resuming a huge session.
  • /usage shows many cache misses mid-task. Read the likely cause on the Prompt cache (main) line. The usual culprits are a /model switch, an effort change on Amazon Bedrock or Google Cloud’s Agent Platform, opusplan plan-mode toggles, or, with tool search off, a server or MCP-bearing plugin added mid-session. Settle them before the next task.
  • A CLAUDE.md edit seems ignored. It applies after /clear, /compact, or a restart.
  • Compaction dropped something you needed. Run /rewind to a turn before the compaction if it is still in the checkpoint history, or re-state the lost facts. Next time pass explicit keep and drop lists to /compact, and add a # Compact instructions section to CLAUDE.md.
  • A cheaper model or lower effort keeps failing the tests. Move back up one step (effort first, then model) for that task type, and record the task type in your comparison set so the next tuning attempt starts from evidence.
  • Your bill includes fast mode you forgot about. Look for the ↯ icon and run /fast off. Your admin can set fastModePerSessionOptIn so fast mode resets each session.
  • Spend keeps rising while nobody types. A /loop, a scheduled task, or a goal check-in starts turns that carry the full context. Review the Loops rows in /usage and cancel the ones nobody reads.