Sam Austinon October 15, 202613 min read

LLM Model Migration: Handle Provider Deprecations Without Breaking Your App

Contents

Prepare before the email, then baseline, tune, shadow, canary, retire — in that order, every time

The email arrives on a quiet Tuesday: "We're retiring the model you depend on." You read it twice, check the date, and realize your whole product runs on a model with a countdown clock.

I got that email last year. We had hardcoded the model name in 14 places, our prompts had grown around the old model's quirks, and nobody remembered which features used what. We made the deadline, but my sleep schedule didn't recover for a week.

This guide shows you how to handle LLM model migration and provider deprecations calmly. You'll learn how to prepare before the email lands and how to swap models without breaking your app — the versioning discipline the prompt versioning guide applies to prompts, applied to models.

Why Model Migrations Hurt

Swapping a model sounds like changing one string. It never works that way. A new model behaves differently, even when the provider calls it an upgrade.

Here's what typically shifts:

  • Output style: tone, verbosity, and formatting change.
  • Instruction following: the new model reads your prompt differently, so old tricks stop working.
  • Structured output: JSON habits, schema adherence, and tool-calling behavior vary.
  • Refusals and safety: borderline requests may flip in either direction.
  • Latency and cost: speed and pricing rarely match the old model.
  • Context and limits: window sizes, token counts, and rate limits differ.

Your app depends on all of these without you realizing it. Ever wonder why a "better" model can make your product worse? This is why.

Prepare Before the Deprecation Email

The best migration starts months before anyone announces a shutdown. Build these habits now, and the email becomes a mild inconvenience.

Keep a Model Inventory

Write down every place your app calls a model. For each one, record the feature, the model name, the prompt version, and the owner. If you can't answer "what uses model X?" in one minute, you have homework.

Put Model Names in Config

Hardcoded model names scatter across your codebase like glitter. Move them into one config file keyed by feature. Then a migration becomes a config change plus testing, not a treasure hunt:

# models.yaml
features:
  support_chat:
    primary: model-large-v2
    fallback: model-medium-v2
    params: {temperature: 0.2, max_tokens: 800}
  summarize:
    primary: model-small-v3
    params: {temperature: 0.0, max_tokens: 400}

Pin Exact Versions

Avoid floating aliases that silently point to new models. Pin a specific version in production so behavior changes only when you decide. Aliases save effort until the day they cost you a weekend.

Build an Abstraction Layer

Wrap provider calls behind a thin interface in your own code. Your features call generate(feature, input), and the layer handles provider-specific details. IMO, this single decision saves more migration pain than any other — and it's exactly what LiteLLM's unified API gives you out of the box.

Track Deprecation Notices

Subscribe to your provider's changelog and deprecation announcements. Send them to a shared channel, not one person's inbox. Add retirement dates to your team calendar, with reminders 90, 60, and 30 days out.

Own a Real Eval Suite

You can't judge a new model by vibes. You need a regression suite that says whether quality held up. Build it from these pieces:

  • Golden dataset: 50 to 200 real prompts with defined success criteria.
  • Deterministic checks: schema validity, required fields, length limits, and banned phrases.
  • Pairwise judge: a model that compares old and new answers and picks the better one.
  • Cost and latency tracking: numbers you compare side by side.

FYI: teams that own this suite finish migrations in days. Teams without it argue about screenshots for weeks — the same four-layer stack the CI/CD guide wires into every deploy.

The Migration Playbook

When a deprecation lands, follow the same sequence every time. Repetition makes it boring, and boring is perfect.

Step 1: Read the Fine Print

Check the retirement date, the suggested replacement, and any changes to the API. Some replacements change request formats, parameters, or token limits. Know what you face before you start.

Step 2: Choose Candidates

Don't accept the suggested replacement automatically. Shortlist two or three candidates, including the recommended one and maybe a different size or vendor. Compare them on quality, speed, and price.

Step 3: Run Baseline Evals

Run your suite against the old model first, then each candidate with your existing prompts unchanged. This shows the raw gap before you tune anything. Review the worst failures by hand, since patterns jump out quickly:

old = run_suite(config["support_chat"]["primary"], golden)   # today's model first
for name in ("model-large-v3", "model-medium-v3", "other-vendor-v2"):
    new = run_suite(name, golden)                            # prompts unchanged
    print(name, {
        "pass_delta":  new.pass_rate - old.pass_rate,        # quality held?
        "cost_delta":  new.cost_per_case - old.cost_per_case,
        "regressions": worst_regressions(old, new)[:5],       # read these by hand
    })

Step 4: Adapt Your Prompts

Now tune. Common fixes include:

  • Loosen overly rigid instructions that the new model follows too literally.
  • Tighten vague instructions that the old model guessed correctly.
  • Rework few-shot examples to match the new model's style.
  • Adjust parameters like temperature and max tokens, because defaults differ.
  • Update tool and schema definitions if the new model handles them differently.

Change one thing at a time and rerun your evals. Prompt tuning without measurement is just superstition.

Step 5: Run Shadow Mode

Send real traffic to the new model quietly, and keep serving the old model's answers. Compare outputs offline. Shadow mode shows how the new model handles the messy inputs your test set missed.

Two laptops side by side on a desk being compared

Figure 1: Old answers beside new ones — shadow mode finds the regressions your golden set never imagined

Step 6: Release a Canary

Route a small share of live traffic, say 5%, to the new model. Watch your primary metric and your guardrails: errors, latency, cost, refusals, and user feedback. Widen the rollout in stages if everything looks healthy.

Step 7: Roll Out and Retire

Shift remaining traffic once the canary proves itself. Keep a fast rollback path for a while, then delete the old model from your config and code. Don't leave dead references behind, because they confuse the next migration.

Handle the Tricky Details

Some migration problems hide until production. Check these on purpose:

  • Tokenization differences: the same text can use more or fewer tokens, which changes costs and context limits.
  • Rate limits: a new model often carries different limits. Check capacity before you shift full traffic.
  • Streaming behavior: chunk sizes and timing can change your UI's feel.
  • Structured output reliability: test malformed JSON rates, not only happy-path samples.
  • Long-context behavior: models vary in how well they use long inputs.
  • Downstream parsers: anything that parses model output may break on small formatting shifts.

Build Safety Nets

Even a careful migration surprises you sometimes. Prepare for that:

  • Fallback model: route to an alternative if the primary fails repeatedly.
  • Feature flags: flip a switch to revert without a deploy.
  • Alerts: watch error rate, latency, cost per request, and feedback scores after the switch.
  • Output validation: reject and retry malformed responses before users see them.

A rollback you tested beats a rollback you hope works.

Plan the Timeline Realistically

Deadlines look generous until you start. Work backward from the retirement date and pad generously:

  • Weeks 1 to 2: inventory, read notices, pick candidates, run baseline evals.
  • Weeks 3 to 4: tune prompts, fix regressions, validate costs and limits.
  • Weeks 5 to 6: shadow mode, then canary.
  • Final weeks: full rollout, cleanup, and buffer for surprises.

Start the moment you hear about a deprecation. Waiting "until things calm down" never works, because things never calm down.

Communicate Early and Often

Migrations touch more than engineering. Tell product, support, and leadership what's changing and when. Warn them that tone and phrasing may shift slightly, so nobody panics when the bot sounds a little different.

Write a short migration note for each feature: old model, new model, test results, known differences. Future you will appreciate that document more than present you expects.

Mistakes I Made So You Don't Have To

Here's my regret list:

  • Hardcoding model names everywhere. One migration meant 14 separate edits and two missed spots.
  • Trusting the vendor's "drop-in replacement" claim. It wasn't, and our JSON parser noticed first.
  • Skipping the cost check. The new model produced longer answers, and our bill crept up.
  • Waiting too long. I started the work three weeks before the deadline and regretted every day.

Common Pitfalls

  • String-swap migrations — the new model reads your prompts differently; measure, don't assume.
  • Floating aliases in production — behavior changes with no commit from you; pin exact versions.
  • No model inventory — feature-to-model mapping discovered mid-deadline is a treasure hunt.
  • Eval-less opinions — "feels better" arguments cost weeks; a golden set decides in hours.
  • Tuning many things at once — one change per eval run or you learn nothing from the delta.
  • Skipping shadow and canary — messy real inputs only exist in production traffic.
  • "Drop-in replacement" trust — vendors test their API, not your JSON parser.
  • Dead references after cleanup — old model names left behind confuse the next migration.

Best Book to Read

CoverBookDescriptionGet it
Cover of “Continuous Delivery” Continuous Deliveryby Humble & Farley the staged-rollout canon this playbook follows: baseline, canary, rapid rollback, and routine, boring releases. View on Amazon
Cover of “Designing Machine Learning Systems” Designing Machine Learning Systemsby Chip Huyen model versioning, monitoring, and the eval-first posture that makes swaps survivable. View on Amazon
Cover of “AI Engineering” AI Engineeringby Chip Huyen comparing candidate models on your own tasks, judge-based evaluation, and cost-quality trade-offs. View on Amazon
Cover of “Building LLM Powered Applications” Building LLM Powered Applicationsby Valentina Alto provider abstraction and config-driven model routing, the layer that makes the next migration a config edit. View on Amazon

As an Amazon Associate I earn from qualifying purchases. Links on this page are affiliate links, and I may earn a commission at no extra cost to you.

Unlock AI That Actually Works

Get lifetime access to GPT-6 Astra, Claude Fable 5.1, Gemini 3.5, Grok 4.5, and more — all in one platform. Build websites, apps, videos, content, and digital products from a single command. No monthly fees. No tool-hopping.

Click here to get GPTAstra Max now — one-time payment, lifetime access.

Frequently Asked Questions

Why does swapping an LLM model break apps?

Because a new model behaves differently even when the provider calls it an upgrade. Output style shifts (tone, verbosity, formatting), instruction following changes so old prompt tricks stop working, structured output varies (JSON habits, schema adherence, tool-calling), refusals on borderline requests can flip in either direction, latency and cost rarely match, and context windows, token counts, and rate limits differ. Your app depends on all of these without you realizing it — which is why a "better" model can make a product worse. The fix is treating migration as a measured change: inventory, config, baseline evals, and staged rollout instead of a string replacement.

How do you prepare before a deprecation email lands?

Build the habits months ahead. Keep a model inventory recording the feature, model name, prompt version, and owner for every call site — if "what uses model X?" takes more than a minute to answer, you have homework. Move hardcoded model names into one config file keyed by feature, so migration becomes a config change plus testing. Pin exact versions in production instead of floating aliases that silently point at new models. Wrap provider calls behind a thin abstraction layer so features call generate(feature, input) and the layer handles provider details. Subscribe to the provider's changelog in a shared channel with 90/60/30-day reminders. And own an eval suite: a 50–200 prompt golden dataset with success criteria, deterministic checks, a pairwise judge, and cost/latency tracking — teams with it finish in days, teams without it argue about screenshots for weeks.

What's the step-by-step migration playbook?

Seven steps, the same sequence every time. 1) Read the fine print: retirement date, suggested replacement, API changes to formats, parameters, or token limits. 2) Choose candidates: don't auto-accept the recommendation; shortlist two or three across quality, speed, and price, including a different size or vendor. 3) Run baseline evals: old model first, then each candidate with prompts unchanged, to see the raw gap — review the worst failures by hand. 4) Adapt prompts: loosen rigid instructions, tighten vague ones, rework few-shot examples, adjust temperature and max_tokens, update tool schemas — one change at a time, rerunning evals, because tuning without measurement is superstition. 5) Shadow mode: real traffic to the new model quietly while serving old answers, compared offline. 6) Canary: ~5% live traffic watching errors, latency, cost, refusals, and feedback, widening in stages. 7) Roll out, keep rollback fast, then delete old model references.

What details break LLM migrations in production?

The ones you don't check on purpose: tokenization differences that change costs and context limits; new rate limits — verify capacity before shifting full traffic; streaming chunk sizes and timing that alter your UI's feel; structured output reliability — measure malformed JSON rates, not happy-path samples; long-context behavior, which varies widely; and downstream parsers that break on small formatting shifts. Build safety nets before you need them: a fallback model routed on repeated failure, feature flags so you can revert without a deploy, alerts on error rate, latency, cost per request, and feedback scores, and output validation that rejects and retries malformed responses before users see them. Work backward from the retirement date across roughly six padded weeks, and start the day you hear — things never calm down.

Wrapping This Up

Providers will retire models, and you can't stop that. You can make it routine. Keep model names in config, pin versions, own a real eval suite, and follow the same playbook each time: baseline, tune, shadow, canary, retire.

Start with the inventory this week. When the next deprecation email arrives, you'll read it, shrug, and open your checklist instead of your panic.

What are You Looking For?

esc