# I build migrations with two valid paths.

How compatible contracts, observable cutovers, and ordinary rollback reduce the risk of changing a live system.

- **Canonical URL:** https://vidhata.me/notes/i-build-migrations-with-two-valid-paths
- **Author:** Vidhatanand V. (Vid)
- **Role:** Fractional CTO and AI Systems Architect

**Published:** 04 Sep 2026 | **Reading time:** 9 minute read

## Why I hold this position

A migration becomes dangerous when the new path can work only after the old path has been dismantled. That design turns every unknown into a deadline. If the new queue, store, service, or interface behaves differently under real traffic, the team has no valid place to return.

I prefer migrations in which old and new remain legitimate paths for a bounded period. The goal is not permanent duplication. The goal is to preserve reversibility while evidence is still arriving. A cutover should be an operating decision, not a leap of faith hidden inside a deployment.

## A migration begins with a contract that both paths can satisfy.

Before moving traffic, I define the behaviour that must remain stable: input shape, output meaning, ordering, idempotency, error states, latency expectations, and the data that becomes authoritative. The old implementation and the new one may differ internally, but callers should not need to understand the migration.

Compatibility layers are useful when they are explicit. A cache-compatible interface can sit in front of a legacy data source. A queue adapter can allow two transports to satisfy the same job contract. A provider boundary can let a new service compete behind existing evaluation fixtures. The abstraction is not decoration. It is the place where equivalence is tested.

## I move traffic before I move authority.

Where practical, I let the new path observe or process a controlled share while the old source of truth remains intact. Shadow reads, duplicated non-consequential events, comparison reports, or a limited tenant cohort can reveal mismatches before the system depends on the result.

The comparison needs named tolerances. A new path is not equivalent merely because both return data. Ordering, missing records, stale caches, retry behaviour, and side effects may differ. I want a reconciliation view that tells the team what disagreed and whether the difference is acceptable, repairable, or evidence that the cutover should stop.

## Rollback is a product capability.

A rollback plan that exists only in a document is weaker than a route the system can still execute. Configuration should make traffic direction visible. Versioned schemas and messages should prevent a new writer from making old readers unusable. Operators should know which completed actions are safe to replay and which require reconciliation.

I also make the trigger for rollback explicit. Error rate alone may not be enough. Data divergence, queue age, latency, cost, lost audit fields, or a change in user-visible meaning can all justify returning traffic. The team should not have to invent the threshold while the migration is already failing.

## The old path leaves only after evidence says it can.

Successful cutover is not the moment the switch moves. I look for a full operating window with stable metrics, reconciled data, no unknown consumers, updated runbooks, and a tested recovery path. Only then does removal become a separate, reviewable change.

This discipline costs some temporary complexity, but it reduces irreversible complexity later. The team learns about the new system while control still exists. That is why I design two valid paths. Reversibility is most valuable before confidence is complete.

## What I take into the next build

- Define stable behaviour before selecting the migration mechanism.
- Compare old and new paths with named tolerances.
- Keep rollback executable, observable, and safe for data.
- Remove the old path only after a complete operating window.

## Continue reading

[All Fractional CTO field notes](https://vidhata.me/notes)

## Work with Vid

Start with the actual technical pressure: [bring the problem](https://vidhata.me/hire).
