4.5.9 · D5Software Engineering

Question bank — Git workflows — Gitflow, trunk-based development

1,575 words7 min readBack to topic

Before we start, one shared vocabulary check so no term is used unearned:


True or false — justify

TBD (trunk-based development) means committing directly to main and never using branches.
False. TBD forbids long-lived branches, not branches themselves — short-lived branches with small PRs are fully allowed. The discipline is about lifetime, not existence.
In Gitflow, a hotfix merges only into main because that's the branch production runs from.
False. A hotfix merges into both main and develop. If it skipped develop, the next release built from develop would silently re-introduce the very bug you just fixed.
More branches always means more safety because work stays isolated.
False. Isolation protects the shared line short-term but creates integration debt: main moves underneath you, so the eventual merge is bigger and riskier. Safety comes from small, frequent integration, not from hiding.
You can run trunk-based development safely without a test suite as long as PRs are small.
False. Small PRs limit conflict size but not bugs. Without Continuous Integration catching a broken trunk in minutes, everyone integrating into that trunk inherits the breakage — CI is the safety net that makes daily integration survivable.
Feature flags are an optional nicety in TBD.
False. Without flags you cannot merge incomplete features daily, so big features force you back into long-lived branches — the exact thing TBD exists to prevent. Flags are what make daily integration of big work possible.
In Gitflow, develop is the branch that gets tagged for each production release.
False. main is tagged per release; develop is only the integration branch where finished features accumulate. develop is "what's being built," main is "what's live."
A release/* branch in Gitflow is where you add the last few features before shipping.
False. A release branch is feature-frozen — only bug fixes allowed. Its whole purpose is to let a release stabilize while new feature work continues on develop.
The --no-ff merge flag is just cosmetic and can be dropped safely.
Mostly false for Gitflow's intent. --no-ff forces a merge commit so history records that a group of commits belonged to one feature. A fast-forward erases that boundary, losing the documentation of what was integrated as a unit.
Deploying code and releasing a feature to users are the same event.
False. With feature flags they're decoupled: code can be deployed (on the server) daily while the feature stays released (visible) to users only when the flag is flipped on.
Choosing Gitflow vs TBD is mainly a matter of which is objectively "better."
False. The strongest predictor is a capability question, not a taste question: do you have CI strong enough to keep trunk green? Without it, TBD's continuous integration has no safety net.

Spot the error

"Our team uses TBD, so we branched off main for the big redesign and we'll merge it back in six weeks."
The six-week lifetime is the error. A six-week branch is a long-lived branch — that's Gitflow-style isolation wearing a TBD label. Under TBD the redesign should merge daily behind a feature flag.
"We stabilize the release by fixing bugs on develop, then merge develop into main."
Stabilizing on develop is the error: it blocks all new feature work and blurs which fixes belong to this release. Stabilize on a feature-frozen release/* branch instead.
"We hotfixed prod off develop because that's the newest code."
Branching the hotfix off develop is the error — develop may contain unfinished, unstable work. Hotfixes branch off main, which reflects exactly what's live, so the patch contains only the fix.
"We merged the feature into develop with a fast-forward to keep history linear and clean."
Fast-forwarding here loses the feature boundary. Gitflow deliberately wants the merge commit (--no-ff) as a marker of which commits formed the feature.
"We have CI, so we don't need feature flags for TBD."
The error is treating CI as a substitute for flags. CI tells you the trunk is safe; flags let you merge incomplete work without exposing it. They solve different problems — you need both.

"Our `P_{\text{conflict}} = 1-(1-p)^{\lambda T}TTP_{\text{conflict}}$ toward 1, not away from it.


Why questions

Why does conflict probability grow with branch lifetime rather than staying constant?
Because each independent diverging change on the shared branch has probability of touching your lines; a branch living days sees about such changes, and climbs with every added day. See Branching and Merging.
Why does Gitflow keep two permanent branches instead of one?
To separate "what is live" (main, every commit a tagged release) from "what is being built" (develop, integration). The split lets a release stabilize without freezing ongoing feature work.
Why can TBD integrate so much faster than Gitflow?
Gitflow asks "is this feature finished?" (answerable in weeks); TBD asks "is this commit safe — tests pass, hidden if incomplete?" (answerable in minutes). The faster question enables far more frequent merges.
Why does regulated / audited software often favour Gitflow?
Its ceremony (release branches, merge commits, tagged releases) produces explicit traceability — you can point at exactly which commits went into which shipped version, which auditors demand. See Semantic Versioning.
Why is "keep trunk green" the central discipline of TBD?
Because everyone integrates into the same trunk daily; a red (broken) trunk blocks or infects every other developer. Green trunk is what makes shared continuous integration safe rather than chaotic.
Why do teams adopting TBD often see better DORA Metrics like deploy frequency?
Small, safe, flag-hidden commits decouple deploy from release and shrink integration debt, so shipping becomes routine and low-risk rather than a rare, scary event.

Edge cases

What happens to as , and what does that model in real life?
It goes to : a branch that lives no time drifts past no changes, so it cannot conflict. This is the mathematical limit that justifies "merge early, merge often."
What happens to as ?
It approaches (as long as and ): given enough time, some diverging change will eventually overlap your edits, making a conflict near-certain. The exponential decay of "clean" merges guarantees it.
Degenerate case: if (no change ever overlaps your lines), what does the model say?
for all — no overlaps possible means no conflicts, regardless of branch lifetime. It's the "everyone edits disjoint files forever" fantasy.
Boundary case: a one-person project — does workflow choice still matter?
Integration conflicts with others vanish ( from teammates is ~0), so the merge-pain argument nearly disappears. Workflow then matters mainly for release discipline (tags, hotfix path), not for avoiding collisions.
Edge case: a TBD feature flag is left on with dead old-path code for a year — is that fine?
No — that's flag debt. Flags are meant to be temporary; stale flags rot into untested dead branches of logic and hidden bugs. Retiring flags after release is part of the discipline.
Edge case: you must support versions 1.x, 2.x, and 3.x simultaneously for different customers.
This favours Gitflow-style long-lived branches (or maintenance branches) because you genuinely need parallel supported lines, each independently hotfixable. Pure single-trunk TBD assumes one live version and struggles here.
Boundary case: what if your CI is slow or flaky under TBD?
TBD's safety collapses — if tests take an hour or fail randomly, a broken trunk isn't caught "in minutes," so developers integrate on top of breakage. Weak CI is the classic signal that a team isn't ready for trunk-based.