Chapter 3 of 6 · 18 min read
Deprecation dates and internal deadlines
On this page
- Four dates, and only one of them breaks anything
- The notice arrives somewhere nobody reads
- The standing cost of a served version
- One model on three platforms has three calendars
- Retirement dates move, mostly in your favour, which is the trap
- Working backwards from shutoff produces a much earlier date
- The record has an owner or it has nothing
- A served model can lose capacity without any date moving
- Parameters, API versions and beta headers have their own calendars
A pinned model version has an expiry, and the provider publishes it. A pin
here is the dated snapshot id a service sends in its model field in place of
a family alias — claude-sonnet-4-5-20250929 rather than claude-sonnet-4-5 —
recorded in your own configuration so that the version changes when you change
it. What a pin buys is time, and that time is a date somebody has to read off a
vendor's page and be right about, from a page carrying several dates at once
that does not say which of them stops a service working.
The events differ in what they change for you and in how reliably they are published, so separating them comes first. What follows is arithmetic, because a shutoff is a last possible moment rather than a deadline.
Four dates, and only one of them breaks anything
Provider lifecycle language is more consistent than it looks once you separate the events it describes.
Announcement is the day the provider says a model is going away. It changes
nothing technically and everything organisationally: it is the moment the clock
you are accountable to starts, and it is also the only one of these dates that
arrives as a push rather than something you have to go and look up. Providers
commit to notice periods — typically measured in months for models in general
availability — GA, the stage at which a model is open to everyone and supported
for production use — and much shorter for previews and experimental releases,
which is a distinction worth checking before you build a revenue surface on
something labelled preview.
Deprecation is a status, not a deadline. A deprecated model still serves traffic and still returns 200s. What changes is entitlement and support: new accounts often cannot select it, it stops appearing in default routing and in the console's model picker, and it is excluded from new features. Code that reads a model list programmatically and picks "the first one" starts behaving differently here, which is a fun class of bug to discover in a batch job.
End of the support window is where the ambiguity lives, because it means different things across providers and sometimes goes unnamed. It is roughly the point at which the provider stops treating problems with that model as bugs — no fixes, no capacity guarantees, sometimes tighter rate limits, sometimes removal from the platforms where it was mirrored. Requests still succeed, which is exactly why it is dangerous: your service degrades in ways that look like your fault.
Retirement, or shutoff, is the day the id stops resolving. Requests naming
it return an error — a 404 not_found_error on most APIs — and they do not
degrade gracefully or fall back. Every code path that names that string fails
at once, including the ones in a cron job nobody has read since 2025.
| Event | What changes for you | Where you read it | Reliability of the date |
|---|---|---|---|
| Announcement | The clock starts; nothing technical | Changelog, email, deprecations page | Fixed once published |
| Deprecation | No new access, dropped from defaults and pickers, no new features | Status column on the deprecations page | Usually the same day as announcement |
| End of support window | Capacity and rate limits stop being guaranteed; no fixes | Often not published as a date at all | Frequently implicit |
| Retirement / shutoff | Requests naming the id fail outright | Shutdown or retirement column | Published, and it moves — usually later |
Two of those rows have no reliable date attached, which is a reason to plan against the two that do rather than against a feeling. The published calendars are the primary sources: Deprecations — OpenAI API is the retirement calendar and notice policy for that provider, and the cross-provider AI Model Deprecation and Lifecycle Calendar is convenient for planning across three vendors at once, with the caveat that any aggregate lags the provider it aggregates. Read the aggregate to notice what is coming; verify against the provider before you put a date in a plan.
The notice arrives somewhere nobody reads
Providers do push the announcement, and the push lands in places that are structurally bad at reaching engineers. The email goes to the account's billing or admin contact, which in most companies is a finance address or a founder who set up the account three years ago. The console shows a banner to whoever logs in, and nobody logs into the console on a working service. The changelog has an RSS feed that one person subscribed to and now filters into a folder. Meanwhile the engineers who own the pin find out from a colleague's link in a channel, or from somebody going to look after an unrelated problem.
Two cheap corrections cover most of the gap. Route the provider's notification address to a team alias rather than an individual, and put the changelog feed into the same channel where your deploys and alerts already land — a webhook into a low-traffic channel costs an afternoon and is the only mechanism in this chapter that fires without anybody remembering to look. Neither is sufficient, because a notice in a channel is still a notice nobody owns, but they turn "we did not know" into "we knew and did not act", which is a much more tractable failure.
The standing cost of a served version
Nothing about a set of trained weights wears out. The numbers are the same numbers a year later, and the arithmetic they describe would go on producing the same scores indefinitely. Retirement is therefore not a fact about the model at all; it is a fact about the arrangement that serves it, and that arrangement has a cost which behaves in a particular way. Almost everything a deprecations page does is downstream of how that cost behaves, and no provider states it in these terms.
To answer a request within the time an API promises, a version's weights have to be sitting on accelerator hardware that is already powered, already loaded and already waiting. Fetching them when a request arrives is not an option at interactive latency: the weights are very large, moving them onto the hardware takes far longer than the whole response budget, and every request that arrives during the move waits for all of it. So a version that is available is a version occupying hardware continuously, whether or not anybody is calling it. What the provider is paying for is not the requests. It is the readiness.
That single property is what the rest follows from. Because the cost is set by the hardware the version sits on rather than by how often it is called, the cost does not fall as the version's traffic falls — a version almost nobody uses costs very nearly what a busy one costs. The hardware, meanwhile, is finite, every newly released model wants some of it, and the models with traffic want more of it over time. The number of versions a provider can keep simultaneously resident is therefore bounded, and retirement is the mechanism that enforces the bound. A deprecations page is a capacity decision written in lifecycle vocabulary. This is the general shape of the constraint rather than a description of any one provider's infrastructure, and the specific numbers behind it are not published by anybody.
Three things that look arbitrary from outside stop looking arbitrary once that is in view. Extensions get granted because the bound is on concurrent residency rather than on any particular date, so capacity freed somewhere else, or a customer large enough to be worth the capacity on its own, changes what the provider can afford to keep up. A notice period is a commitment to hold capacity for a stated stretch after the announcement, which is why models in general availability get one and previews, which were never given that commitment, can be taken back on much shorter terms. And an offer to keep paying the published rate is not, by itself, an argument: the published rate prices requests, the scarce thing is residency, and a version serving a trickle of traffic at the list price is the least defensible occupant of a machine that something else wants.
This is where a belief forms that is worth naming, because it is tempting for good reasons and wrong in a way that costs a quarter. The belief is that a deprecation notice is a deadline you control. Everything about the way it arrives supports that reading. It is addressed to your account, it names your model, it carries a date, and it lands in the same inbox as every other date an engineer is handed — a ticket due date, a release date, a review date — all of which are negotiable by moving work around, because the constraint behind every one of them is your own team's time.
The constraint behind this one is not your time, and nothing you reschedule touches it. The date is an output of somebody else's capacity plan, computed from inputs you cannot see and do not supply. What that leaves on your side of the line is exactly two quantities: when you start, and what you have qualified by the time the date arrives. Both of those are entirely yours. The arithmetic in this chapter is arithmetic on those two, which is the only place the effort has anywhere to go.
One model on three platforms has three calendars
The same weights served through a cloud marketplace, a first-party API and a partner platform are three products with three availability schedules, and the partner platforms typically lag the first-party one on both arrival and departure. If your service calls the model through one platform and your eval harness calls it through another, the two can be looking at different retirement dates for what everyone believes is the same model. Record the platform alongside the id, and read the calendar for the platform you actually call.
Why they diverge follows from the standing cost. Availability is a property of an arrangement to hold capacity ready, not a property of the weights, and each platform is a separate arrangement: its own hardware pool, its own contract with its own support obligations, and its own set of customers whose usage decides when that capacity is better spent on something else. Two platforms serving byte-identical weights will reach that moment on different days, and neither is under any obligation to wait for the other. The id itself gives you no warning of this, because it is the same string, or very nearly, on all three. It names the weights, and the weights are the half of the pair that has no schedule.
Retirement dates move, mostly in your favour, which is the trap
Published shutoff dates get extended. It happens often enough that engineers learn to discount them, and the discount is the problem: a team that has watched two deadlines slip stops treating the third as real, and the third is the one that holds. Dates also occasionally move the other way, and previews get withdrawn with far less ceremony than a GA model.
The consequence is operational rather than philosophical. A retirement date is a fact with a shelf life, so recording it once and filing it is not enough — the record needs to carry when it was last checked against the source, and something needs to re-check it. A date verified nine months ago is a rumour.
What makes it a rumour rather than merely an old fact is the shape of the
record. The date sitting in your repository is a copy of something held on the
provider's page, and a copy does not update itself when the original moves. The
two agree right up until they do not, and no step in your build, your deploy or
your review notices the moment they stop agreeing, because nothing in that
pipeline has ever read the original. That is the job of the verified_at field
further down this chapter. It does not record the date. It records the day
somebody last stood the copy next to the thing it is a copy of.
The drift is also not symmetric, and the asymmetry is what does the damage. Extensions are common and they are quiet, so a stale copy is usually pessimistic — it says a version dies sooner than it now does. A team acting on the pessimistic copy watches the real date slide past the one it wrote down, and learns the lesson that is correct for those instances and disastrous in general: that these dates are soft. The discount engineers learn to apply is not a character flaw in them. It is manufactured by stale copies, each one confirming the lesson, until a copy happens to be accurate and the lesson costs a surface.
Working backwards from shutoff produces a much earlier date
The date your team works to is not the shutoff. It is the shutoff minus everything that has to happen before it, minus one failed attempt, minus the periods in which your organisation does not ship.
Take a 15 May 2027 shutoff and lay the work out backwards.
The chart is read from the right, and the direction is doing real work. Working forwards asks how long the work will take and then checks whether it fits before the shutoff, which is a question whose answer bends towards fitting, because every estimate in it belongs to the person who wants it to fit. Working backwards asks something that cannot bend, because the date at the end of it is fixed by somebody else: the latest each piece can start and still leave the piece after it whole. Each subtraction is then a commitment that the stretch below it will not be borrowed from, and the plan fails loudly at the moment somebody proposes borrowing rather than quietly at the end.
Take the terms one at a time, from the shutoff backwards. The reserve comes off first, because a reserve left until last is a reserve that has already been spent: you want the last tenant moved with weeks in hand rather than days, because the failure you have not thought of shows up during cutover and needs somewhere to be fixed, and only a stretch that is not promised to any named activity can absorb it. Burn-in on the new version is two weeks of real traffic before you declare it done, and it is in the arithmetic because some behaviour only appears at the volume and variety of production — month-end batches, the retry path, the one tenant whose documents are unlike everyone else's — and no amount of effort makes that appear sooner than the traffic does. The cutover itself is four weeks if you are sequencing by tenant rather than flipping a global switch. Those four weeks are the sequencing rather than the work, so compressing them does not mean working faster; it means moving more tenants at once, which is the same as giving up the evidence that ordering was buying you.
The next subtraction is the one plans routinely omit, and omitting it is what makes an otherwise sensible schedule fail. A second re-qualification attempt — because the first one surfaced a regression and somebody had to rework a prompt — is six weeks. It belongs in the arithmetic because re-qualification is a measurement, and a measurement whose result is allowed to be no is one that will sometimes come back no. A plan with no room for that outcome has not taken a risk; it has assumed an answer, and it discovers the assumption at the one point where there is nothing left to spend. Budgeting a failed attempt is not pessimism about the successor. It is the thing that makes the first attempt's result usable whichever way it falls, which is the only condition under which running it early is worth anything.
That leaves the first attempt, and it is where the units change underneath you. The first attempt is six calendar weeks that contain about three working ones, because your organisation has a change freeze from 18 December to 4 January. Calendar weeks and working weeks are different quantities, and a subtraction is only valid when both terms are in the same unit — a freeze, a holiday stretch, a quarter close, the fortnight when half the team is at a conference — each of them converts calendar weeks into working weeks at a rate the calendar itself does not show. Doing the arithmetic in calendar weeks and meeting the freeze afterwards is how a plan that looked comfortable on the page becomes late in reality without any single person in it having missed a step.
That arithmetic produces a decision point on 15 January 2027: by that date the successor is either qualified or the situation is escalated to someone who can reallocate a quarter. Four months earlier than the date everybody wrote down. The gap between 15 January and 15 May is not padding; it is the only reason a bad result in January is recoverable.
The specific numbers are yours to set, and setting them is the exercise. Two of them are worth arguing about explicitly. The first is how long a re-qualification actually takes in your team, which you can measure the next time you do one instead of guessing. The second is the slack reserve, and the honest framing is that it is the amount of failure you are prepared to absorb: three weeks of reserve means you can survive discovering, at the last moment, one problem that takes three weeks. Teams that set it to zero are not being efficient, they are betting the surface on nothing going wrong.
The record has an owner or it has nothing
A date with no name against it is not owned, and a date owned by "the platform team" is owned by nobody in particular, which produces the same outcome more slowly. The record belongs next to the pin itself, so that the version and its expiry cannot drift apart:
# config/models.yaml
extraction:
provider: anthropic
model: claude-sonnet-4-5-20250929
previous: claude-sonnet-4-5-20250514
tier: revenue # blast radius if this fails: customer-facing, billed output
owner: r.okafor # a person, not a team
lifecycle:
announced: 2026-08-10
retires: 2027-05-15
source: https://platform.claude.com/docs/en/about-claude/models/overview
verified_at: 2026-08-11
internal_deadline: 2027-01-15Four of those fields do work that is not obvious. tier is what lets you treat
a pin behind a billed customer surface differently from a pin in a scratch
script, which matters as soon as you have more than a handful. source is a
URL somebody can open to check, which turns "where did this date come from"
from a conversation into a click. verified_at is what makes staleness
detectable; it is the field that decays, and something has to notice when it
does. And internal_deadline is the output of the backwards arithmetic,
recorded so that the reasoning does not have to be redone by whoever inherits
this.
The owner field is a person because migrations need someone whose name is attached when the date approaches and the quarter is full. Rotate it deliberately when people change teams, and treat an owner who has left as a broken record rather than a stale one.
A served model can lose capacity without any date moving
The calendar answers when the model stops existing. It says nothing about when the model stops being good enough, and those can be very far apart. A deprecated model that is still served can lose capacity, sit behind lower rate limits, or be routed to older hardware, and none of that appears on a deprecations page. The signal for it is your own latency and error metrics, not the vendor's calendar.
The reason that gap is so hard to spot is a mismatch between where the change happens and where anybody is looking. A capacity or routing decision on the provider's side does not produce a response field announcing itself; requests carry on returning 200s, exactly as the deprecation status promised they would. What arrives instead is longer tail latency, more retries, and rate-limit responses at volumes that used to be comfortable. Every one of those is a shape your monitoring already recognises, and recognises as your own problem, because nine times out of ten it is. So the investigation starts inside your own stack — a connection pool, last week's deploy, a slow dependency — and a deprecations page is nowhere on the list of places to look for the cause of a latency regression. That is why "requests still succeed" is the dangerous property of this window rather than the reassuring one.
What turns that around is cheap and only works if it exists beforehand. The record beside the pin already carries the version and its dates; adding a note of that version's normal latency and error profile, taken while it was still in general availability, gives whoever is on call a comparison they can actually make. The question stops being "is this slow", which has no answer, and becomes "is this slower than this version was before it was deprecated", which has one. Without that baseline a deprecated pin that is quietly degrading and a deprecated pin that is perfectly healthy look identical from inside your own graphs.
Parameters, API versions and beta headers have their own calendars
A shutoff date does not bound your actual exposure. Model retirement is the
loudest lifecycle event but not the only one: API versions get deprecated,
parameters get removed, tool schema versions get superseded, and beta headers
stop being accepted. Each of those breaks a request naming them exactly as
thoroughly as a retired model id does, and they run on separate calendars that
no model registry keys on. A migration plan that tracks only model ids will
eventually be surprised by a temperature parameter that started returning a
400.
A date is worth what the work behind it is worth. Having 15 January 2027 in a calendar with a name next to it is a large improvement on "next May", and it is still only a marker: it does not say what has to be true on that date for the answer to be yes, nor who decides. Both are downstream of measuring the successor, which is where the next difficulty is: it can come back ahead of the incumbent on the number everyone quotes and still be unshippable, because that number averages a mixture nobody chose.
Model deprecation and version pinning — read the current version at https://daviddominguez.dev/learn/en/model-deprecation-and-version-pinning/deprecation-dates-and-internal-deadlines