
September 10, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Choosing between Git branching strategies: GitFlow vs trunk-based is not a style debate. It decides how fast you ship, how painful merges become, and whether production stays stable after Friday deploys. On real client projects I maintain with GitLab CI and Deployer 7, the wrong branch model has caused more release pain than bad PHP code. This guide maps both models, compares them on criteria that matter in production, and gives copy-paste branch rules you can adopt this week.
What is GitFlow and how does its branch model work?
GitFlow, popularised by Vincent Driessen in 2010, splits work across several long-lived branches. The model assumes releases are planned events, not continuous streams. You merge features into develop, stabilise in release/*, then merge to main and back-merge hotfixes.
In practice, a Laravel team on GitFlow might run PHP 8.3 on Laravel 12 with this layout:
- main — production-ready code only; tagged semver releases
- develop — integration branch for the next release
- feature/* — one branch per ticket, branched from develop
- release/* — freeze window for QA, version bumps, changelog
- hotfix/* — emergency patches from main, merged back to develop
Typical GitFlow commands
Start a feature from develop:
git checkout develop
git pull origin develop
git checkout -b feature/KT-142-payment-webhook
git push -u origin feature/KT-142-payment-webhook Cut a release when develop is stable:
git checkout -b release/2.4.0 develop
composer install --no-dev --optimize-autoloader
php artisan test
git commit -am "Bump version to 2.4.0"
git checkout main && git merge --no-ff release/2.4.0
git tag -a v2.4.0 -m "Release 2.4.0"
git checkout develop && git merge --no-ff release/2.4.0
git branch -d release/2.4.0 GitFlow shines when you ship versioned software with formal QA. I've used it on legal-tech portals where stakeholders expect a named release and a rollback tag. The overhead is real though. Every merge back to develop after a hotfix is a place where teams forget a step and drift occurs. Pair GitFlow with protected branches and required pipelines, similar to patterns in Azure Repos branch policies.
What is trunk-based development and when does it fit?
Trunk-based development keeps one primary line — usually main — as the integration point. Developers work on short-lived branches, often less than two days, or commit directly behind feature flags. Releases are cut from main tags, not from a separate develop branch.
The model assumes CI is fast and trustworthy. If tests pass on main, the code is deployable. That matches how I run Deployer 7 pipelines on sister sites like Notary Kathmandu: push to main, pipeline runs, symlink swap, PHP-FPM reload.
Core trunk-based rules
- Keep feature branches alive for hours or days, not weeks.
- Rebase or merge from main at least daily to avoid drift.
- Hide incomplete work behind feature flags or config toggles.
- Run the full test suite on every pull request to main.
- Tag main after deploy for traceability, not before integration.
Trunk-based pairs naturally with blue-green or canary deployments. You deploy small slices from main and roll back by switching traffic, not by reverting a release branch. For Laravel 13 on PHP 8.3+, that means queue workers, scheduled tasks, and migrations must be backward-compatible within a single deploy window.
Google's engineering practices document trunk-based development as the default for high-velocity teams. The official Git reference at git-scm.com branching workflows describes simpler alternatives that overlap with trunk-based ideas. Neither source mandates feature flags, but in practice you need them if multiple developers touch the same codebase daily.
How do GitFlow and trunk-based compare on real criteria?
Teams often ask which model is "better." The honest answer depends on release cadence, team size, and CI maturity. Use the table below as a decision aid, not a scorecard.
| Criterion | GitFlow | Trunk-based |
|---|---|---|
| Release cadence | Scheduled (weekly, monthly, semver) | Continuous (daily or per merge) |
| Branch count | High (main, develop, release, hotfix) | Low (main + short feature branches) |
| Merge complexity | Higher; back-merges required | Lower if branches stay short |
| CI requirements | Can tolerate slower pipelines on develop | Requires fast, reliable automated tests |
| Parallel features | Easy on separate feature branches | Needs flags or careful coordination |
| Rollback | Revert a tagged release on main | Revert commit or redeploy prior tag |
| Best fit | Versioned apps, mobile backends, client sign-off | SaaS, web apps, internal tools, small teams |
| Typical team size | 5–30+ with dedicated QA | 1–15 with strong automation |
A pattern I've seen repeatedly: a three-person agency adopts GitFlow because a blog post said it was "enterprise." CI takes 25 minutes. Features sit on branches for three weeks. The merge to develop becomes a manual conflict festival. That team was one rule change away from trunk-based with a staging environment.
Conversely, trunk-based fails when tests are flaky or missing. Without a green main branch, you lose the core promise of the model. Invest in automated tests and pipeline quality gates before you delete develop.
How should Laravel teams wire CI/CD to each branching model?
Branch strategy and deployment are one system. Your Git branches should mirror what your server actually runs. On projects I deploy with Deployer 7, the mapping looks like this.
GitFlow pipeline mapping
- feature/* — lint, unit tests, optional preview env
- develop — full test suite, deploy to staging
- release/* — smoke tests, migration dry-run, tag candidate
- main — production deploy on tag push only
- hotfix/* — expedited tests, deploy tag, back-merge to develop
Example GitLab CI rule snippet:
workflow:
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH =~ /^release\//
- if: $CI_COMMIT_BRANCH =~ /^feature\//
deploy_production:
stage: deploy
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
script:
- dep deploy production -o branch=$CI_COMMIT_TAG Trunk-based pipeline mapping
Every merge to main triggers the same pipeline. Staging may track main directly or use a ephemeral preview per pull request. Production deploys from main tags or from main after manual approval, depending on risk tolerance.
deploy_staging:
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- dep deploy staging
deploy_production:
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
script:
- dep deploy production Align image or artefact tagging with your branch model. See Docker image tagging strategies for semver vs commit-SHA trade-offs. After deploy, reload PHP-FPM so opcache picks up changed files. That step is easy to miss and causes "works on staging" bugs.
For enterprise Laravel apps with multiple clients on different versions, custom release trains per client sometimes force a GitFlow-like model even when the core product team prefers trunk-based. Document which repos use which model. Mixed rules across microservices create integration surprises.
What are the most common Git branching mistakes in production?
Branching strategy fails in predictable ways. These are the ones I troubleshoot most often on live systems.
Long-lived feature branches
A branch open for four weeks defeats both models. Rebase costs explode. Context is lost. The fix is the same for GitFlow and trunk-based: slice work smaller, merge daily, and use rebase vs merge deliberately so history stays readable without hiding conflicts.
Skipping the back-merge after hotfix
GitFlow hotfixes merged only to main leave develop broken until someone notices. Automate a bot pull request from main to develop after every hotfix tag. If that pull request fails, block the next release cut.
Deploying untested merge commits
Fast-forward merges without CI on the target branch have caused production outages on booking systems I've maintained. Require status checks on main and develop. Scan for secrets in CI with tools covered in Git secrets scanning with Gitleaks.
Ignoring database migration order
Trunk-based deploys assume migrations run forward only on every merge. GitFlow release branches need migration compatibility across the gap between develop and main. Test migrations on a copy of production data before tagging. Pair this with database backup strategies so a bad migration does not become a bad quarter.
Permission and ownership drift on servers
Branching does not fix bad deploy hygiene. After symlink swap, storage/ and bootstrap/cache/ must stay writable by PHP-FPM. That topic sits closer to Linux server administration than to Git, but bad deploys often get blamed on the branch model.
When something goes wrong, Git reflog saves hours. Branch strategy does not replace recovery skills. Train the team on both.
Can you mix GitFlow and trunk-based on the same project?
Yes, but only with clear boundaries. A common hybrid: trunk-based on the main application repo, GitFlow on a mobile SDK or WordPress theme that clients pin to semver versions. Another hybrid: trunk-based development with a long-lived develop that mirrors main weekly for client UAT — effectively GitFlow without release branches.
On a legal-tech portal like Court Marriage In Nepal, content updates may ship daily while payment module changes wait for sign-off. Feature flags on main handle that without a permanent develop branch. For Adventure Third Pole Trek, booking logic changed often enough that trunk-based with staging matched the business cadence.
Do not mix models inside one repo without documented rules. If two developers use GitFlow and two use trunk-based on the same Laravel codebase, you get duplicate integration branches and lost tags. Pick one default. Document exceptions in the project README.
Version control hygiene also means a correct .gitignore. Committed .env files and vendor directories have caused more incidents than wrong merge strategies. See fixing Git ignore problems and validate JSON config in CI with a JSON formatter when pipeline files change.
API versioning is adjacent but separate. Your branch model does not replace URL versioning rules. Read Laravel API versioning strategy if mobile clients lag web deploys by weeks.
Key Takeaways
- GitFlow fits scheduled semver releases, formal QA, and teams that need a stable develop integration line.
- Trunk-based fits daily deploys, small teams, and repos with fast CI plus feature flags for incomplete work.
- Match branch names to pipeline rules so main always reflects what production or staging actually runs.
- Keep feature branches short; long branches hurt both models and create expensive merge conflicts.
- After every GitFlow hotfix, back-merge to develop automatically or you will ship regressions.
- Re-evaluate your Git branching strategies: GitFlow vs trunk-based whenever release cadence or team size changes — the wrong model slows every deploy.
People Also Ask
Is GitFlow outdated in 2026?
GitFlow is not obsolete for teams that ship versioned releases with QA sign-off. It is a poor fit for continuous delivery SaaS where main must stay deployable. Many organisations moved to trunk-based, but client-facing Laravel products with monthly releases still benefit from release branches.
How many branches should a small team maintain?
A small team should maintain one trunk (main) plus one short-lived branch per active task. That is usually two to four branches total. Adding develop, release, and hotfix branches only pays off when release coordination or QA workload justifies the overhead.
Does trunk-based development require feature flags?
Not strictly, but without feature flags you cannot merge incomplete features safely. Flags let you integrate code to main while hiding UI or API behaviour until ready. For Laravel, use config, database settings, or a simple Feature facade pattern.
What branch should CI deploy to staging?
Under GitFlow, deploy develop (or release/* during stabilisation) to staging. Under trunk-based, deploy main to staging on every merge, or deploy pull request previews per branch. Production should always deploy from an immutable tag or commit SHA, never from a moving branch tip without recording the SHA.
Choose a branch model your deploy pipeline can actually support
Git branching strategies: GitFlow vs trunk-based is a trade-off between release ceremony and integration speed. GitFlow gives you named releases and a place to freeze code. Trunk-based gives you fewer merges and a single line of truth. Neither fixes weak tests or slow CI. Start from your real release cadence, write branch protection rules, then align Deployer or your platform pipeline to match.
If you want help auditing an existing Laravel repo — branch rules, GitLab CI, zero-downtime deploys — see the support and maintenance service or review shipped work in the project portfolio. For greenfield apps, custom software development includes sensible Git defaults from day one. Questions about your setup? Contact us and include your current branch diagram — that single artefact tells most of the story.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

