Git Branching Strategy for Shared Code Across Multiple Features

When you are building multiple features at the same time, there is one problem that shows up quickly: several branches need the same in-progress code.

If you do not account for this up front, you end up copying commits around, resolving the same conflicts repeatedly, and wondering why pull requests are impossible to review.

I use two branching patterns depending on the type of work. Both are valid. The key is choosing the one that matches the dependency structure of your features.

The Problem

Here is a common scenario where a branching strategy helps.

You start one feature branch and start adding code. A day later, you start two more features that also need that same code. One of those new features may also depend on the other.

Now your branches are connected, but not all in the same way. If you treat them as completely independent, you will repeatedly solve the same merge conflicts and pull requests become hard to review.

Strategy 1: Daisy-Chained Branches

Use this approach when each feature builds directly on the previous one.

Example:

git switch -c feature/a main
# work, commit

git switch -c feature/b feature/a
# work, commit

git switch -c feature/c feature/b
# work, commit

In this model, each branch has a clear parent:

  • feature/a is the base
  • feature/b depends on feature/a
  • feature/c depends on feature/b

This is ideal when features are tightly layered and cannot be completed independently.

How to Maintain a Daisy Chain

Maintenance is the part people underestimate.

If feature/a changes, those changes must be propagated up the chain:

git switch feature/b
git merge feature/a

git switch feature/c
git merge feature/b

Daisy Chain Best Practices

  • Keep each branch focused and small enough to review.
  • Merge parent updates into child branches frequently, not once at the end.
  • Open pull requests early, even as drafts, so review can begin before the entire chain is done.
  • Document branch order in each pull request description, for example: "depends on feature/a".
  • Avoid stacking too deeply. A chain of 2 to 4 branches is manageable. Beyond that, consider splitting work differently.

Strategy 2: Shared Base with Sibling Branches

Use this approach when multiple efforts need the same foundation, but are otherwise independent.

Example:

git switch -c feature/base main
# shared refactor or shared primitives

git switch -c feature/ui feature/base
git switch -c feature/api feature/base
git switch -c feature/jobs feature/base

In this model:

  • feature/base contains common code all siblings need
  • feature/ui, feature/api, and feature/jobs can evolve separately

This is useful when one large initiative touches multiple areas of the codebase and you want to keep concerns separated while reusing the same base.

How to Maintain a Shared Base Model

The base branch is the critical dependency. Treat it like a product.

When feature/base changes, merge it into each sibling branch:

git switch feature/ui
git merge feature/base

git switch feature/api
git merge feature/base

git switch feature/jobs
git merge feature/base

Do this regularly. If you wait too long, each sibling diverges and conflict resolution gets expensive.

After feature/base is merged to main, each sibling should be merged onto updated main to drop duplicate history and keep pull requests clean.

Shared Base Best Practices

  • Keep feature/base narrowly scoped to truly shared code.
  • Do not sneak sibling-specific logic into feature/base.
  • Protect feature/base with CI and code review just like main.
  • Re-sync siblings from feature/base on a routine cadence (daily is often right during active development).
  • Use consistent branch naming to make relationships obvious.

Merge Order and Promotion Rules

No matter which strategy you choose, define the merge order before coding.

For daisy chains:

  1. Merge lowest parent first.
  2. Merge each child only after its parent lands.
  3. Re-target pull requests as parent branches merge.

For shared base plus siblings:

  1. Merge feature/base first.
  2. Merge each sibling onto the updated target branch.
  3. Merge siblings independently once they are clean.

This avoids accidental regressions and keeps review diffs understandable.

Which Strategy Should You Choose?

Use daisy-chained branches when each feature directly depends on the one below it.

Use shared base with sibling branches when multiple workstreams need common foundational code but can progress independently.

If you choose correctly, your branch strategy reduces risk and makes reviews easier. If you choose poorly, Git becomes the work instead of the tool.

A little upfront branch design saves a lot of cleanup later.