The one-line version: a model retirement is a scheduled outage on the provider's calendar, and if a model ID is hard-coded in your app, it becomes an outage on yours. On August 5, 2026, Anthropic hard-retired Claude Opus 4.1 — requests to that model now error (release notes). DeepSeek did the same to deepseek-chat and deepseek-reasoner on July 24 (we covered the migration). The good news: surviving this is not hard. It's five moves you can finish in an afternoon, below.
The survival checklist (do these in order)#
- Inventory every model ID your code touches — app, config, notebooks, infra, prompts.
- Centralize each ID behind one config constant or environment variable.
- Pin dated snapshots you control; stop calling floating aliases in production.
- Subscribe to every provider's deprecations page so the shutdown date never surprises you.
- Qualify replacements against a golden eval set, and keep a fallback for the hard-retirement case.
That's the whole piece. The rest is how to do each one well.
First, understand the two failure modes#
Every retirement breaks your app in one of exactly two ways, and which one you get depends on how you named the model.
You pinned a dated snapshot. You called something like claude-opus-4-1-20250805 or gpt-5-6-sol-2026-07-09 — an exact, dated version. When the provider retires it, that ID stops resolving and your requests start returning an error: a 404 model-not-found or a 400 invalid-model, depending on the API. This is loud. It is also the good kind of failure, because it fails closed — you know immediately, and you knew the date in advance if you were watching the deprecations page.
You called a floating alias. You called claude-opus-latest, or a bare gemini-flash, or any name with no date. When the provider ships a successor, it silently repoints the alias to the new model. Your calls keep returning 200 OK — but the model behind them changed. Prompts tuned for the old model drift, tool-calling behavior shifts, output formats you parsed by hand stop matching. This is the dangerous kind, because nothing pages you. You find out from a slow bleed of quality complaints days later.
A dated snapshot that 400s on a known date is a problem you can schedule around. A -latest alias that changes under you is a problem you discover from your users. Pin the snapshot.
1. Inventory every model ID#
You cannot pin what you cannot find. Grep the entire repo — not just src/, but config, notebooks, IaC, and prompt templates — for every vendor prefix:
# every model reference across the repo, with file:line
grep -rniE \
'(gpt-|o1|o3|claude-|gemini-|deepseek-|qwen|grok-|mistral|-latest)' \
--include='*.py' --include='*.ts' --include='*.js' \
--include='*.yaml' --include='*.yml' --include='*.json' \
--include='*.toml' --include='*.env*' --include='*.ipynb' \
--include='*.md' .
Read the output with one question in mind: how many distinct files name a model? The answer should become 1. If it's twelve, that's your real bug — twelve chances to miss one on migration day and leave a corner of your app returning errors.
2. Centralize the ID behind one constant#
Move every model reference to a single source of truth. A config module or environment variable means a retirement is a one-line change, not a code-wide search-and-replace under time pressure.
# models.py — the ONE place a model ID is allowed to live
import os
# Pin a dated snapshot. Bump it deliberately, after it passes the eval set.
PRIMARY_MODEL = os.getenv("PRIMARY_MODEL", "claude-opus-4-8-20260315")
FALLBACK_MODEL = os.getenv("FALLBACK_MODEL", "gpt-5-6-terra-2026-07-09")
CHEAP_MODEL = os.getenv("CHEAP_MODEL", "gemini-3-6-flash-2026-05-20")
# everywhere else in the codebase:
from models import PRIMARY_MODEL
resp = client.messages.create(model=PRIMARY_MODEL, ...)
Now the answer to "what models are we on?" is one file, and a migration is a diff you can review, not an archaeology project. (Model IDs in this snippet are illustrative — use whatever snapshot your provider currently publishes.)
3. Pin snapshots, not aliases#
With the ID centralized, make sure it's a pinned one. In production, prefer the exact dated snapshot over the convenience alias:
- OpenAI: use
gpt-5-6-sol-2026-07-09, notgpt-5-6-sol. The dated snapshot won't change under you; the alias can be repointed. - Anthropic: use
claude-opus-4-8-20260315, notclaude-opus-latest. Anthropic publishes both an alias and a dated version — pin the date. - Google: Gemini exposes
-latest, stable, and preview flavors. Pin the stable dated version and treat anything-latestor-previewas retirable on short notice.
The rule: an alias hands the provider permission to change your model with no code change on your side. That's a feature in a prototype and a liability in production. Opt into "always newest" only where you've decided, explicitly, that fresh beats stable.
4. Subscribe to the deprecations page#
The shutdown date is public before it happens. Missing it is an operational choice, not bad luck. Bookmark and monitor each provider's canonical list:
- Anthropic — the release notes and the model deprecations policy.
- OpenAI — the deprecations page, which lists shutdown dates and the recommended replacement for each model.
- Google — the Gemini models page and Vertex AI's version notes.
- DeepSeek, Qwen, and other open-weight vendors — their API changelogs; these move fastest and give the least notice.
If you want it to be impossible to miss, poll the page. A tiny weekly job that diffs the deprecations list and posts to Slack turns "we forgot" into "we got a message three weeks out":
# crude but effective: diff the deprecations page weekly, alert on change
curl -s https://platform.openai.com/docs/deprecations \
| sha256sum | cut -d' ' -f1 > /tmp/dep.new
diff /tmp/dep.old /tmp/dep.new >/dev/null 2>&1 \
|| echo "OpenAI deprecations page changed — review it" # → pipe to Slack
mv /tmp/dep.new /tmp/dep.old
5. Qualify the replacement with a golden eval set#
When the notice lands — or when a hard retirement forces your hand — the question is "does the replacement still work for us?" You want to answer that in an hour, with data, not in production, with users.
Keep a golden eval set: 30–100 representative inputs with known-good expected outputs, covering your real prompts, tool calls, and the edge cases that bit you before. On migration day, run the candidate model against it and compare pass rate, latency, and cost:
from models import PRIMARY_MODEL # current
CANDIDATE = "claude-opus-5-20260724" # the proposed replacement
passed = 0
for case in golden_set: # your fixed eval cases
out = run(model=CANDIDATE, prompt=case.input)
if case.check(out): # your grader: exact, regex, or judge
passed += 1
print(f"{CANDIDATE}: {passed}/{len(golden_set)} passed")
# swap only if it clears your bar — otherwise try the next candidate
If you don't have a golden set yet, build one before you need it — it's the single highest-leverage artifact for surviving churn, and it pays off on every model swap, not just forced ones. We walk through building one in How to Build an LLM Eval Dataset, and the pattern for testing a candidate against live traffic before you commit in How to Shadow-Test a Cheaper LLM Before You Switch.
The hard case: a retirement with no successor to migrate to#
Migration assumes there's somewhere to go. Sometimes there isn't — the model is simply gone, and the in-provider "replacement" is a different enough model that it fails your evals. That's what a fallback chain is for: when the primary errors, fail over to a qualified model on a second provider rather than returning an error to the user.
from models import PRIMARY_MODEL, FALLBACK_MODEL
def complete(prompt):
for model in (PRIMARY_MODEL, FALLBACK_MODEL):
try:
return call(model=model, prompt=prompt)
except (ModelNotFound, InvalidModel, ProviderError):
continue # retired or down — try the next one
raise RuntimeError("all models exhausted")
The subtlety is doing this without a silent quality drop — a fallback that quietly serves a worse model is its own incident. The design pattern for that (qualify each tier on the same eval set, log which tier served each request, alert when you're running on fallback) is in Build a Fallback Model Chain Without Silent Quality Loss.
The migration runbook, in five lines#
When a deprecation notice lands for a model you use:
- Confirm the date and the recommended replacement from the provider's deprecations page.
- Run the replacement against your golden eval set. Compare pass rate, latency, cost.
- If it clears your bar, bump the one constant in
models.pyand ship behind a flag. - If it doesn't, qualify the next candidate — a different tier, or a second provider — the same way.
- Do it before the shutdown date, so the calendar never becomes an incident.
What it means for you#
The pace of model releases in 2026 is also the pace of model retirements — Anthropic shipped Opus 5 in late July and hard-retired Opus 4.1 two weeks later; DeepSeek and the open-weight labs turn over even faster (see the two August deadlines that quietly raised agent bills). You cannot slow that down. What you can do is make it boring: one config constant, pinned snapshots, a monitored deprecations page, and a golden eval set that turns every forced swap into an hour of measured work. Do that once, and the next retirement notice is a Tuesday Slack message — not a Saturday page.



