Compatibility Window
also called Overlap Window, Dual-Version Period
The period during which two versions of a caller or message must both work, set by deployment and queue mechanics rather than by how many consumers there are, which is why even a single internal consumer requires compatibility.
The common argument against versioning internal APIs is that every caller is known and can be found with a code search. The argument confuses knowing who the callers are with being able to change them at the same instant, and nothing about being in the same company makes deploys atomic.
The compatibility window is the honest version of the question. How long must the old and the new form both work? Answer that, and the versioning decision follows mechanically rather than philosophically.
Why it matters
Three mechanics set the floor, and none of them depends on consumer count.
- Rolling deploys. Old and new instances of one service run together for the duration of the rollout, typically minutes. A breaking change must survive that even with a single consumer.
- Rollback. If a change went out and callers were updated, reverting it breaks them. Compatibility is what makes a deploy reversible, and reversibility is worth more than elegance in the hour after a bad release.
- Durable queues. A message produced before a change is consumed after it, so the format must span the queue's retention, which since durable job queues became standard practice around 2015 is routinely 7 days and sometimes far longer.
Implementation patterns
- Additive changes freely; breaking changes behind a new field, endpoint or message type, with the old one maintained until telemetry shows nobody uses it.
- Measure usage, do not ask for it. Per-field or per-endpoint counters, attributed to a caller, replace a census by email that finds subscribers rather than users.
- Set the window from mechanics plus a margin: the longest of rollout duration, queue retention and replay horizon, then add the slowest consumer team's release cadence.
- Expire deprecations by traffic, not by calendar. "Removed when weekly calls reach zero for two weeks" is a condition that can be met; "removed on 30 June" is a date that slips because it was never tied to anything observable.
- Make the old path noisy before it is gone: a warning header, a log line with the caller identity, a dashboard. Silence is what lets a deprecation reach its deadline untouched.
Industry example
Public cloud providers publish these windows as policy, typically 12 months or more for a deprecated API, and internal platform teams at large organisations tend to converge on weeks to a quarter. The mechanics are the same in both cases and only the duration differs, which is the useful insight: internal platforms may shorten the window, not skip it.
Failure scenarios
- The flag day that slips. Coordinating simultaneous deploys works at three consumers and fails at thirty; the team that cannot deploy that day becomes an outage.
- The unrollbackable change. The API is fixed forward under pressure because reverting would break updated callers, which removes the safest option at the worst moment.
- The queue surprise. Everything works for six days, then a retried message from before the change is consumed and fails with a deserialisation error nobody can reproduce.
- The invisible caller. A Terraform provider, a scheduled script, a partner integration: present in traffic, absent from code search.
Trade-offs
Maintaining two paths costs code, tests and cognitive load, and it is genuinely worse than a clean cut when the cut is safe. The counterweight is that the cost is bounded and known, while the cost of a broken change is unbounded and arrives unplanned. For internal platforms the pragmatic position is a short window, aggressively measured, rather than either extreme.
When not to use it
When the caller and the callee deploy as one unit — the same binary, the same release, the same rollout — there is no window and compatibility is wasted work. This is one of the strongest arguments for a modular monolith: internal interfaces within one deployable can change freely, and the moment they cross a deployment boundary they acquire a compatibility window whether or not anybody writes one down.
Interview question
Q: Your internal API has exactly one consumer, owned by the team sitting next to you. They agree to a breaking change and you plan to deploy both within the hour. What can still go wrong?
What a strong answer covers: the rolling-deploy overlap on both sides · in-flight requests and retries from the old form · messages already in a queue · rollback becoming impossible for either service independently · clients you did not count, such as scripts and dashboards · the conclusion that the minimum window is set by mechanics and is never zero across a deployment boundary, though it may be an hour rather than a quarter.
Quick check
Quiz: What sets the floor on a compatibility window? The longest of rollout duration, queue retention and replay horizon, all of which are independent of consumer count.
Flashcard: When is a compatibility window genuinely zero? — When caller and callee ship in the same deployable unit, which is why intra-monolith interfaces can change freely.