July 15 2025
Old and New Versions Coexisting at the Same Time
How to plan compatibility across code, contract, and data when a perfect cutover simply does not exist.
Andrews Ribeiro
Founder & Engineer
4 min Intermediate Systems
The problem
Many migrations start with a quiet fantasy:
“At deploy time, everyone starts using the new version.”
In practice, that is not what happens.
During the transition, you often have all of this at once:
- old instances
- new instances
- outdated mobile apps
- old jobs still running
- old messages arriving from the queue
- external clients still using the previous contract
If your design depends on an instant switch, the environment is not the real problem.
The plan is.
Mental model
Think about it like this:
mixed state is not a rollout bug. It is the normal state of a system in change
Once you accept that, the right question stops being:
“How do I make everyone change together?”
And becomes:
“How do I make old and new talk to each other without getting in each other’s way for long enough?”
Breaking the problem down
Separate read compatibility from write compatibility
Those two things look connected, but they are not the same.
Read compatibility answers:
- can new code understand old data?
- can a new consumer handle an old event?
Write compatibility answers:
- does the new producer emit something the old consumer still accepts?
- does the new application write data the old one can still read?
When teams mix those two, they often break half the ecosystem without realizing it.
Prefer additive change before destructive change
Safe change usually looks like this:
- add an optional field
- accept the old format for a while
- read the new thing when present and fall back to the old one when not
Risky change usually looks like this:
- remove a required field too early
- swap the format all at once
- assume the old consumer will adapt by itself
Good compatibility usually buys time.
Let producers and consumers age at different speeds
In real systems, both sides almost never update together.
That is why one simple rule helps a lot:
- the consumer should be tolerant when possible
- the producer should be conservative while the change is still rolling out
That prevents the faster side from taking down the slower side.
Have criteria for coexistence and criteria for removal
Coexisting forever turns into accumulated garbage.
But removing too early turns into an incident.
You need to define:
- how long the temporary compatibility will exist
- how to measure whether old traffic still exists
- which dependencies need to leave before removal
- which rollback path still depends on the old format
Without that discipline, the old version becomes a permanent ghost.
Do not hide lack of compatibility behind version numbers
Version numbers help organization.
They do not replace compatibility policy.
You can still have:
- an API v2 that breaks clients every week
- a “versioned” event that still demands perfect synchrony
- a payload with a nice number and terrible coexistence
Version without coexistence rules is just a label.
Simple example
Imagine a paid-order event.
Before:
{
"orderId": "o-123",
"status": "paid"
}
Now the team wants to add more context:
{
"orderId": "o-123",
"status": "paid",
"paidAt": "2026-03-23T18:00:00Z",
"source": "pix"
}
If the old consumer ignores unknown fields, it stays alive.
If the new consumer accepts that paidAt and source may still be missing in older messages, it also stays alive.
That coexistence is much safer than swapping the whole format and expecting perfect synchrony.
Common mistakes
- Assuming simultaneous deploy will happen.
- Making the new consumer break when the new field does not exist yet.
- Removing the old contract without measuring who still depends on it.
- Creating a new version with no deprecation strategy.
- Letting temporary compatibility become an eternal rule because nobody defined the end.
How a senior thinks about it
Someone with more experience treats mixed state as part of the design, not as operational detail.
The good question usually sounds like this:
“If half the ecosystem is old and half is new one hour from now, what still works?”
If the answer is “almost nothing,” the change is still weak.
What the interviewer wants to see
In interviews, this topic measures whether you understand distributed systems outside the idealized mode.
The evaluator wants to see whether you:
- accept partial coexistence between versions
- think in read and write compatibility
- prefer additive change when possible
- define removal criteria, not only introduction criteria
A strong answer often sounds like this:
“I would design the change so old and new can coexist for a while. That means tolerant consumers, conservative producers, additive change first, and removal only after I can prove the old path stopped being necessary.”
A system in transition does not need perfection. It needs enough compatibility to cross the change without breaking.
The most dangerous moment is not when everything is old. It is when half became new.
Quick summary
What to keep in your head
- Mixed state between old and new versions is not an exception. It is a normal phase in real systems.
- Compatibility has to be thought through in reads, writes, contracts, and stored data, not only in version numbers.
- Additive change is usually safer than demanding perfect synchrony between producers and consumers.
- Removing the old path is only safe when you can prove it is no longer needed.
Practice checklist
Use this when you answer
- Can I explain why distributed systems almost never switch perfectly all at once?
- Do I know the difference between read compatibility and write compatibility?
- Can I describe one additive change and one safe removal?
- Can I explain in an interview how I would let old and new coexist without breaking clients or consumers?
You finished this article
Next step
API Versioning in Practice Next step →Share this page
Copy the link manually from the field below.