The idea in one minute#
In ordinary software you choose a database and live with it for ten years. In AI the central component — the model — is replaced every few months, whether you want it or not. Providers release new versions several times a year and retire old ones on a published schedule, often with only months of notice. A system built in 2025 has already been through two or three forced model changes.
So the question is not “how do we avoid lock-in?” in the abstract. It is “how cheap is our next model change?” — because there will certainly be one. An organisation that can swap a model in a week treats each new release as an opportunity. One that needs a quarter treats each retirement as a crisis.
Portability comes from a few deliberate seams: a gateway in front of every model, prompts and evaluations that you own, tools exposed through open protocols, and data kept in stores you control. Full independence from every vendor is neither possible nor worth its price. The skill is choosing where to stay flexible and where to commit.
A picture#
flowchart LR
subgraph OWN["What you own — keep portable"]
direction TB
O1[":i-file-text: <b>Prompts</b><br/><small>versioned, per model family</small>"]
O2[":i-flask-conical: <b>Evaluation sets</b><br/><small>the migration test</small>"]
O3[":modelcontextprotocol: <b>Tools over MCP</b>"]
O4[(":i-database: <b>Data and indexes</b><br/><small>re-embeddable</small>")]
O5[":i-scroll-text: <b>Traces and history</b>"]
end
GW[":envoyproxy: <b>Gateway</b><br/><small>logical names: fast, smart</small>"]
OWN --> GW
GW --> M1[":i-brain: <b>Model A v5</b><br/><small>current</small>"]
GW -.->|"evaluated, ready"| M2[":i-brain: <b>Model A v6</b><br/><small>next</small>"]
GW -.->|"fallback"| M3[":i-brain: <b>Model B</b><br/><small>second provider</small>"]
CAL[":i-clock: <b>Lifecycle calendar</b><br/><small>release, deprecation, retirement</small>"] -.-> GW
class O1,O2,O3,O5 compute
class O4 memory
class GW queue
class M1 io
class M2,M3 neutral
class CAL warnHow it really works#
The life of a model version#
| Stage | What it means | What you do |
|---|---|---|
| Preview | Available to try; may change; often no service commitment | Evaluate; do not put it on a critical path |
| Generally available | Supported for production | Adopt after it passes your gate |
| Deprecated | Still works; a retirement date has been announced | Start the migration now |
| Retired | Requests fail | You should have left already |
Two further facts shape planning. The same model can have different dates on different platforms — a version retired on its maker’s own API may remain available on a cloud platform, or the reverse. And model aliases that point to “the latest” change behaviour under you without warning; production systems should pin an exact version and move deliberately.
Where lock-in actually lives#
Rank the layers by how hard they are to change, and spend your portability effort from the top.
| Layer | Why it sticks | How to keep it loose |
|---|---|---|
| Data and its governance | Volume, permissions, pipelines; moving it is a programme | Keep source data in stores you control; treat indexes as rebuildable |
| Identity and network | Deep in the enterprise landing zone | Accept this commitment — it is the reason you chose the cloud |
| Agent state and memory | Stored in a platform’s proprietary format | Prefer runtimes with exportable state; store long-term memory in your own database |
| Proprietary platform features | Managed agents, built-in tools, vendor-specific retrieval | Use them where the saving is large; write an ADR naming the exit cost |
| Embeddings | Vectors from one model cannot be searched with another | Keep the source text; budget for re-embedding |
| Prompts | Tuned to one model’s habits | Version them per model family; rely on the evaluation set to re-tune |
| The model API | Parameter and feature differences | A gateway and a thin internal interface |
| Fine-tuned models | Hosted fine-tunes do not transfer | Keep the training data and the evaluation; retraining is the migration |
The model API — the thing most people think of as lock-in — is near the bottom. Data, state and proprietary platform features are where organisations actually get stuck.
Five seams to build in#
- A gateway with logical model names. Applications ask for “fast” or “smart”; the mapping to a real model is configuration.
- Prompts as versioned assets, outside the code. With the evaluation results they were tested against.
- Evaluation sets you own. The single most important portability asset: they turn “will the new model work?” from a debate into a test run.
- Tools behind open protocols. An enterprise system exposed over MCP serves any compliant agent, on any platform.
- Your own record of what happened. Traces, conversation history and feedback in your stores, in a documented format — not only inside a vendor’s console.
How much portability to buy#
Portability is insurance, and insurance has a premium: lowest-common-denominator features, extra abstraction, two of everything to test. Buy it according to exposure.
| Situation | Sensible posture |
|---|---|
| Early, low-risk, internal tool | Single provider. A gateway and owned evaluations are enough |
| Core business process | Primary plus an evaluated fallback on a second provider or region |
| Regulated or critical service | Fallback exercised regularly; documented exit plan; contractual notice periods |
| Extreme sensitivity or scale | An open-weight option you can run yourself, kept warm for the critical path |
“Multi-cloud everything” is rarely the right answer. Running one workload identically on three platforms triples the testing and forbids the best features of each. A stronger and cheaper position is one primary platform, clean seams, and a rehearsed exit.
The migration procedure#
When a new version arrives or a retirement is announced, the procedure is the same — and should be boring.
- Read the release notes for behaviour and parameter changes, not only the headline.
- Run the evaluation set against the candidate with the existing prompts.
- Read the differences, case by case. Regressions are usually concentrated in one slice.
- Re-tune prompts where needed. Newer models often want less instruction, not more.
- Check the system metrics: latency, token counts, cost. A cheaper price per token with longer outputs can cost more.
- Shadow, then canary in production; compare quality and cost on live traffic.
- Switch, keep the old route available until the new one has run clean, then remove it.
- Record the result in the decision log.
With the seams in place this is days of work for one system. Without an evaluation set, step 2 becomes “try it and hope”, which is why migrations without evaluations take months.
A lifecycle process for the organisation#
At platform level, model change becomes a routine.
| Element | What it is |
|---|---|
| Model catalogue | The approved models, each with a logical tier, status, and the date it must be left by |
| Lifecycle calendar | Retirement dates for every model in use, with owners |
| Lead-time rule | For example: migration starts no later than 90 days before retirement |
| Subscription to notices | Someone named reads every provider’s deprecation announcements |
| Standing budget | Time reserved every quarter for upgrades — it is never zero |
| Contract terms | Notice periods and version availability negotiated where the provider allows |
Exit plans#
For any strategic dependency — a platform, a vendor product, a model family — write a one-page exit plan when you adopt it, not when you need it.
EXIT PLAN — Managed agent runtime, vendor X reviewed yearly
What we depend on Hosted agent execution, built-in memory, built-in code sandbox
What we own Prompts, tool servers (MCP), evaluation sets, traces (exported nightly)
What we would lose Stored agent memory (proprietary format) — export tested quarterly
Replacement Self-hosted runtime on our cluster using the same agent framework
Estimated effort 3 engineers × 10 weeks; no change to tools or evaluations
Triggers Price increase > 30%; loss of in-region processing; two major outages
Notice we need 6 months — contract gives 12The plan almost never gets executed. Writing it changes the adoption decision, the contract and the architecture, which is its real purpose.
Code#
A lifecycle check a platform team can run every week: for each model in use, how long until retirement, whether a replacement has passed evaluation, and whether the migration is late.
// lifecycle.go — which model migrations are due, late or unprepared.
package main
import (
"fmt"
"sort"
"time"
)
type deployment struct {
system, model string
retires string // provider's announced date, "" if none yet
replacement string
evalPassed bool
weeksOfWork int
}
func main() {
today := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
const leadDays = 90 // migration must have started this long before retirement
fleet := []deployment{
{"claims extraction", "mid-v5", "2027-03-01", "mid-v5.5", true, 2},
{"policy assistant", "mid-v4", "2026-12-15", "mid-v5.5", false, 3},
{"email triage", "small-v4", "2026-11-10", "small-v5", false, 1},
{"coding agents", "frontier-v5", "", "", false, 0},
{"customer assistant", "mid-v4", "2026-12-15", "mid-v5.5", true, 4},
}
type row struct {
d deployment
days int
status string
}
var rows []row
for _, d := range fleet {
if d.retires == "" {
rows = append(rows, row{d, 1 << 30, "no retirement announced — keep watching"})
continue
}
t, _ := time.Parse("2006-01-02", d.retires)
days := int(t.Sub(today).Hours() / 24)
slack := days - d.weeksOfWork*7
var status string
switch {
case slack < 0:
status = "AT RISK: not enough time left for the work"
case !d.evalPassed && days < leadDays:
status = "LATE: replacement not yet evaluated"
case !d.evalPassed:
status = "evaluate the replacement"
default:
status = "ready: schedule the switch"
}
rows = append(rows, row{d, days, status})
}
sort.Slice(rows, func(i, j int) bool { return rows[i].days < rows[j].days })
fmt.Printf("%-20s %-12s %10s %s\n", "system", "model", "days left", "status")
for _, r := range rows {
left := "—"
if r.d.retires != "" {
left = fmt.Sprint(r.days)
}
fmt.Printf("%-20s %-12s %10s %s\n", r.d.system, r.d.model, left, r.status)
}
}The output is the meeting agenda. Sorted by days remaining, it shows at a glance which system is late — not because the work is hard, but because nobody ran the evaluation in time.
Remember this#
- Model change is certain and frequent. Design for a cheap next migration.
- Pin exact versions in production. Track retirement dates as operational risk.
- Lock-in lives mostly in data, state and proprietary platform features — not the model API.
- Five seams: gateway, versioned prompts, owned evaluations, open tool protocols, your own records.
- One primary platform with clean seams and a rehearsed exit beats multi-cloud everything.
Try it#
- Run
lifecycle.go. Which system needs attention first? ChangeleadDaysto 180 and see what a more cautious policy would have flagged. - Look up the published deprecation schedule of one model provider. Which generally available model has the nearest retirement date, and how much notice was given?
- Write a one-page exit plan for an AI product or platform you depend on. Which line was hardest to fill in?
Check yourself#
- Why is “how cheap is our next model change?” a better question than “how do we avoid lock-in?”
- Which layers are hardest to move, and which is usually easiest?
- Why is an evaluation set the most important portability asset?
- What is the argument against running every workload on three clouds?