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 branches | main, develop, release/* | main only |
| Feature branch lifetime | Days to weeks | Hours to one day |
| Integration cadence | At end of feature | Multiple times per day |
| Release cadence | Scheduled (weekly/monthly) | Continuous or daily |
| How unfinished features hide | In develop or feature branches | Behind feature flags |
| Hotfix path | Branch from main, merge back | Same path as any PR |
| Merge complexity | High (long branches diverge) | Low (branches do not survive long enough) |
| CI requirements | Tolerant of slow CI | Needs fast CI plus a merge queue |
| Best fit | Versioned software, regulated releases | SaaS, 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.
- 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.
- Add feature flags. Pick a flag library, wrap the next two or three features in flags, get the team comfortable with shipping dark.
- Stop creating develop branches for new work. New PRs target main. Existing develop work merges to main on its normal schedule.
- 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.
- 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.