Skip to content
← Back to trunk-based development guide Branch lifetime patterns

Short-lived branches in practice

Trunk-based development asks for branches under one day. Real features take longer than that. The patterns below are how engineering teams resolve the contradiction without faking the metric.

In one paragraph

Branch lifetime is the variable. Feature size is the constraint. The skill is decoupling them. The four patterns most teams use are stacked PRs (chain dependent branches), branch by abstraction (refactor through an interface), feature flags (ship dark), and splitting by execution path (scaffolding then primary path then edges). One feature, many branches, none of them long-lived.

Why one day, not one week

The DORA research that backs trunk-based development as a high-performance practice draws the line at 24 hours. Below that, integration cost is essentially zero. Above it, the cost grows non-linearly with branch lifetime. By the time a branch hits a week, the cost of integrating it competes with the cost of writing the feature itself.

The 24-hour target is not arbitrary. It tracks the cycle most teams already use: morning planning, midday work, end-of-day review and merge. A branch that closes inside that cycle stays close enough to main for integration to be a non-event. A branch that survives overnight starts to drift.

Pattern 1: Stacked PRs

flowchart LR
  M["main"]
  PR1["PR #1<br/>scaffolding"]
  PR2["PR #2<br/>primary path"]
  PR3["PR #3<br/>edge cases"]

  M --> PR1 --> PR2 --> PR3
  PR1 -.->|merges first| M
  PR2 -.->|merges next| M
  PR3 -.->|merges last| M

  style M fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style PR1 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style PR2 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style PR3 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24

A multi-day feature shipped as three small PRs in a stack. Each one is reviewable in fifteen minutes. The bottom merges first, the rest cascade.

A stacked PR (or stacked diff) is a chain of dependent branches where PR #2 builds on PR #1's branch instead of main. You break a multi-day change into small reviewable units and open all of them at once, without waiting for the bottom one to merge before opening the next.

When PR #1 lands, PR #2 rebases onto main and is ready to merge. The cycle continues until the stack is empty. Each individual branch lives for hours. The whole feature ships in days.

Stacked PRs are how Meta, Google, and most teams that took inspiration from them actually work. On the tooling side, Mergify Stack is what most of our customers use to manage chains of dependent PRs without leaving GitHub.

Pattern 2: Branch by abstraction

flowchart TB
  S1["Step 1<br/>Add interface<br/>(small PR)"]
  S2["Step 2<br/>New impl behind interface<br/>(small PR)"]
  S3["Step 3<br/>Switch callers via flag<br/>(small PR)"]
  S4["Step 4<br/>Delete old impl<br/>(small PR)"]

  S1 --> S2 --> S3 --> S4

  style S1 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style S2 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style S3 fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style S4 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24

A risky refactor delivered as four sequential PRs, each shippable in a day. The old and new implementations coexist behind an interface until the switch is complete.

Branch by abstraction is the answer for refactors and migrations where a feature flag does not fit. You cannot put a flag on "the new database client". You can put an interface in front of it.

The pattern: introduce an interface that wraps the existing implementation (one PR), add a new implementation behind the same interface (another PR), switch callers over (one PR, often gated by a runtime flag for safe rollout), then delete the old implementation (one final PR). Four PRs, none of them long-lived, none of them require a multi-week branch.

The pattern was named by Paul Hammant and is one of the more reliable techniques for moving large pieces of a codebase without freezing development around them.

Pattern 3: Ship behind a feature flag

For new features (as opposed to refactors of existing code), feature flags do most of the work. The code lands in main behind an off-by-default flag. The branch closes the moment the slice of work is reviewable, even if the feature is months from being user-facing.

Feature flags and trunk-based development covers flag types, flag debt, and how to retire them before they become permanent config.

Pattern 4: Split by execution path

Most multi-day features have natural seams. The split into "scaffolding, primary path, edge cases" works for most of them.

  • Scaffolding. The shape of the code: types, interfaces, file layout, empty function bodies. Lands in a few hours, ships behind a flag, prepares the ground.
  • Primary path. The happy-path implementation. Tests for the common case. Half a day to a day of work, ships once review is in.
  • Edge cases. Error handling, retries, validation, the cases that came up during testing. Each one its own small PR if needed.

Two engineers can work on a feature in parallel this way: one on scaffolding, one on the primary path once the interfaces are stable. By the time the edge cases land, the feature has been in main for a week behind a flag and is ready to turn on for users.

What does not work

Two anti-patterns show up in teams that try to adopt short-lived branches without the supporting infrastructure.

  • Force-merging incomplete work. Merging a half-finished feature into main "to keep the branch short," with no flag and no abstraction in front of it. The feature breaks in production. The team blames trunk-based development.
  • "WIP" branches that secretly stay open for weeks. The team measures branch lifetime by when the PR is opened, not when the work started. Engineers do the work locally, then open a PR at the end. The metric looks great, the underlying problem is identical to long-lived branches.

Both fail because they treat short-lived branches as the goal rather than as the side effect of a working setup. The actual goal is to keep main shippable while shipping continuously. Branch lifetime is the visible metric. The infrastructure underneath is what makes that metric honest.

Short branches mean more merges per day.

Multiple PRs landing on main in a single day is what a merge queue is built for. Mergify keeps main green when the whole team is shipping.