Skip to content
← Back to trunk-based development guide Branching model comparison

Trunk-based development vs Gitflow

Gitflow was the canonical model for a decade. Trunk-based development is what most modern teams use instead. The difference is not philosophical, it is operational, and it tracks one variable: how often you ship.

In one paragraph

Gitflow uses long-lived develop, release, and feature branches and a scheduled release cadence. Trunk-based development uses one shared branch and continuous integration. Gitflow was designed for software shipped on a calendar (boxed software, mobile apps with store review). Trunk-based development is designed for software shipped continuously, which is most modern SaaS.

What Gitflow looks like

flowchart LR
  M["main"]
  D["develop"]
  R["release/1.4"]
  H["hotfix/auth"]
  F1["feature/billing"]
  F2["feature/dashboard"]

  D --> F1 --> D
  D --> F2 --> D
  D --> R --> M
  M --> H --> M
  R --> D

  style M fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style D fill:#F2F4F7,stroke:#43A7E5,color:#1A1D24
  style R fill:#FFF4E5,stroke:#F27B2A,color:#1A1D24
  style H fill:#FDECEA,stroke:#E53935,color:#1A1D24

Gitflow's branch graph: develop is the integration branch, release branches stabilize for production, hotfixes land on main, features merge into develop.

Vincent Driessen wrote the canonical Gitflow post in 2010. It described the workflow most teams converged on for shipping desktop and server software: a develop branch where work integrated, release branches that stabilized before going to production, hotfix branches that bypassed the cycle when something was on fire.

Gitflow assumes a release event. There is a moment where the team says "we are cutting 1.4 now," opens a release branch, freezes new features, and runs through a stabilization phase before tagging and shipping. The branch model is built around that moment.

What trunk-based development looks like

flowchart LR
  M0["main"]
  M1["main"]
  M2["main"]
  M3["main"]
  PR1["PR #1"]
  PR2["PR #2"]
  PR3["PR #3"]

  M0 --> PR1 --> M1
  M1 --> PR2 --> M2
  M2 --> PR3 --> M3

  style M0 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style M1 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style M2 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
  style M3 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24

One main branch. Short-lived PRs land throughout the day. Releases come from main itself or from a tag, not from a separate long-lived branch.

There is no develop branch, no release branch, no hotfix branch. Engineers branch off main for hours at a time, open a PR, merge back. Releases are tags on main (or whatever main was at the moment of the release). Hotfixes are PRs against main that ship through the same path as anything else.

The whole model assumes main is shippable at all times. That assumption is what makes the rest of the simplification possible. It is also what most teams underestimate when they migrate.

Side by side

Aspect Gitflow Trunk-based development
Long-lived branchesmain, develop, release/*main only
Feature branch lifetimeDays to weeksHours to one day
Integration cadenceAt end of featureMultiple times per day
Release cadenceScheduled (weekly/monthly)Continuous or daily
How unfinished features hideIn develop or feature branchesBehind feature flags
Hotfix pathBranch from main, merge backSame path as any PR
Merge complexityHigh (long branches diverge)Low (branches do not survive long enough)
CI requirementsTolerant of slow CINeeds fast CI plus a merge queue
Best fitVersioned software, regulated releasesSaaS, web apps, internal tools

When Gitflow is still the right answer

Gitflow has not aged out, it has narrowed in scope. There are real cases where it is still the right model.

  • Versioned software with parallel maintenance. If you support 1.x, 2.x, and 3.x in production at the same time, you need long-lived release branches to backport fixes.
  • Regulated software with formal release gates. Medical devices, automotive, financial systems where each release goes through a compliance review. The release branch is part of the audit trail.
  • Mobile apps where the store review takes days. The release branch holds the candidate while it is in review and lets development continue on develop.
  • Embedded software shipped to physical hardware. Once the firmware is on the device, the release branch is what you patch.

Most of these have one thing in common: shipping to production is genuinely a discrete event with cost and ceremony. The branch model exists because the deployment model demands it. For everything else (which is most modern software), the branch model is solving a problem the deployment model no longer has.

How to migrate from Gitflow

Skip the rip-and-replace. Migrating in place takes a few weeks and lets the team keep shipping the whole time.

  1. Make main shippable. Any commit on main should be safe to deploy. This is the precondition for everything else, and it usually requires a merge queue and a "green main" rule that nobody bypasses.
  2. Add feature flags. Pick a flag library, wrap the next two or three features in flags, get the team comfortable with shipping dark.
  3. Stop creating develop branches for new work. New PRs target main. Existing develop work merges to main on its normal schedule.
  4. Cut release branches as needed, not on a schedule. If you ship to production from a tag on main, you may not need release branches at all. If you do, treat them as short-lived stabilization branches, not as the integration branch.
  5. Shorten branch lifetimes. Aim for branches that open in the morning and merge by end of day. This is the cultural shift, and it usually takes a quarter to settle.

By the end, develop is gone, feature branches are short, releases come from main, and the release branch (if any) is a stabilization line, not an integration line.

Trunk-based development needs a green main.

Mergify's merge queue keeps main green when the whole team is merging at once. The piece that makes Gitflow's stabilization branches unnecessary.