Articles

Trunk-Based Development vs Git Flow

Trunk-based development merges small changes into main constantly; Git Flow routes work through long-lived branches. How each affects releases and CI.

Takina Takina · · 4 min read
Git version control branching visualized on a screen

Trunk-based development and Git Flow are two branching models that answer the same question differently: how should a team organize commits, branches, and merges on the way to a release? Trunk-based development keeps everyone merging small changes into a single shared branch continuously; Git Flow routes work through a structured set of long-lived branches — develop, feature, release, and hotfix — before anything reaches production.

Git Flow: a branch for every purpose

Git Flow, popularized in the late 2000s, defines a specific role for each branch:

  • main — always reflects what’s in production.
  • develop — the integration branch where finished features accumulate between releases.
  • feature/* branches — created off develop for each new piece of work, merged back into develop when done.
  • release/* branches — cut from develop when preparing a release, used for final stabilization and version bumps, then merged into both main and develop.
  • hotfix/* branches — cut directly from main to patch production urgently, merged back into both main and develop.

This structure gives a team clear checkpoints: develop is where integration happens, release branches are where a version gets locked down and tested, and main never sees anything that hasn’t passed through that pipeline. It’s well suited to software with scheduled releases, multiple versions supported in parallel, or a formal QA sign-off stage between “feature complete” and “shipped.”

Trunk-based development: one branch, constant integration

Trunk-based development inverts the emphasis. There’s a single long-lived branch — usually still called main or trunk — and developers merge into it directly, or via very short-lived feature branches (often open for less than a day). The core discipline is that main is always kept in a releasable state, which means:

  • Changes are broken into small, frequent commits rather than large, batched features.
  • Incomplete work ships behind a feature flag rather than living on a long-lived branch, so main never has to wait for a feature to be “done” before absorbing it.
  • CI runs on every merge to main, and a red build is treated as a stop-the-line event, not something to fix “eventually” on a separate branch.
  • Releases are cut from main at any point, often via tags, rather than being staged on a dedicated release branch.

This model trades the structure of dedicated integration and release branches for a tighter feedback loop: merge conflicts are smaller and more frequent instead of large and rare, and integration problems surface within hours instead of at the end of a sprint.

Comparing the two

Git FlowTrunk-based development
Long-lived branchesmain, develop, plus release/hotfix branchesJust main/trunk
Feature branch lifespanDays to weeksHours to a day, or none
Incomplete workStays on its branch until doneMerged behind a feature flag
Release mechanismDedicated release/* branchTag or cut directly from main
Merge conflict sizeLarger, less frequentSmaller, more frequent
Best fitScheduled releases, multiple supported versionsContinuous deployment, single production version

Why trunk-based development pairs with CI/CD

Trunk-based development and continuous deployment reinforce each other. If every merge to main has to be release-ready, you need automated tests catching regressions immediately — there’s no release-branch stabilization period to catch problems later. And if main is always deployable, there’s little reason to delay deploying it, which is exactly the premise of continuous deployment. Teams practicing full continuous deployment almost always use some form of trunk-based development, because a Git Flow-style pipeline with dedicated release branches assumes there’s a batching and staging step that continuous deployment is specifically designed to eliminate.

The reverse pairing holds too: Git Flow tends to show up alongside more traditional CI/CD pipelines where releases are versioned, scheduled, and sometimes supported in parallel — think desktop software, libraries with semantic-versioned releases, or systems where a customer might stay on an older major version for a while.

Feature flags as the load-bearing piece

The part of trunk-based development that trips teams up most often isn’t the branching — it’s what replaces the isolation a feature branch used to provide. Without a long-lived branch to hide unfinished work, teams need feature flags to merge incomplete code into main without exposing it to users. This shifts complexity from Git history into application code: instead of a merge conflict resolving whether a feature exists, a runtime flag decides whether it’s active. That’s a reasonable trade for faster integration, but it does mean flag cleanup has to become a real habit — stale, forgotten flags accumulate as technical debt in exactly the way stale feature branches used to.

Which one fits your team

Neither model is universally correct. Git Flow’s structure earns its keep when a project needs to support multiple released versions simultaneously, when releases go through a distinct QA or compliance gate, or when contributors work asynchronously across time zones without shared CI infrastructure. Trunk-based development earns its keep when the team ships continuously to one production environment, has strong automated test coverage, and would rather resolve small conflicts constantly than large ones occasionally. Many teams also land somewhere in between — a simplified Git Flow with just main and short feature branches, skipping develop and release branches entirely, borrowing trunk-based development’s cadence without going all the way to flag-gated continuous deployment.

The takeaway

Git Flow organizes work through dedicated branches for integration, release, and hotfixes, giving teams clear checkpoints at the cost of larger, less frequent merges. Trunk-based development collapses that structure into a single branch kept perpetually releasable, trading branch isolation for feature flags and constant CI feedback. The right choice tracks how a team actually ships: scheduled, versioned releases favor Git Flow’s structure, while continuous deployment to one production environment favors trunk-based development’s tighter loop.

The Lycoris Team The Lycoris Team · · 4 min read

Git Hooks Explained: pre-commit, pre-push, and More

Git hooks are scripts that run automatically at points in the Git workflow, like before a commit or push. How they work and when to use them.

#Git #Developer Tools #Workflow
The Lycoris Team The Lycoris Team · · 5 min read

Git Rebase vs. Merge: A Practical Guide

When should you rebase and when should you merge? A clear, example-driven breakdown of the trade-offs, plus a simple workflow you can adopt today.

#Git #Version Control #Workflow