The first month I used Claude Code seriously, I left Opus as the default and never
thought about it again. It worked. It also cost about three times what it needed to,
and the reason wasn’t that Opus is expensive — it’s that I was paying Opus rates to
rename variables.
Model choice is the dial everyone reaches for first, and it is genuinely the biggest
one. But it’s one of four, and the other three are where most of the waste actually
lives. Here’s the whole set.
The tiers, and what each one is for
List prices per million tokens, input / output:
| Model | Input | Output | Context |
|---|
| Fable 5 | $10 | $50 | 1M |
| Opus 5 | $5 | $25 | 1M |
| Sonnet 5 | $3 | $15 | 1M |
| Haiku 4.5 | $1 | $5 | 200K |
Fable 5 sits above Opus and is worth knowing about — /model fable, or the best
alias — but it isn’t the default on any account type and it isn’t what this post is
about. The working set is the bottom three.
Opus is five times Haiku and roughly 1.7× Sonnet at list price. Sonnet 5 is on
introductory pricing of $2 / $10 until the end of August 2026, though, which makes the
gap 2.5× right now — worth knowing before you build a habit around a ratio that’s
about to move.
Those ratios are worth internalising either way, because they’re smaller than the
intuition most people carry around. Opus is not ten times the price of Sonnet, and
treating it as a luxury you ration by the sip gets you the worst of both: you use it
too little for the thinking, and the cheaper model then takes twelve turns to converge
on something the expensive one would have got in two.
The split that has worked for me:
Opus plans and diagnoses. Anything where being wrong is expensive downstream:
scoping a feature, working out why an intermittent test fails, deciding how a migration
should be sequenced, reading unfamiliar code and telling you what it actually does. The
output here is small — a plan, an explanation — but its quality determines how much
work happens next. This is the one place where paying more is unambiguously cheaper.
Sonnet executes the plan. Once the approach is settled and the files are named,
the work is mostly mechanical judgment: write the code, follow the existing patterns,
run the tests, fix what broke. Sonnet 5 is very good at this and is what I leave as
the session default.
Haiku does the errands. Reformatting, renaming across a directory, extracting a
list from a log, generating fixtures, first-pass triage of a big file. Anything where
you’d have written a script if you’d felt like it. Haiku’s 200K context is a real
constraint, so this is per-task work, not “hold the whole codebase in your head” work.
/model switches the current session and saves the choice as your default for new
ones — it stopped being a session-only toggle a few releases back, which surprises
people who reach for it to try something once. /config and the model setting cover
the same ground. For subagents, set model: haiku in the subagent’s own configuration
and it stays cheap regardless of what the main session is running.
There’s also opusplan, which is this whole section as a built-in: Opus while you’re
in plan mode, Sonnet the moment you leave it. It’s the right default for a lot of
people. Read the next section before you turn it on, though, because it has a cost that
isn’t in the name.
The handoff has a cost most people miss
Here’s the part that isn’t obvious. The model is part of the cache key. Each model
has its own cache, so the moment you /model from Opus to Sonnet halfway through a long
session, the next request reads the entire history with no cache hits and writes it all
again under the new model. The reads you’d been getting at roughly a tenth of the input
rate are gone for that turn.
Worth being precise about what that costs, because it’s easy to overstate. The re-read
isn’t billed at full input price — it’s a cache write, which is 1.25× the base input
rate on the five-minute cache and 2× on the one-hour one. But it’s charged at the rate
of the model you switched to. Opus → Sonnet is a downgrade, so you’re re-processing
at Sonnet’s cheaper rates:
| 200K-token session |
|---|
| One more Opus turn, cache warm | 200K × 0.1 × $5 = $0.10 |
| Switching to Sonnet, one-time | 200K × 1.25 × $3 = $0.75 |
| Each Sonnet turn afterwards | 200K × 0.1 × $3 = $0.06 |
So the switch costs about seven or eight turns’ worth of what you were already paying,
then starts paying you back. Annoying, not ruinous — and if you’re going to keep working
for a while, it clears.
The direction that actually hurts is the other one. Sonnet → Opus on the same context is
200K × 1.25 × $5 = $1.25, against the $0.06 a turn you were paying on Sonnet. That’s
twenty turns of savings gone on one keystroke, and it’s the switch people make casually,
because it feels like reaching for the better tool rather than restarting a meter.
Two things that soften it. The old cache isn’t destroyed, only abandoned — it lives out
its TTL, so bouncing Opus → Sonnet → Opus inside the window can still hit the original
prefix. And the cost is once per switch, not once per turn.
So make the handoff a boundary, not a swap:
- Plan with Opus, then
/clear and start the implementation session on Sonnet with
the plan pasted in. New session, new cache, and the fresh context is smaller anyway.
- Or keep the main loop on one model and push the cheap work into subagents. A
subagent starts its own conversation with its own system prompt and builds its own
cache, so delegating to Haiku leaves the parent’s prefix untouched — the call and its
result just append to your conversation. This is the better pattern for anything you
do repeatedly. (Subagents run on the five-minute TTL even on a subscription, so a
long-lived one has its own cold starts. It just isn’t your problem.)
Which brings back opusplan. Every toggle in and out of plan mode is a model switch,
with exactly the cost above, and plan mode is something you flip more than once in a
session. It’s still a good setting — it just isn’t free, and it’s the one case where the
tooling makes the expensive move so convenient you stop counting it.
The general rule: switching models is cheap between conversations and expensive inside
one.
Effort is the dial nobody touches
Most current models take an effort level — low, medium, high, xhigh, max —
which controls how much they think before acting. Thinking tokens bill as output
tokens, at output rates, and the default budget can run to tens of thousands of tokens
per request. On a reasoning-heavy task that’s money well spent. On “rename this
function everywhere” it is pure burn.
The exception matters here, though: Haiku 4.5 has no effort level at all.
Opus 5, Sonnet 5, Fable 5 and the 4.7/4.8 Opuses take the full
low–max range; Opus 4.6 and Sonnet 4.6 take everything but xhigh. Haiku isn’t on
the list, so “run the errands on Haiku at low effort” is one dial, not two. Set a level
the active model doesn’t support and Claude Code quietly clamps to the highest one it
does.
high isn’t just a sensible default — it is the default on every model that supports
effort, Opus 4.7 aside, which defaults to xhigh. So the useful moves are downward.
xhigh earns its keep on genuinely hard agentic coding; low is right for anything
mechanical; max is session-only and won’t persist.
The catch, which is the same catch
Here is the thing I had wrong when I first wrote this section, and it undercuts the
obvious advice: effort is part of the cache key too. Same model, different effort
level, different cache. Running /effort mid-conversation invalidates the prefix
exactly the way /model does — Claude Code will now show you a confirmation dialog
before it applies one, which is a decent tell that it isn’t free.
So “drop to low for this batch of routine edits, then put it back” is, on a large
context, two full re-reads to save output tokens on a handful of cheap turns. That can
easily lose. The saving is real, but it belongs at the top of a session, not in the
middle of one — pick the level when you pick the model, and let it ride.
When you want one deep think without paying to move the dial, put ultrathink anywhere
in the prompt. Claude Code recognises the keyword and adds an in-context instruction; the
effort level sent to the API doesn’t change, so the cache survives. It’s the cache-safe
version of the thing everyone actually wants. (“think hard”, “think more” and friends do
nothing — they’re passed through as ordinary text.)
With that caveat attached, this is still the dial with the best ratio of impact to
effort, and almost nobody moves it. Starting a session of routine edits at low rather
than high is often a larger saving than dropping from Opus to Sonnet, because you’re
cutting the expensive half of the bill — output — rather than the cheap half.
Which is really the whole argument in one line: model, effort and context, chosen
together and chosen early. Not one of the three, picked once, and left alone — and
not all three, fiddled with continuously, which is its own way of paying twice.
Your context window is the actual bill
Claude Code sends the full conversation with every request. Every tool call sends it
again, with the tool results attached. Prompt caching means you re-read that history
at a discount rather than full price, but a one-line question in a session that’s been
open all day still draws against the entire conversation.
Three habits, in descending order of impact:
/clear between unrelated tasks. Stale context is a tax on every subsequent
message, and it never gets cheaper. /clear itself costs nothing. /compact does cost
something, but I had the reason wrong: yes, it sends a separate request that reads the
whole conversation in order to summarise it — but while the cache is warm, that request
reads your prefix from cache like any other, so a mid-session compact costs a fraction
of what the context size suggests. Most of it is generating the summary.
The expensive compact is the cold one. Resume a session the next morning, run
/compact, and there’s no cache left to read, so the summarisation request reprocesses
the entire history as uncached input. Same command, wildly different bill. Compact at a
natural break while you’re still working; don’t compact as the first thing you do after
a gap.
And if you’re compacting because you went down a path you want to abandon, use
/rewind instead. It truncates back to a prefix the cache was already built from, so
the next request hits the earlier entry rather than building a new one. Compaction
constructs a history that has never been cached; rewinding returns to one that has.
Watch the cache lifetime. Come back from lunch and your first message misses the
cache entirely and reprocesses everything. An hour on a subscription, five minutes on
an API key or a cloud provider — and, the part that catches people, it silently drops
to five minutes on a subscription too once you’re over your plan limit and drawing on
usage credits, because that’s the point where cache writes start costing you money. On
Pro and Max, resuming a large session after a long break will offer to resume from a
summary instead, which is usually the right answer.
Restarting is a cache miss too. Upgrading Claude Code rewrites the system prompt,
so the first turn afterwards rebuilds from the top — and resuming a long conversation
after an upgrade re-reads the entire history behind a prefix that no longer matches.
That first turn back into a big session can genuinely be the most expensive request you
send all week. Same for connecting or disconnecting an MCP server whose tools aren’t
deferred, toggling a plugin that ships one, or adding a bare tool name as a deny rule.
The cache is also scoped per machine and per directory, so two worktrees of the same
repo never share one.
Move instructions out of CLAUDE.md. CLAUDE.md loads into context at session start,
every session, whether or not it’s relevant. If it has grown a detailed PR-review
procedure and a database-migration runbook, you’re paying for both while writing CSS.
Keep it under a couple of hundred lines of genuine essentials and move the procedures
into skills, which load only when used.
/context shows what’s currently taking up space. /usage shows what it cost, broken
down by model — and on a Pro, Max, Team or Enterprise plan it also attributes recent
usage to specific skills, subagents, plugins and individual MCP servers, so you can see
which of your own tools is expensive. It flags behaviours responsible for 10% or more
of recent usage, which is how you find out that half your spend is cache misses. Two
caveats: that breakdown is the subscription view, not the API one, and the dollar figure
is computed locally at list rates, so it ignores promotional pricing and contracted
discounts. Treat it as a distribution, not an invoice.
Skills: the on-demand half of your context
A skill is a folder with a SKILL.md in it. The frontmatter description sits in
context permanently — it’s how Claude decides whether the skill is relevant — and the
body loads only when the skill actually runs. That asymmetry is the whole point: a
2,000-word procedure costs you one sentence of context until the moment you need it.
Writing one
Personal skills live in ~/.claude/skills/<name>/SKILL.md and apply everywhere;
project skills live in .claude/skills/<name>/SKILL.md and are committed with the
repo, so everyone on the team gets them.
mkdir -p .claude/skills/review-migration
Then SKILL.md:
---
name: review-migration
description: Reviews a database migration for lock risk,
reversibility and data loss before it ships. Use when the user
adds or edits a file in db/migrations.
paths: db/migrations/**
model: opus
effort: high
---
Review the migration against these, in order:
1. **Locking** — does any statement take an ACCESS EXCLUSIVE
lock on a table with significant rows? Adding a column with
a default, changing a type, and adding a non-concurrent
index all do.
2. **Reversibility** — is there a `down`, and does it actually
restore the prior state rather than just dropping what `up`
created?
3. **Data loss** — any `DROP`, any narrowing type change, any
`NOT NULL` added to a column with existing rows.
4. **Ordering** — does this migration assume application code
that hasn't deployed yet? Schema and code deploy separately.
Report each finding with the statement, the risk, and the safe
rewrite. If all four pass, say so in one line — don't pad it.
Two things there are worth pulling out.
The description is doing real work. It’s the only part Claude sees before deciding
to load the skill, so it needs to say when to use this, not just what it is. “Reviews
migrations” is a poor description; the version above names the trigger. It gets
truncated at 1,536 characters in the listing, so front-load the use case.
And model: opus / effort: high are per-skill overrides. That’s the cost story
coming full circle: this particular review is worth Opus at high effort, and it says so
in its own frontmatter, so the session can sit on Sonnet at medium and only escalate
for the ten seconds it matters. The override lasts for the rest of the current turn and
isn’t written to settings — your next prompt is back on the session model. You can go
the other way too: model: haiku, effort: low on a formatting skill.
Given everything above, be honest about what that escalation is: a model and effort
change, which is a cache-key change, there and back. For a skill you fire two or three
times a session on work that genuinely needs the better model, that’s a fine trade. For
one that runs on every edit, you’ve automated the expensive keystroke — which is worse
than doing it by hand, because now nothing prompts you to notice.
context: fork sidesteps that: the skill runs as a subagent with its own context, so
the model it uses is its own business and the intermediate work never lands in your
conversation. The result does come back — that’s the point — but the twenty tool calls
that produced it don’t. It runs in the background by default and won’t see your
conversation history, so it only makes sense for skills that contain an actual task
rather than a set of guidelines. Set background: false if you’d rather wait for the
result in the turn that invoked it.
Invoke it with /review-migration, or let Claude pick it up on its own when you touch
a matching file. paths scopes automatic loading so it doesn’t fire while you’re
working on something unrelated.
The part people skip: skills expire
Here’s a point Boris Cherny, who created Claude Code, has
made — and it’s the one I’d most want people to take away.
Revisit your skills with every model release. A skill is, in large part, a
correction: you wrote it because the model kept doing something you didn’t want, or
kept missing a step. Newer models often don’t need the correction. Worse, they can
perform better without it, because your carefully enumerated procedure overrides
judgment that has since become better than the procedure.
I’ve seen this concretely. A “check the tests actually ran, don’t just report success”
instruction that was load-bearing on one model became redundant on the next and then
became actively counterproductive on the one after, where it produced a verification
step on every trivial edit. The skill was right when I wrote it. It just stopped being
right, silently, and nothing told me.
So build it into your release routine. When a new model lands:
- Take one skill and delete it — or comment out the half you suspect is obsolete.
- Run a real task that would have used it.
- Compare. If the output is as good or better, the skill was overhead. If it’s worse,
you’ve re-earned it.
This costs half an hour per release and it is the highest-leverage half hour in the
whole workflow, because skill bloat compounds in exactly the way context bloat does:
invisibly, on every single request.
Where to learn the craft of it
If you want to go deeper on skills as a discipline rather than a config format,
Matt Pocock’s skills repository is the best
resource I’ve found. It’s a curated set — “Skills for Real Engineers” — aimed squarely
at the failure modes people actually hit: agents that drift from intent, output that
sprawls, architecture that degrades a little with every session.
Some of what’s in there:
/grill-with-docs — interview-style planning that pins down the domain model before
any code gets written
/tdd — a genuine test-first workflow rather than tests bolted on afterwards
/diagnosing-bugs — structured debugging instead of guess-and-check
/improve-codebase-architecture — scans for structural problems and proposes fixes
/handoff — carrying knowledge between sessions, which is directly a cost technique:
a good handoff is what lets you /clear without losing your place
His YouTube channel covers the same material with the reasoning shown, which is more
useful than the finished artefacts — you can copy someone’s skill, but you can’t copy
their judgment about when to write one.
The short version
- Opus to plan and diagnose, Sonnet to build, Haiku for errands.
- Switch models between sessions, not inside one — the model is part of the cache key.
Upward switches cost the most;
opusplan makes them frequent.
- So is the effort level. Choose both at the top of a session and leave them; use
ultrathink when you want one deep turn without moving the dial.
- Haiku has no effort level, so there’s only one lever there.
- Push cheap work into subagents; they build their own cache and leave yours intact.
/clear between tasks — it’s free. /compact is cheap while the cache is warm and
expensive once it’s cold, so compact at a break, not after one. /rewind beats
/compact for abandoning a path.
- Keep CLAUDE.md thin, put procedures in skills.
- Delete a skill every release and see if you miss it.
None of this is about using Claude Code less. It’s about not paying Opus to do
Haiku’s job — and, just as often, not paying Sonnet to redo work Opus would have
got right the first time. Run /usage at the end of a working day for a week. The
distribution will tell you which of those two you’re actually doing.