Course · Claude Code · Models & thinking
Switch models with /model to match the task
Some tasks cannot use a capable model’s depth. In src/payments/processor.ts - part of a small TypeScript payments service this course uses as its running example - the duplicate check ends with throw new Error("duplicate payment"): a message written for whoever wrote the code, not for whoever trips over it. The task is to rewrite it so a caller who retries with the same Idempotency-Key learns what happened and what to do next.
That is one string in one file: no architecture, no design fork, nothing to reason about. Running your most capable model to rephrase a sentence is paying for a brain the task cannot use, so drop a gear before you start. The command, typed at the prompt of a running Claude Code session, is /model (if you have never started one, the install lesson covers that setup).
Two ways to switch
Section titled “Two ways to switch”/model controls which model answers you. Two forms, depending on whether you already know what you want:
/model # open the picker - arrow through the options/model <alias> # switch straight to a known alias, no pickerOne behavior surprises people, and it is worth getting right early: both forms save the choice as your default for new sessions, the same as picking a row in the picker and pressing Enter. The switch you want for a one-off is different - open the picker, move to the row, and press s to apply it to this session only, leaving the default untouched. The model-config reference documents all of this, and the aliases change as new models ship, so prefer the live picker over memorized IDs.
The picker lists rows by model name, not by capability labels. The built-in lineup is aliases - default, fable, opus, sonnet, haiku, plus variants with a larger context window (more of the conversation visible at once) and a few special-purpose aliases the picker describes when you need them - each row carrying a short description. The list below is illustrative, not captured; your roster depends on plan and provider, and which row is the capable one is a question the row descriptions answer, not the order they appear in. A row without a note in the illustration simply carries none:
> /model ◇ pick a model › Default Fable Opus complex reasoning Sonnet daily coding tasks Haiku fast, simple tasks ← this oneFor the error string, take the fast row and hand it the mechanical job. An illustration of the exchange - not a captured run; your picker and your test output are your own:
> in src/payments/processor.ts, rewrite the "duplicate payment" error message so it tells the caller what happened and what to do next. Keep the phrase "duplicate payment" at the start of the message so the existing test assertion still matches. Then run the tests.
⏵ Edit src/payments/processor.ts - throw new Error("duplicate payment"); + throw new Error( + "duplicate payment: this idempotency key was already used. The " + + "first request completed or failed after recording the key - " + + "do not retry with the same key." + );
⏵ npm test ✓ all tests passOne detail in that prompt is doing real work: a test in tests/payments.test.ts pins this exact wording with .rejects.toThrow("duplicate payment"), and a string assertion like that matches by substring. Extending the message keeps the suite green. Replace the phrase, and the duplicate-key test fails on the wording - the recovery is to update the assertion to the new message, not to revert the copy. A string edit is mechanical; the pinned assertion is the part that bites.
The hedged wording is earned, not decorative: this service records a key at first check, before the payment finishes, so a duplicate rejection cannot tell the caller the original request succeeded. An error message should not promise more than the code knows. (The planning chapter traces that defect in full, if you want the whole investigation.)
To confirm which model answered, run /status, or configure a status line that shows it - there is no always-on display of the active model by default.
The same judgment generalizes past this one tool. The widget below maps a handful of task shapes to a tier (a rough capability class: light, balanced, flagship) and a switch mechanism per tool; it covers five tools, and for readers who delegate to subagents (separate workers that can carry their own model) it includes a per-subagent pin. The full tool-by-tool comparison lives in Foundations: model selection. The task mapping is editorial, the commands come from each tool’s current docs:
Session switch, or new default
Section titled “Session switch, or new default”The s key is the one-off: this session, default unchanged. When a choice should persist, be deliberate about the layer. /model <alias> or Enter in the picker writes the model field in your user settings - your default, every new session. The same key in a project-level settings.json (the repo’s .claude/settings.json, committed with the code) pins it for the team, and the ANTHROPIC_MODEL environment variable steers a single launch, which is what scripts and CI want. The model-config reference owns the current field names and which layer wins when more than one is set.
Switching mid-conversation has a tax worth knowing before you flip back and forth. Each model keeps its own prompt cache - conversation input the provider has already processed and re-bills at a discount when it is reused - so a switch means the next request, in the docs’ words, “reads the entire conversation history with no cache hits”: the whole thread reprocessed at full input price.
Per the docs, Claude Code asks you to confirm the switch while the cache is still warm, which is your cue that this costs something; the prompt-caching reference has the details. On a short session the tax is negligible. On a long, heavy one it is a real reason to pick your gear early.
When the heavier model earns it
Section titled “When the heavier model earns it”If light is cheaper and faster, why ever run the capable one? Because mechanical edits are only part of any real day. The trade is always the same shape, and it is the course’s capability-versus-cost trade in miniature: a stronger model buys reasoning depth, and depth is only worth paying for when the task has something to reason about.
- Mechanical, scoped, one obvious answer - a rename, a copy edit, a format change. A lighter model does it just as well, and this is most of any real day.
- Ambiguous, cross-cutting, several reasonable approaches - the kind of problem where a confident wrong guess costs an hour of unpicking. Here the capable model’s deeper reasoning is not a luxury; it is what stops the agent from building the wrong design beautifully.
A useful test: would you hand this to a junior without a second thought, or would you want your sharpest engineer on it? Junior-obvious work gets the light model. The problems that need your sharpest engineer get the capable one.
One check before you switch up, though: if the agent is failing because it never had a fact you know - a private acceptance criterion, a constraint that lives in your head - a stronger model does not recover it. Supply that context first; switch up when the agent has the facts and still cannot reason through them.
Model is one dial. How hard the model thinks before it acts is the second, and on a genuinely hard problem - a concurrency bug in a rate limiter, say - switching up is the right first move but not the last one: you also want the agent reasoning harder before it edits. That second dial is next.