claudio.cool
CCDV-FLessons

CCDV-F · Model Selection and Optimisation

Haiku, Sonnet or Opus based on evidence, and migrating without breaking things

⏱ 3 min readDraft

A classifier that routes hundreds of thousands of tickets a month has run on Opus since launch and is the biggest line on the bill. Another team changed only the model ID and now every request returns 400. Choosing and changing models are engineering decisions: they’re made with data and tested before shipping.

Choose based on evidence

The quality/latency/cost tradeoff is resolved per task, not per company:

Family When it fits
Haiku High volume and narrow tasks: classifying, routing, extracting fields, simple subagents. The fastest and cheapest
Sonnet The middle ground: most applications, agents and code of normal complexity
Opus Hard reasoning, long agentic tasks, problems where mistakes are expensive

The rule: start with the smallest model that could be enough, run your eval set with labeled cases and move up a family only if there is a measured gap. If Haiku stays within the agreed margin, the savings in cost and latency are immediate. The large model “just in case” costs more and responds more slowly with no proven gain.

Before changing family, look at the other knobs: lowering the effort level on the same model, fast mode if the problem is output speed, or the Message Batches API if the work can wait.

When the new model breaks something

A breaking change between versions shows up in two ways: a 400 invalid_request_error as soon as you change the ID, or different behavior with the same prompt. The most common ones when moving to current models:

  • Assistant prefill: ending the request with an assistant message that starts the answer (for example, a { brace to force JSON). New models reject it: “the conversation must end with a user message”. For JSON, the replacement is structured outputs.
  • Sampling parameters: temperature, top_p and top_k return an error. They’re removed, and behavior is guided with the prompt.
  • Thinking with budget_tokens: replaced by adaptive thinking and an effort level.
  • Other options some models no longer accept, such as forcing a tool with tool_choice or turning thinking off with disabled.
  • Changes with no error: new stop_reason values to handle (refusal, model_context_window_exceeded), a different tokenizer that counts more tokens for the same text, and effort levels with different defaults.

Faced with a 400, read the message: it says which parameter is extra. Retrying doesn’t help, because the request is invalid. Neither does switching to another family: the new versions of every family share these changes.

Migrate with a method

  1. Read the target model’s migration guide.
  2. Adjust the code: remove what is no longer accepted and replace it with its equivalent.
  3. Run the eval set and compare quality, latency and usage against the current model.
  4. Roll out gradually, with the previous ID ready to roll back.

Model lifecycle

Models go through active, legacy, deprecated (with a retirement date and a recommended replacement) and retired. After model retirement, requests to that ID fail. Anthropic gives advance notice, so the migration is planned, not improvised on cutoff day.

The usual traps

  • Using the most expensive model for everything without having tried another.
  • Retrying with backoff a 400 that appears when changing the ID.
  • Staying on the old model forever instead of adapting the code.
  • Switching family hoping it accepts what the new version rejects.
  • Shipping the migration without running the eval set.

On the exam

If the scenario talks about cost or latency on a simple task and nobody has tried another model, the answer is to run the existing set on a smaller one. If it talks about a 400 after changing the ID, look for the option that replaces the feature that no longer exists (prefill with structured outputs, budget_tokens with effort) and reruns the evaluation.

Quick question

A practice exam question on this domain. It counts towards your progress.

A healthcare scheduling start-up extracts appointment details from patient emails as JSON. To make the model start its answer with JSON, the code has always ended each request with a short assistant message containing a single opening brace, then concatenated that brace with the returned text before parsing.

The team changed only the model ID to move from an older Sonnet model to the newest one. In staging, every extraction request now fails immediately with HTTP 400 and an invalid_request_error whose message says the conversation must end with a user message.

A developer suggests wrapping the call in retries, since the old model ID still works and the error might be a rollout issue. Another wants to move to the Opus family.

The release is planned for next week, and the team already has an eval set of 300 emails.

What is the best way to complete the migration?

Practise this domain in a practice exam →