Migrating from Gitflow to trunk-based development: a step-by-step playbook
A team that tried to migrate from Gitflow to trunk-based development overnight ground to a halt for three weeks. Here is a realistic five-phase playbook that takes 2 to 4 months.
A team I advised last year decided to switch from Gitflow to trunk-based development. On a Monday, the lead announced the change in the team channel. By Wednesday, develop was deleted. By Friday, main was broken. By the following Friday, three engineers had quit, two more were openly job-searching, and the team had reverted everything. Three weeks of zero output.
What went wrong? They tried to migrate the workflow before migrating the supporting practices. Trunk-based development depends on automated tests, feature flags, and a fast review culture (see post #2). Without those, removing develop does not get you to TBD — it just removes the only safety net Gitflow gave you.
Migration has to be gradual. A team can move from Gitflow to TBD safely over 2–4 months, in phases, with each phase building the foundation for the next. This post is the step-by-step playbook.
Why gradual is non-negotiable
The temptation is to switch over a weekend. "Read this post, follow the steps Monday morning, done by lunch."
This does not work because TBD requires three things in place before the workflow change makes sense:
- Feature flag infrastructure. Without flags, you cannot merge unfinished work to
mainsafely. Without that ability, branch lifetime cannot actually shrink. - Tests fast and reliable enough to trust on every merge. Without trust in the test suite, engineers will be afraid to merge to
main, and they will revert to long branches in practice (even if the workflow document says otherwise). - Cultural acceptance that small, frequent merges are good. A team that grew up on big, infrequent merges has muscle memory the other way. That memory takes weeks to change.
The phases below build these prerequisites first, then change the workflow, then internalize new habits. Skip any phase and the migration becomes the story at the top of this post.
Phase 1 (weeks 1–2): install feature flag infrastructure
Before you touch the workflow, install the safety net.
Pick a flag tool
Options include LaunchDarkly, Optimizely, Unleash, Flagsmith, and many homegrown systems. The vendor matters less than picking one and standardizing.
What you need at minimum:
- An SDK in your application code that checks flags at runtime.
- A way to toggle flags without deploying code.
- Per-environment configuration (dev, staging, prod).
Most teams can be operational with a flag tool in 3–5 working days.
Write the team conventions
Once the tool is installed, write a short doc:
- Naming:
feature_search_v2, notjohn_test_thing. - Lifetime expectation: most flags live 2–6 weeks. Anything older than 90 days gets reviewed for removal.
- Default state: new flags default to "off" in production.
- Cleanup: when a feature is fully rolled out, the flag and the old code path get removed in a follow-up PR.
The exact conventions matter less than having them written down.
Pilot with one feature
Pick one current feature being built and put it behind a flag. Ship it via the new flag flow. Toggle it on for internal users, then 10% of customers, then 100%. Watch the dashboards. Remove the flag.
The point of the pilot is to find rough edges in your tooling before the team relies on it.
What does not change yet
Workflow is still Gitflow. develop still exists. Releases still cut from release/* branches. Nothing about Git changes in Phase 1. You are only installing infrastructure.
Phase 2 (weeks 3–4): shrink branch lifetime targets
Now use the safety net to start shrinking branch lifetime. The workflow is still Gitflow, but the behavior changes.
Set explicit lifetime targets
Communicate to the team:
- Week 3 target: feature branches live at most 3 days before merging to
develop. - Week 4 target: at most 1 day.
Branches that need to be longer must use feature flags. The unfinished work merges to develop behind a flag, daily, and is not visible to users.
Make the data visible
Set up a simple dashboard or weekly report:
- Median branch lifetime this week.
- 95th-percentile branch lifetime.
- Number of branches older than 7 days.
A 10-line script using git for-each-ref --sort=committerdate can produce these numbers. Post them weekly in the team channel.
Visibility itself shifts behavior. When everyone sees "median branch age is 4.2 days, target is 3 days," people self-correct.
Address the friction
In Phase 2 you will find what was hidden by long branches:
- Tests that are flaky and only fail under certain merge timings.
- PRs that sit for two days waiting for review.
- Build times that make rapid merging impractical.
Each of these is a Phase-2 fix. Without addressing them, you cannot get to short branches.
If review latency is the blocker, set explicit review SLAs: PRs reviewed within X hours. If flaky tests are the blocker, prioritise fixing or removing them. If build is the blocker, parallelise and shard.
What does not change yet
develop still exists. release/* branches still exist. Hotfixes still go through Gitflow's hotfix mechanism. The structure is intact; the cadence inside it is changing.
Phase 3 (weeks 5–6): shrink the role of develop
This is the phase where the workflow itself starts to change.
Eliminate the release-branch step
For small releases, stop cutting release/* branches. Ship directly from develop after testing. Use feature flags to gate work-in-progress.
In Gitflow, the release/* branch existed to stabilize a release while new work continued on develop. With feature flags, work-in-progress is already invisible. The release branch is solving a problem you no longer have.
Shorten the develop-to-main cycle
In standard Gitflow, develop merges into main only at release time. In late Gitflow, merge develop into main daily. Each merge is itself a small, deployable release.
# Daily — automate this
git checkout main
git merge --ff-only develop
git push
# Auto-deploy the new main
The fast-forward-only merge ensures main and develop stay in lockstep. Once they are always identical, the question of "do we need both?" answers itself.
Reduce hotfix overhead
In standard Gitflow, hotfixes branch from main, fix, then merge to both main and develop. In late Gitflow with daily merges, hotfixes can branch from main and merge back to main only — the next daily merge propagates to develop automatically.
This eliminates the "forgotten hotfix" failure mode (see post #7) because there is no second branch to forget.
What does not change yet
develop still exists. The team is still nominally on Gitflow. The mechanics have shifted, but the name is unchanged.
Phase 4 (weeks 7–8): retire develop
If Phase 3 went well, develop and main are now identical most of the time. It is time to remove develop.
Announce the change
Two weeks of notice. "On <date>, develop will be archived. After that, all new branches start from main and merge back to main."
Common questions to address:
- "What about hotfixes?" — Now there is only
main. Hotfixes branch frommainand merge tomain. If you still ship versioned releases, see Microsoft Release Flow (post #7) for the cherry-pick model. - "What about long-running features?" — Use feature flags. The flag infrastructure from Phase 1 is what makes this work.
- "What if
mainbreaks?" — Revert the offending commit immediately. Do not letmainstay broken. This habit takes practice; expect it to feel painful at first.
Update tooling
- Default branch on GitHub/GitLab: set to
main. - CI: every workflow that ran on
developnow runs onmain. - Deploy pipelines: deploy from
maininstead of fromdeveloporrelease/*. - Branch protection: enable on
main(probably already done; double-check). - CODEOWNERS: still applies. Path-based ownership is workflow-agnostic.
Archive develop
# Keep history for reference
git checkout develop
git tag archived/develop-2026-08-01
git push --tags
# Delete the branch
git push origin --delete develop
The tag preserves history without leaving a confusing branch lying around.
What does not change yet
The team is now technically on trunk-based development. But muscle memory is still Gitflow. Phase 5 is about building new habits.
Phase 5 (ongoing): build TBD-native habits
The hardest phase, because it is cultural rather than technical.
Smaller PRs
Gitflow teams often have PRs of 500–1500 lines. TBD teams aim for 50–300 lines per PR. The smaller PR is faster to review, faster to revert if wrong, easier to reason about.
Encourage in code review:
- "Can this be split into two PRs?"
- "Does this change need to be in this PR, or can it ship later?"
- "Is there a flag-protected first slice we can ship today?"
This takes 6–12 weeks to feel natural. Be patient.
Faster reviews
A TBD-shaped team reviews PRs within hours, not days. Mechanisms:
- A rotating "reviewer of the day."
- Async pings in chat when PRs sit too long.
- Pairing reviews — review the PR with the author over a call when it is complex.
The goal is that a PR opened in the morning is merged by end of day. Always.
Comfort with revert
In Gitflow, "broken main" rarely happens because main only changes at release time. In TBD, main changes 20 times a day. Sometimes one of those changes breaks something.
The right response: revert immediately. git revert <bad-commit-sha>. The bad change is out in 60 seconds. The author then re-opens a corrected PR.
Teams new to TBD often hesitate to revert ("but the author worked on that change!"). This hesitation lets broken main linger, which erodes trust. Make revert routine, blame-free, and fast. Anyone can revert anyone.
Continuous flag cleanup
Every month, audit flags. Anything fully rolled out for 30+ days gets a cleanup PR. Anything older than 90 days gets a "still needed?" discussion.
Without this discipline, the codebase accumulates dead flag branches and becomes harder to reason about than the Gitflow you left behind.
Realistic timeline
For a healthy team with existing CI and reasonable culture:
- Phase 1 (flag infrastructure): 1–2 weeks
- Phase 2 (shrink branches): 1–2 weeks
- Phase 3 (shrink develop's role): 2 weeks
- Phase 4 (retire develop): 1 week
- Phase 5 (habit building): 6–12 weeks of focused attention, ongoing forever
Total: 2–4 months to feel mostly natural, with continued refinement for at least another quarter.
For teams with weak testing, slow CI, or skeptical culture, double these numbers and consider whether the migration is worth the cost right now. Sometimes the answer is "fix the prerequisites this quarter, migrate next quarter."
Common stumbling blocks
"Our QA team blocks releases." If a separate QA cycle must run before any code ships, full TBD may not fit. Consider GitLab Flow with a pre-production branch instead — it preserves the QA step without the full Gitflow overhead.
"Cultural resistance from senior engineers." Some senior engineers prefer Gitflow because they grew up with it. Address the substance of their objections, not just the workflow change. Often the real objection is "I do not trust the tests" or "I do not trust the reviewers" — fix those first.
"Broken main fatigue." In the first few weeks of TBD, main will break more than the team is used to. Engineers panic. The fix is not to abandon TBD; it is to (a) revert faster, (b) improve the test suite to catch the issues earlier, (c) accept that main being briefly red is normal and fine as long as it gets fixed fast.
"Missing tooling." Merge queues, fast CI, branch protection, flag tooling — TBD needs more infrastructure than Gitflow. If the infrastructure is missing, the migration will hurt. Build the infrastructure first.
What success looks like
Three months after Phase 4:
- Median branch lifetime: under 24 hours.
- PRs merged per engineer per day: 1–3.
mainred duration when broken: under 30 minutes (revert habits).- Feature flags in active use: matches the size of the team.
- Release overhead: largely gone, replaced by continuous shipping.
- Team sentiment: "this is faster than what we had."
If you reach this state, the migration succeeded. The team is no longer running Gitflow with rebrandings; it is running TBD with internalized habits.
What to track during the migration
Measurement is what tells you the migration is working. A short set of metrics, tracked weekly, gives you the signal you need.
Branch lifetime. Median and 95th percentile of branch age at merge. Should decrease from "many days" at the start to "under 24 hours" by end of Phase 4.
main health. Percentage of time main is green over a week. Should stay above 90% throughout the migration; if it drops, slow down.
Merge frequency. PRs merged per engineer per week. Should rise steadily through the migration.
Feature flag count and age. Number of active flags and median flag age. A growing pool of old flags signals cleanup discipline is slipping.
Production incidents tied to migration. Track any incident caused directly by the workflow change. The number should be near zero. If you see two or more incidents in a single week traceable to the migration, pause and address the root cause before proceeding.
A weekly 15-minute review with the team — "here are the numbers, here is what changed, here is the next step" — keeps everyone aligned. Migrations fail silently when the team loses the thread; visible metrics prevent that.
When NOT to migrate (yet)
Migration is not always the right next step. Some honest reasons to defer:
Versioned software with active support windows. If you ship v2 of a product and still support v1 with patches, you have a parallel-timeline problem that Gitflow handles natively. Moving to TBD requires you to also adopt a release-branch model (see post #7). If you are not ready for that, stay on Gitflow.
No appetite for feature flag infrastructure. TBD without flags is not TBD. If the team or leadership cannot commit to maintaining flag infrastructure for the long term, the migration will collapse in 6 months.
Slow tests with no investment in fixing them. TBD with slow tests creates queue backups and merge anxiety. If test improvement is not on the roadmap, do that first, then consider migrating.
An ongoing major project with a hard external deadline. Do not migrate in the middle of shipping a critical release. Wait for a calmer quarter. The migration deserves attention, not noise.
A team in any of these situations should keep Gitflow (or whatever they have) for now and revisit in 3–6 months. There is no prize for switching workflows on a forced timeline.
Common myths
Myth 1: "You can switch to TBD over a weekend." Wrong. Teams that try this usually grind to a halt for weeks and revert. The story at the top of this post is the common case, not the exception. Gradual is faster than fast.
Myth 2: "Migration to TBD means losing your Gitflow history." Wrong. The migration does not rewrite history. develop, old release/* branches, and old hotfix branches stay in the repo's history. They just stop receiving new commits. Past releases remain inspectable forever.
Myth 3: "Once on TBD, you can never go back to release branches." Wrong. Most teams on TBD still occasionally cut a release/* branch — for a customer-specific patch, for a long-running security maintenance line, or because a regulatory cycle requires it. The trunk is the default, not the only place code lives. TBD and release branches coexist comfortably when needed.
What to read next
You have reached the end of this 8-post series. The other posts:
- Choosing a Git workflow: a decision guide for real teams — the framework that started the series.
- Trunk-based development: when it wins and when it doesn't — the destination of this migration playbook.
- Gitflow in 2026: still useful, or time to move on? — when the right call is to stay on Gitflow.
- Scaling a Git workflow from solo to large team — how team size interacts with workflow choice.
- Monorepo Git techniques — repository-shape considerations.
- Multi-repo coordination — the alternative repo shape.
- Release branches and hotfix workflows that don't lose commits — operational mechanics for parallel timelines.
If you want to feel what a TBD-shaped workflow looks like in your hands before committing your team to it, the Trunk-Based Dev lesson below opens a live terminal where you can run through the rhythm in two minutes.