Umbrella PRs as a Coordination Tool

Some features don't want to be broken into small PRs. Their phases have real interdependencies. Their intermediate states aren't shippable. Their review overhead as a single monolithic diff is enormous. And feature flags, which are usually the answer for gating half-built work, don't always fit the situation.

The umbrella PR pattern is what fills that gap. It's a way to handle a large feature that resists decomposition, organized into phase-sized PRs that each get real review, coordinated on a long-lived branch that stays out of main until the whole thing is done.

The pattern isn't new in spirit. Long-lived feature branches predate git. What's new is that the specific mechanics (the anchor commit, the rebase rhythm, the review structure) cohere into something reusable across teams and projects. Here's what the pattern is, when to reach for it, and what to watch out for at different team scales.

What an umbrella PR is

An umbrella PR is a long-lived branch that hosts a whole feature across multiple phase PRs. Each phase branches off the umbrella. Each phase PR merges back into the umbrella. When the feature is fully complete, the umbrella squash-merges to main as a single commit.

Visually:

main:                    A ─────────────────────────── B ─── (umbrella squashed in)
                              │                        │
umbrella feature branch:      └── U0 ── U1 ── U2 ── U3 ┘
                                       │    │    │
phase branches:                        P1   P2   P3

The umbrella itself is a durable coordination surface. It has a description. It has a checklist. Reviewers can watch it grow phase by phase. The main branch stays clean; it never sees the intermediate half-done states. But the umbrella and the phase PRs preserve the full history of the work.

The review advantage

The single biggest benefit of this pattern is how it distributes review effort. Deep, hard-hitting review happens on the small child branches. The umbrella itself gets a cursory review at merge time.

Each phase PR is reviewed at its natural size, a few hundred lines, focused on a specific layer. Reviewers have the mental capacity to look at that code carefully, catch smells, argue about names, request restructuring. The child PRs are where the real code review happens.

When the umbrella squash-merges to main, the reviewer is checking a different question. Not "is this code well-shaped", that's already been answered on the child PRs. The umbrella review asks "did we accumulate what we intended to accumulate?" Look at the checklist. Confirm every phase landed. Scan for anything that got added directly to the umbrella outside the phase PRs (this shouldn't happen but the review catches it if it did). Check the final integration works. Merge.

This is a review pattern many teams already use for large refactors: hard-hitting review on each piece as it lands, cursory review on the whole once it's all there. The umbrella pattern makes that formal in the git workflow. Instead of relying on convention, you get a structure that enforces it.

The related benefit is that the umbrella PR itself never has to be reviewed as a single 3000-line diff. If your team has ever tried to review a large feature PR cold, you know why this matters. Reviewers glaze over past a certain size. Comments get shallower. Real problems get missed. Splitting the review across phase PRs means every part of the code got fresh eyes at a size the reviewer could actually hold.

The anchor commit trick

GitHub won't let you open a PR with zero commits between two branches. So the first thing you do when starting an umbrella is add an anchor commit. An empty commit is fine:

git checkout -b feature/user-dashboard-umbrella
git commit --allow-empty -m "Chore: open user-dashboard umbrella branch"
git push -u origin feature/user-dashboard-umbrella

Now you can open the umbrella PR against main, even with no real diff yet. Phase branches can start landing on it immediately. This is a tiny mechanical detail that trips people up when they first try the pattern, so worth calling out.

An alternative to the empty commit is a one-line documentation commit. Add a stub docs/user-dashboard.md explaining what the umbrella is going to build. This has the benefit of making the umbrella's purpose visible in the diff from day one, and the documentation grows alongside the code. Either approach works. I've come to prefer the doc-stub version because the umbrella PR then has real content from the start, which makes reviewers less confused when they open it.

Phase design as a discipline

The interesting constraint the umbrella pattern imposes: every phase has to ship something runnable on its own. Not "runnable in production" (that's what the umbrella is gating against), but "runnable and testable in isolation." Phase 1 might be schema groundwork with no UI. Phase 2 might be a background pipeline that runs from the console. Phase 3 might be the first UI surface built on top of phases 1 and 2.

This forces you to think about what "runnable" means at each layer. A schema phase that isn't runnable is a schema phase that lacks factories. A pipeline phase that isn't runnable lacks a console entry point. The discipline of phasing surfaces the layering questions early, and the phases naturally partition into things a reviewer can hold in their head.

The point isn't project management overhead. It's that phasing is a design constraint that improves the work.

A typical six-phase decomposition for a substantial feature looks something like:

  1. Schema groundwork. Models, migrations, factories, no external calls, no UI. Reviewable as "does the data shape make sense."
  2. Core pipeline. The main business logic, runnable from the console. Reviewable as "does the transformation work."
  3. First UI surface. The primary user-facing screen. Reviewable as "does the interaction model make sense."
  4. Persistence and lifecycle. History, revert, versioning if applicable. Reviewable as "does the object have the right lifecycle."
  5. Configuration and controls. Options, toggles, per-user preferences. Reviewable as "does the feature adapt to the range of users it needs to serve."
  6. Polish and observability. Metrics, logs, error handling, UX refinement. Reviewable as "is this production-ready."

The last phase is often where design-principle sweeps live, or where the feature gets its final QA pass. If you find yourself adding a feature-important requirement in phase 6 that should have been in phase 3, that's a signal to re-examine the phase boundaries next time.

The rebase rhythm and small-conflict chunks

The mechanics:

  1. Phase PR merges into umbrella.
  2. Fetch main. If main moved, rebase umbrella onto main.
  3. Push umbrella with --force-with-lease.
  4. Open the next phase branch off the rebased umbrella.

For a solo developer running the pattern:

# Something landed on main from a dependency-update PR while I was working on the umbrella.
git checkout main
git pull
git checkout feature/user-dashboard-umbrella
git rebase main
git push --force-with-lease
# Now in-flight phase branches need to rebase onto the new umbrella.
git checkout feature/user-dashboard-phase-3
git rebase feature/user-dashboard-umbrella
git push --force-with-lease

The --force-with-lease flag is important. It's the safety net that prevents you from clobbering somebody else's push. If someone else did push, --force-with-lease fails and you have to investigate before overwriting.

Aggressive rebasing on the small phase branches is actually a strength of the pattern. When main moves, you rebase the umbrella onto it, then rebase each in-flight phase branch onto the umbrella. If there are conflicts, each conflict is small, because each phase branch is small. You're never resolving a merge conflict against a 3000-line umbrella. You're resolving a small handful of conflicts against a phase that's only a few hundred lines.

Compare that to the alternative: a single monolithic feature branch that gets rebased onto main every so often. When main moves, the whole feature branch has to be reconciled at once. Small conflicts have to be sorted out amid the volume of the whole feature. It's mentally exhausting and error-prone.

With the umbrella pattern, the mental cost of handling a merge conflict is proportional to the size of the phase branch, not the size of the whole feature. That's a real ergonomic win. It's what makes the pattern viable at all.

When to use it

The umbrella pattern lives as a quiet alternative in each of the common workflow worlds. It fits differently in each.

Trunk-based development. Trunk-based assumes every commit can go to main. Most work should. But some features are too big or too coupled for that, a schema migration that needs to happen before the pipeline that uses it can ship; a UI that depends on a background job that can't be flag-gated at runtime; a multi-model change where phase 3 requires phase 1's data. The umbrella pattern is the exception you reach for on those specific features. Everything else keeps flowing to main as normal.

Feature-flag world. Flags gate at runtime rather than at merge time. When flags fit, when a half-built feature can safely be in production behind a flag and toggled per-user, you should use flags. Flags let you get production evidence during the build. But some intermediate states aren't safe behind a flag: an API contract change, a background job that can't be selectively invoked, a migration that has to complete before the flag can be flipped. When flags feel painful for a specific feature, the umbrella pattern is a quiet alternative for that feature.

Gitflow world. Gitflow already has long-lived feature branches. The umbrella pattern adds structure inside them: instead of a monolithic feature branch that grows through direct commits, the feature branch becomes an umbrella hosting phase PRs. The mechanics fit naturally into gitflow's model, and the review advantage carries over. If your team is on gitflow already and finds feature branches getting hard to review, this is the tweak.

The through-line: the umbrella pattern is the exception, not the default. Whatever your team's default workflow is, use it. Reach for the umbrella pattern when the default workflow is fighting you on a specific feature.

Handling merge conflicts during a phase

The umbrella pattern has a specific failure mode: what happens when phase 3 has a merge conflict with the umbrella because phase 2 changed something phase 3 also touches?

The correct workflow:

  1. Phase 3 branches off the current umbrella (which now includes phase 2's changes).
  2. If phase 3 is already open and phase 2 landed on the umbrella while phase 3 was in flight, rebase phase 3 onto the new umbrella.
  3. Resolve the conflicts. Small chunks, small effort. Handle them semantically, don't just accept "theirs" or "ours" without thinking about which behavior you want.
  4. Re-run the tests. Push with --force-with-lease.

The pathological failure mode is phase 3 has been open for a long time, phase 2 landed a big refactor that touches the same files, and now phase 3's rebase is a substantial merge. If this happens, the umbrella pattern isn't the problem, the phase sizing is. If phase 2 and phase 3 are that entangled, they should have been merged as a single phase, or phase 3 should have branched off phase 2 directly instead of off the umbrella.

The rule I use: a phase branch that's been open for more than three or four days should either be merged or restarted. Long-lived phase branches accumulate the same problems as long-lived main branches. The umbrella is what stays long-lived. The phase branches are what stays fresh.

Team scale: what I've seen work

Team size affects the pattern significantly. I want to be specific about what I've watched work and where things start to strain.

Solo. The pattern's cheapest. No coordination overhead. You control the umbrella, the phases, the rebase schedule. Merge conflicts only come from what lands on main from other sources (dependency updates, etc.), and those are usually small and localized.

Two engineers on the same umbrella. This works. I've done it recently on a shared codebase. The main additions are (a) rebase coordination, when I rebase the umbrella and force-push, my collaborator has to fetch and rebase their in-flight phase branch, so we tell each other before pushing; and (b) phase ownership, phases with independent surface area can go in parallel, but if phase 4 depends on phase 3's shape and phase 3 is still in flight, phase 4 has to wait. Serialize at the phase level; parallelize at the file level within a phase.

Small teams (three to six). Extrapolating from the two-person experience, this probably works with slightly more coordination discipline. Every umbrella rebase needs a broadcast so everyone with in-flight phase branches can fetch and rebase in a coordinated order. Phase ownership becomes more important, if two people are both working on phases that touch the same models, one of them will end up rebasing on top of the other's phase, and the conflict resolution work goes up. I'd try this without hesitation on a three-person team; on a six-person team I'd want to be sure everyone's disciplined about the rebase protocol.

Larger teams (roughly ten to twenty). I've used the pattern on a team of twenty engineers. It worked, but rebases were hard. When main is moving fast because ten other engineers are landing PRs to it, the umbrella has to rebase constantly to stay current. Each rebase pulls new changes into the umbrella that in-flight phase branches then have to rebase on top of. The coordination cost compounds. It's doable, but it's real work. The specific failure mode was engineers getting frustrated when they'd rebase their phase branch three times in one week because the umbrella kept moving. My honest read: the pattern's ergonomics degrade as team size grows, and somewhere between ten and twenty engineers the ergonomics get tight enough that you should think about alternatives, larger phases, more feature-flagging, or breaking the feature into smaller umbrellas run independently.

Beyond twenty. I haven't tried this at that scale. My intuition is it starts to work against you more than for you. The bigger the team, the more main moves, the more rebases, the more compounded coordination cost. Somewhere past twenty engineers, feature flags become the more scalable answer, because runtime gating decouples the work from the branch structure.

A note on AI-assisted work

AI happens to make the mechanics of this pattern cheaper. Rebases are faster because the AI can resolve straightforward conflicts, run the test suite, verify the working tree is clean. Reviewing a phase PR is faster because the AI can summarize what changed relative to the umbrella. Context reload between phases is faster because the AI carries the context forward.

But that's an incidental convenience, not the reason to use the pattern. The pattern is fundamentally about handling a large feature with a review structure that works. The AI just makes some of the git work less annoying. If you're not using AI, the pattern still works. If you are, you'll find the rebase rhythm collapses to something you barely notice.

The failure mode to watch for

The one specific pathological failure mode is: the umbrella becomes unmergeable to main.

This happens when main has evolved in ways the umbrella hasn't tracked. Someone landed a refactor on main that changes the shape of a model your umbrella depends on. The umbrella's tests all pass on the umbrella. Main's tests all pass on main. But if you tried to squash-merge the umbrella to main, the merge would create broken code that both the umbrella and main were quietly tolerating in isolation.

The prevention: rebase the umbrella onto main aggressively. Every time main moves, or at minimum once a week, pull main into the umbrella. Don't let the umbrella drift.

The recovery, if you find yourself in this state: abandon the umbrella and restart. Cherry-pick the phase commits onto a fresh umbrella that's freshly rebased off current main. This is painful. It's also always cheaper than trying to merge a two-month-old umbrella that has diverged fundamentally.

The rule: the umbrella branch is a live document. Treat it that way. If you neglect it, it dies.

When it isn't the right answer

If every phase could go to main independently, do that instead. Trunk-based development is simpler and works fine for work that decomposes into shippable slices.

If the intermediate states could safely ship behind a feature flag, use flags. They give you production evidence during the build.

If the feature is small enough to fit in one PR without becoming unreviewable, just do one PR. The umbrella pattern has overhead. Don't add overhead you don't need.

If your team is large enough that the coordination cost of rebases is going to overwhelm the review benefit, reach for other patterns, bigger phases, more flags, or splitting the feature into independent smaller umbrellas.

The bigger point

The umbrella pattern is one tool for one specific problem: handling a large feature that resists decomposition. Its main benefit is the review structure, deep review on small child branches, cursory review on the umbrella at merge time. Its main cost is the coordination overhead of rebases, which grows with team size.

It's a quiet alternative in whatever workflow you're already using. If your defaults are trunk-based, use trunk-based; reach for umbrella on the specific features that resist it. If you're on gitflow, use gitflow; the umbrella adds review structure to the feature branches that need it. If you're heavy on feature flags, use flags; the umbrella is what you reach for when flags don't fit a specific case.

The pattern isn't a philosophy. It's a specific tool for a specific problem. Reach for it when the problem shows up. Don't over-use it, and don't build a whole workflow around it. It's the exception, not the rule.


If you're leading engineering work where you're figuring out what the new normal looks like for large features and coordinated review, that's a conversation Rock Agile is deep in every day. If it'd help to compare notes, get in touch.

Next
Next

The Articulation Workflow