Get Started Free
Wade Waldron

Wade Waldron

Staff Software Practice Lead

Branch by Abstraction

Overview

Branch by Abstraction is a pattern used in Trunk-Based Development. Rather than performing source control branches to make changes to the code, we instead create an abstraction in front of the changes we want to make. We then create a second code branch behind the abstraction in order to implement the changes. This has several key advantages for avoiding code drift, allowing A/B testing, and more.

Topics:

  • Source Control Branching
  • Code Drift
  • Branch by Abstraction
  • A/B Testing
  • Compared to the Strangler Fig Pattern

Resources

Use the promo codes MICRO101 & CONFLUENTDEV1 to get $25 of free Confluent Cloud usage and skip credit card entry.

Be the first to get updates and new content

We will only share developer content and updates, including notifications when new content is added. We will never send you sales emails. 🙂 By subscribing, you understand we will process your personal information in accordance with our Privacy Statement.

Branch by Abstraction

Hi, I'm Wade from Confluent.

The Branch-by-abstraction pattern is useful when decomposing a monolith into a series of microservices.

Let's talk about how it works, and how it can be applied.

In a traditional architecture, when we replace an existing piece of functionality, one way to do it is by creating a new source-control branch.

We re-implement the functionality in the new branch, testing it as we go.

When we're ready, we can merge our changes to the main branch and deploy them to production.

This replaces the old implementation with the new one.

However, this approach has a few problems.

What if our new implementation takes a long time to build?

The code in the main branch doesn't stop evolving.

It begins to diverge or drift from the working branch.

When we are ready to merge, we might find a messy process because of the drift.

If we deploy to production, we might discover that there are problems we didn't account for.

This could be due to:

Unexpected load

A bug we didn't catch

Or maybe even code drift that occurred while we were working.

Regardless, now we have to roll back the changes and fix any issues before re-deploying.

While we work on the fixes, the code continues to drift, possibly introducing new issues.

So how do we know when we are done?

How can we guarantee that our new code is producing the same results as the old code?

How can we be sure it won't create production issues?

In an ideal world, we'd have a suite of tests to verify it works.

However, those tests are rarely as comprehensive as we need.

What if, instead of using a source-control branch, we create a branching path through our code using an abstraction layer?

Imagine an eCommerce system broken down into:

Orders

Shipments

and Customers

These all live in the same monolith.

And all three rely on another component for notifications.

How could we extract those notifications to its own microservice?

We start by inserting an adapter in front of the code we want to replace.

In this case, the notifications.

Any calls to our old code are routed through the adapter.

We then build a new implementation alongside the old one.

If we are decomposing a monolith, then the new implementation can be a proxy that makes calls to our notifications microservice.

We continue to serve requests using the old implementation until we are ready to cut over.

Once our microservice is finished, we can swap the adapter to point to the proxy.

Then we are ready to start cleaning up the old code and potentially removing the adapter.

This technique is what we call Branch by Abstraction.

It allows us to incrementally make changes and continually merge them to the main source-control branch, without risk.

It's part of a larger collection of techniques known as trunk-based development.

This reduces the risk of having messy merges that can occur when working on long-lived branches.

It also enables a powerful way of testing and verifying the code is working.

Unlike with a source-control branch, the abstraction allows us to have both versions of the code live in production at the same time.

This means we can make calls to either the new code, the old code, or both.

We can use that to perform A/B testing.

For example, we could send most of the traffic to the old system, but route some of it to the new system.

This allows us to subject only a subset of our users to the new system.

Alternatively, we can make calls to both systems and compare the results.

We can then log any differences and monitor them over time.

This lets us see how close we are to completion.

During the testing phase, we always return the results from the old system.

But, once our accuracy has crossed a specific threshold, we swap the implementation to return the results from the new system.

This is a low-risk way of live testing the system so when we are ready to cut over, we hopefully have eliminated any issues.

Branch by Abstraction has a lot of similarity to the Strangler Fig pattern, which is also used for decomposing monoliths.

Both involve inserting an abstraction layer and swapping between old and new implementations.

Just be aware that creating the abstraction layer can be challenging in a complex system.

The primary difference between the two is that the Strangler Fig pattern is designed to be used at the edge of the monolith, wrapping around the outside of it.

The Branch-by-abstraction pattern can be used deeper inside the monolith at any point where you might want to send requests to an external microservice.

However, both approaches allow us to replace the functionality of our monolith while our system is in production.

To use an old metaphor, they both allow us to replace the wings of the plane while it's in flight.

Have you used branch by abstraction in your production systems?

What did you think of it?

Are there other techniques you prefer?

I'd love to hear about your experience, so let me know in the comments.

Meanwhile, if you aren't already on Confluent Developer, use the link in the video description below to find more content on event-driven microservices.

Don't forget to like, share, and subscribe.

And, thanks for watching.