Skip to main content

Branch by Abstraction and Parallel Change

What This Concept Is

Two techniques for making a large structural change while the system keeps shipping.

Parallel Change (also called expand-and-contract): a pattern for changing a widely used interface in three phases:

  1. Expand: add the new form alongside the old. Both work.
  2. Migrate: move callers from old to new, one batch at a time.
  3. Contract: remove the old form once no caller uses it.

Branch by Abstraction: a pattern for replacing an entire dependency (library, framework, subsystem) without a long-lived version-control branch.

  1. Insert an abstraction layer between client code and the current supplier.
  2. Migrate all clients to call through the abstraction.
  3. Build a new supplier behind the same abstraction.
  4. Switch clients (a section at a time) from old supplier to new.
  5. Remove the old supplier. Remove the abstraction if it no longer pays.

Both techniques keep trunk releasable at every step.

Why It Matters Here

Small-step refactoring (Clusters 1-4) assumes the change fits in a commit or two. Some changes do not: replacing an ORM, migrating a pricing engine, splitting a monolithic module. If you do those on a long-lived branch, you pay for weeks of merge conflicts, and you cannot ship any other feature cleanly during the wait.

Parallel Change is Change Function Declaration at scale. Branch by Abstraction is Move Function at scale. The discipline is the same: every commit keeps the system working.

Concrete Example

Parallel Change. Switching from (x, y) to Coordinate on a Grid class:

// Phase 1: expand
class Grid {
private Cell[][] cells;
private Map<Coordinate, Cell> newCells = new HashMap<>();

public void addCell(int x, int y, Cell c) { cells[x][y] = c; }
public void addCell(Coordinate coord, Cell c) { newCells.put(coord, c); }
public Cell fetchCell(int x, int y) { return cells[x][y]; }
public Cell fetchCell(Coordinate coord) { return newCells.get(coord); }
}

// Phase 2: migrate callers, one file or one team at a time.
// Phase 3: contract -- delete the (int,int) overloads and the cells[][] array.

Branch by Abstraction. Replacing a hand-rolled repository with a library:

Step 1: Introduce interface OrderRepo { save(o); find(id); }
Step 2: Existing code implements OrderRepo as LegacyOrderRepo.
Step 3: All call sites use OrderRepo, not LegacyOrderRepo.
Step 4: Build NewOrderRepo using the library.
Step 5: Feature-flag: some call sites use NewOrderRepo; compare outputs.
Step 6: Flip all callers to NewOrderRepo.
Step 7: Delete LegacyOrderRepo.

At no point is the main branch broken. At no point is the feature flag permanent.

Common Confusion / Misconception

Skipping the contract phase. Teams add the new form, migrate "most" callers, and never delete the old. A year later you have both -- twice the surface, half the clarity. Book the contract work as a ticket when you start the expand.

Parallel Change ≠ deprecation notice. A deprecation message without a migration timeline is an indefinite postponement. Parallel Change assumes someone actually migrates callers.

Branch by Abstraction does not mean version-control branches. The opposite: the whole point is to avoid a long-lived feature branch. The "branch" is the in-code abstraction.

How To Use It

Rules:

  • Every commit ships. If a commit leaves the system broken, step is too big.
  • Feature flags may decide which implementation runs but must be removed with the contract step.
  • Keep the migration phase on the team's dashboard. If it stalls past a sprint, that is a real risk.

Check Yourself

  1. Why is the contract phase the one most often skipped, and what is the long-term cost?
  2. When is Branch by Abstraction a better fit than Parallel Change, and vice versa?
  3. A team says "we're on a six-week refactor branch." Which of these two techniques are they not using, and why would adopting it help?

Mini Drill or Application

Pick a dependency in your system (a library, a helper module, an internal service) you suspect you might replace in the future. Sketch the Branch-by-Abstraction plan: what is the abstraction interface, which clients migrate in which order, where is the feature flag, when is the contract phase?

Video and Lecture References

Article References

External Exercises

  • In your repo, find a public function whose signature you would like to change. Sketch the Parallel Change in three commits (expand, migrate, contract). Actually do the first commit.
  • Industrial Logic refactoring album - worked Parallel Change examples

Depth Path


Source Backbone

Refactoring is the canonical book backbone for this module. Use these sources after attempting the refactor and tests yourself.