Legacy and migration

5 min read

Branch by Abstraction: How to Replace Part of a System Without One Big Switch

The most dangerous part of a large migration is often not building the new implementation, but the day everything must switch at once. Branch by Abstraction replaces one large cutover with a sequence of smaller, reversible steps.

When a company needs to replace an important part of an old system, a natural approach is a separate development branch: build the new implementation in parallel, then merge it and replace the old one.

In practice those branches can live for months. The main product keeps changing, the new version falls behind, conflicts grow, and the final cutover becomes increasingly risky.

What problem are we solving?

Branch by Abstraction separates consumers from the concrete implementation through a stable boundary. First, the system stops calling the old component directly and goes through an abstraction. Then a new implementation is added behind that boundary.

For a period, old and new implementations can coexist. Traffic can move gradually by feature, customer group, environment, or scenario. Once the new implementation proves itself, the old one is removed.

This is not abstraction for elegance. Its purpose is to make migration reversible and reduce the size of each risky step.

What does the business get?

The business can modernize a critical component without betting on one large release. A defect in the new implementation does not have to invalidate months of migration work.

Just as importantly, product delivery can continue. The organization does not have to freeze the main system while a rewrite is in progress or constantly synchronize a massive long-lived branch.

The migration can be slowed, paused, or reconsidered if the economics turn out worse than expected. That reduces sunk-cost pressure: the company is not forced to finish a huge technical program simply because it has already invested heavily.

What does the team get?

The team gets shorter feedback cycles. The new implementation can be connected earlier and validated on real scenarios in limited scope.

Failures are easier to localize because it is clear which implementation served a request and where behavior differs.

The approach also supports continuous work on the main branch and avoids months of merge conflict. The price is that the abstraction boundary must be chosen carefully enough to support both implementations.

What does the customer get?

Customers mainly benefit from lower cutover risk. The new implementation can be exposed gradually, and traffic can return to the old one if problems appear.

If the migration is designed well, customers should not need to know that an important internal component is being replaced.

What do we pay for it?

For a while, the system contains two implementations. That temporary complexity has to be operated: both versions need support, behavior may need comparison, routing must be controlled, and data ownership must stay clear.

The abstraction itself can also be wrong. If it simply copies the old system's details, the new implementation inherits legacy constraints instead of escaping them.

There is also a risk of an endless migration. If the old implementation remains “just in case” for years, temporary architecture becomes permanent and can cost more than the original problem.

When is the approach unnecessary?

If the component is small, easy to test, and safe to replace in one short release, the extra boundary may add more complexity than value.

Branch by Abstraction is useful when migration is long-running, the component is critical, the product must keep evolving, and one large switch carries significant business risk.

What should we ask before using it?

In the end

A large migration does not have to become a large release.

Branch by Abstraction lets the company buy a new system in small units of risk while the old implementation gradually disappears without stopping the business.