Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Git Branching Strategies: GitFlow vs Trunk-Based

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
GitFlow Branch Topologymaindevelopfeature/authfeature/cartrelease/2.4.0hotfix/2.3.1Features merge to develop; releases merge to main
GitFlow uses multiple long-lived branches — ideal when releases are scheduled and QA-heavy.

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.

Trunk-Based Flowmain (trunk)feat Afeat Bfeat CCI + Deployon every mergeShort branches; main always deployable
Trunk-based development merges small changes often — main stays the single source of deployable truth.

Core trunk-based rules

  1. Keep feature branches alive for hours or days, not weeks.
  2. Rebase or merge from main at least daily to avoid drift.
  3. Hide incomplete work behind feature flags or config toggles.
  4. Run the full test suite on every pull request to main.
  5. 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.

CriterionGitFlowTrunk-based
Release cadenceScheduled (weekly, monthly, semver)Continuous (daily or per merge)
Branch countHigh (main, develop, release, hotfix)Low (main + short feature branches)
Merge complexityHigher; back-merges requiredLower if branches stay short
CI requirementsCan tolerate slower pipelines on developRequires fast, reliable automated tests
Parallel featuresEasy on separate feature branchesNeeds flags or careful coordination
RollbackRevert a tagged release on mainRevert commit or redeploy prior tag
Best fitVersioned apps, mobile backends, client sign-offSaaS, web apps, internal tools, small teams
Typical team size5–30+ with dedicated QA1–15 with strong automation
Pick Your Git Branching StrategyShip more than weekly?YesCI under 15 min?YesTrunk-based+ feature flagsNoGitFlow+ release branchesNoGitFlowstabilise in QAGit branching strategies: GitFlow vs trunk-based — match cadence to CI speed
Decision tree: release frequency and CI speed usually determine the right Git branching strategy.

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.

Branching Anti-PatternsLong branchesweeks without mergeNo back-mergehotfix lost on developStale releasebranch never closedMerge conflicts + production driftFix with short branches, bots, and branch expiry rulesPrevention checklistDaily sync · CI gates · auto back-merge · delete merged branches
Long branches, missed back-merges, and stale release branches cause most GitFlow production incidents.

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

GitFlow, popularised by Vincent Driessen in 2010, splits work across several long-lived branches for planned releases rather than continuous streams. Main holds production-ready code with semver tags. Develop integrates features for the next release. Feature branches branch from develop per ticket. Release branches freeze code for QA, version bumps, and changelogs. Hotfix branches patch production from main and must merge back to develop. On a Laravel legal-tech portal, that layout gives stakeholders a named release and a rollback tag, but every hotfix back-merge is a step teams can forget.

Trunk-based development keeps one primary line, usually main, as the single integration and deployable truth. Developers use short-lived feature branches, often under two days, or commit directly behind feature flags. Releases are cut from main tags, not a separate develop branch. The model assumes fast, trustworthy CI: if tests pass on main, the code is deployable. That matches Deployer 7 pipelines where a push to main triggers tests, symlink swap, and PHP-FPM reload. Incomplete work stays hidden until ready via flags or config toggles.

GitFlow uses multiple long-lived branches and scheduled semver releases; trunk-based uses main plus short feature branches for continuous delivery. GitFlow adds ceremony; trunk-based adds integration speed.

Pick GitFlow when you ship versioned software on a scheduled cadence—weekly, monthly, or semver—and need formal QA sign-off before production. It fits client-facing Laravel products where stakeholders expect a named release, rollback tag, and a stable develop integration line. Teams of roughly five to thirty or more with dedicated QA benefit from release branches as a freeze window. I've used it on legal-tech portals where payment or compliance changes wait for approval while content may move faster elsewhere. GitFlow is a poor fit if you need daily deploys and main must always stay green.

Trunk-based fits SaaS, web apps, internal tools, and small teams shipping daily or per merge. Typical team size is one to fifteen with strong automation. If your CI is fast and reliable, main becomes the single source of deployable truth and you avoid develop back-merge drift. It pairs naturally with blue-green or canary deployments: deploy small slices and roll back by switching traffic or redeploying a prior tag. Before deleting develop, invest in automated tests and pipeline quality gates. Trunk-based fails quickly when tests are flaky or missing.

No for versioned releases with QA sign-off; yes for continuous-delivery SaaS where main must stay deployable.

GitFlow targets scheduled releases with higher branch count—main, develop, release, hotfix—and higher merge complexity because hotfixes and releases require back-merges to develop. Trunk-based targets continuous delivery with low branch count—main plus short feature branches—and lower merge complexity if branches stay short. GitFlow can tolerate slower CI on develop; trunk-based requires fast, reliable automated tests on every pull request to main. Parallel features are easy on separate GitFlow feature branches; trunk-based needs feature flags or careful coordination. Rollback under GitFlow means reverting a tagged release on main; under trunk-based, revert the commit or redeploy a prior tag.

One trunk (main) plus one short-lived branch per active task—usually two to four branches total.

Not strictly, but without them you cannot merge incomplete features safely to main.

Under GitFlow, deploy develop to staging, or release/ branches during stabilisation windows before production tagging. Under trunk-based, deploy main to staging on every merge, or use ephemeral preview environments per pull request. Production should always deploy from an immutable tag or commit SHA—never from a moving branch tip without recording the SHA. Align your GitLab CI workflow rules and Deployer 7 targets so branch names mirror what staging and production actually run. Mismatch here causes the classic works-on-staging production bug, often compounded by PHP-FPM opcache not reloading after deploy.

Map branches to pipeline stages explicitly. Feature branches run lint and unit tests, optionally a preview environment. Develop runs the full test suite and deploys to staging. Release branches run smoke tests, migration dry-runs, and tag candidates. Main deploys to production only on semver tag push, for example v2.4.0. Hotfix branches get expedited tests, a production deploy tag, and an automated back-merge pull request to develop. Use workflow rules matching branch names and protect main and develop with required status checks. After Deployer symlink swap, reload PHP-FPM so opcache picks up changed PHP files.

Every merge to main triggers the same pipeline: full test suite, staging deploy, and optionally a manual production gate depending on risk tolerance. Staging may track main directly or use per-pull-request preview environments. Production deploys from main tags or from main after manual approval. Tag main after deploy for traceability, not before integration. Queue workers, scheduled tasks, and migrations must stay backward-compatible within a single deploy window because you are not stabilising on a separate release branch. Without a green main branch, you lose the core promise of trunk-based development entirely.

Long-lived feature branches open for weeks cause expensive conflicts in both models—slice work smaller and merge daily. Skipping the GitFlow hotfix back-merge to develop leaves integration broken until someone notices; automate a bot pull request after every hotfix tag. Deploying untested merge commits to main or develop has caused outages—require status checks and scan for secrets in CI. Ignoring database migration order hurts both models: trunk-based assumes forward-only migrations every merge, while GitFlow release branches need compatibility across the develop-to-main gap. Server permission drift after deploy is often blamed on Git but is really deploy hygiene.

Yes, but only with clear boundaries documented in the project README. A common hybrid is trunk-based on the main application repo and GitFlow on a mobile SDK or WordPress theme that clients pin to semver versions. Another pattern is trunk-based with a long-lived develop that mirrors main weekly for client UAT—effectively GitFlow without release branches. On a legal-tech portal, content may ship daily while payment modules wait for sign-off, handled by feature flags on main without a permanent develop branch. Do not let two developers use GitFlow and two use trunk-based in the same Laravel repo; pick one default and document exceptions.

Branch hotfix/ from main, apply the emergency patch, run expedited tests, merge to main with a semver tag, deploy production from that tag, then immediately back-merge to develop with a no-fast-forward merge if your team uses that convention. If the back-merge is skipped, develop diverges and the next release reintroduces the bug you just fixed. Automate a pull request from main to develop after every hotfix tag and block the next release cut if that pull request fails. Pair hotfix discipline with protected branches and required pipelines so untested commits never reach main. Git reflog helps recovery, but prevention beats firefighting.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: