Skip to main content

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

Andrews Ribeiro

Founder & Engineer

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

Practice checklist

Use this when you answer

You finished this article

Next article Testing Modern Frontend: State, Async, Network, and UI Previous article Strangler Fig in Practice

Keep exploring

Related articles