4.5.9 · D4Software Engineering

Exercises — Git workflows — Gitflow, trunk-based development

3,305 words15 min readBack to topic

Before we begin, one shared piece of notation. Throughout the numeric problems we reuse the parent note's merge-conflict model:

The logic in one breath: each change avoids your lines with probability . To avoid all of them (independent events) you multiply: . That is the clean case. The conflict case is "not clean", so . Keep this picture — half the problems below are just this formula wearing a costume.

The figure below is that formula drawn as a curve — refer back to it whenever a problem asks how risk moves with branch lifetime .

Figure — Git workflows — Gitflow, trunk-based development
Alt text / what the figure shows: three cyan/amber/white curves of (vertical axis, 0 to 1) against branch lifetime in days (horizontal axis), one curve per value of . All curves start at the origin, rise steeply, then flatten toward 1. A labelled amber dot sits on the dashed 50% line at days for the curve.

How to read the figure: the horizontal axis is branch lifetime in days; the vertical axis is . Each coloured curve is one value of . Notice all curves start at 0 (the point of L1.3), rise steeply at first, then flatten as they approach 1 — the exact exponential shape L5.1 proves with calculus. The labelled amber dot on the dashed horizontal 50% line marks the crossing days from L3.2.


Level 1 — Recognition

L1.1 — Which two branches in Gitflow are permanent (never deleted), and which three are temporary?

L1.2 — True or false: "Trunk-based development means no branches are ever created."

L1.3 — In the model above, if a branch lives for zero days (), what is ? Compute it and say what it means.

Recall Solutions — L1

L1.1 Permanent: ==main and develop. Temporary: feature/*, release/*, hotfix/*==. (Mnemonic MAD-FRH from the parent note.)

L1.2 False. Trunk-based allows branches — it forbids only long-lived ones. Short-lived branches (hours to ~1 day) merged via small PRs are normal. The discipline is lifetime, not existence.

L1.3 Here . Substitute: A branch that lives no time has missed changes, so it cannot drift, so it cannot conflict — probability . This is the mathematical reason "merge immediately" is the safest possible strategy. On the figure this is where every curve touches the origin.


Level 2 — Application

L2.1 — A feature branch lives days. The shared branch receives conflict-causing changes/day, and each has chance of touching your lines. Compute to 3 decimals.

L2.2 — Same , , but a trunk-based branch lives only days (half a day). Compute and compare with L2.1.

L2.3 — Order these three Gitflow commands into a correct feature merge, and explain why --no-ff is used: git merge --no-ff feature/login · git checkout -b feature/login · git checkout develop

Recall Solutions — L2

L2.1 First find diverging changes, then plug into the formula. , so (about a 33% chance of a conflict).

L2.2 Now . — about 4%. Same team, same collision odds — only the lifetime (hence ) changed, and conflict risk dropped from 33% to 4%. That gap is the argument for CI-driven short branches.

L2.3 Correct order:

git checkout -b feature/login       # 1: isolate new work off develop
# ...commits...
git checkout develop                # 2: return to the integration branch
git merge --no-ff feature/login     # 3: merge, forcing a merge commit

What is a fast-forward? When develop has not moved since you branched, Git can merge by simply sliding develop's pointer forward onto your feature's last commit — no new commit is created, and the history becomes a single straight line as if the feature commits were made directly on develop. That is a "fast-forward" merge. --no-ff forbids that shortcut and forces Git to create an explicit merge commit instead. Why we want that: with a fast-forward the feature boundary vanishes — you can no longer see which commits belonged to one feature. The merge commit is a documentation anchor saying "these commits were one feature." See Branching and Merging.


Level 3 — Analysis

L3.1 — A team on Gitflow discovers a critical production bug in released version v1.2.0. A junior branches hotfix/1.2.1 off develop, fixes it, and merges only into main. Name two distinct things wrong with this and give the correct procedure.

L3.2 — For the model, find the branch lifetime at which first reaches 50%, given , . (Hint: solve using logarithms — the log answers "what exponent produces this value?")

L3.3 — Two teams have identical and identical branch lifetime days. Team A has well-modularised code so ; Team B has tangled code so . Compute both values and explain what the architecture bought Team A.

Recall Solutions — L3

L3.1 Two errors:

  1. Branched off the wrong base. A hotfix must branch off ==main==, because main is the exact live code that is broken. develop may contain unreleased, unstable work — building the patch on top of it risks shipping half-finished features to production.
  2. Merged into main only. It must merge into both main and develop. If you skip develop, the next release built from develop re-introduces the same bug — a regression.

Correct procedure:

git checkout main
git checkout -b hotfix/1.2.1
# ...fix...
git checkout main    && git merge --no-ff hotfix/1.2.1 && git tag v1.2.1
git checkout develop && git merge --no-ff hotfix/1.2.1

The tag v1.2.1 follows Semantic Versioning — a patch bumps the third number.

L3.2 We want , i.e. . Take the natural log of both sides (the log turns "unknown in the exponent" into "unknown multiplied" — that's why we reach for logs here): Solve for : Watch the signs carefully. Because , its logarithm is negative. And is also negative. A negative divided by a negative is positive, so comes out positive as a lifetime must: So a branch on this team crosses a coin-flip's worth of conflict risk at roughly 8.6 days (the labelled amber dot on the figure) — a strong argument for merging well before then.

L3.3 for both teams.

  • Team A: (≈11%).
  • Team B: (≈46%). Same , same , same — the only difference is , driven by code architecture. Good modularity (small ) roughly quartered the conflict risk. Clean boundaries mean fewer people editing the same lines, which literally lowers in the model.

Level 4 — Synthesis

L4.1 — A team ships once every 6 weeks, must support three older versions in production simultaneously, works in a regulated/audited industry, and has a weak test suite. Which workflow, and give three reasons tied to those facts.

L4.2 — A different team wants to merge a half-finished payment redesign into main today without exposing it to users, while still deploying main to production every afternoon. Sketch the mechanism (with a code snippet) and name the two technologies that make it safe.

L4.3 — Design rule: your team's CI test suite takes 40 minutes and is flaky (fails randomly ~15% of the time even when code is fine). You want trunk-based development. State the single biggest blocker and one concrete fix, referencing why trunk-based depends on this.

Recall Solutions — L4

L4.1 Choose Gitflow. Reasons:

  1. Scheduled 6-week releases map naturally onto release/* branches, where you feature-freeze and stabilise while develop keeps moving.
  2. Three supported versions need long-lived maintenance lines and clean hotfix/* paths per version — Gitflow's ceremony provides that traceability.
  3. Regulated/audited + weak CI: the explicit merge commits and branch structure give an audit trail, and the human review gates compensate for tests you can't fully trust. Speed isn't the constraint here — traceability is.

L4.2 Merge the incomplete code behind a feature flag — a runtime switch that keeps the new path turned off in production:

if flags.enabled("payment_redesign"):   # off in prod today
    run_new_payment()
else:
    run_old_payment()

Now the code lives on main and deploys every afternoon, but users never see it until you flip the flag. This decouples deploy from release. The two enabling technologies: Feature Flags (hide incomplete work) and Continuous Integration (catch a broken trunk within minutes so daily merges stay safe).

L4.3 Biggest blocker: a slow, flaky CI pipeline breaks the core promise of trunk-based development — that a broken trunk is caught in minutes and that green means green. A 15% false-failure rate trains developers to ignore red builds ("just re-run it"), so real breakages slip through. Concrete fixes (any one): quarantine/fix flaky tests until the suite is deterministic; parallelise the suite to cut the 40 min feedback loop; add a fast pre-merge subset (~5 min) that gates PRs. Trunk-based depends on a trustworthy, fast signal — without it you're forced back toward long branches and Gitflow-style gates.


Level 5 — Mastery

L5.1 — Derive from scratch why decays your safety exponentially, and use calculus to show that the marginal extra risk per day is largest when the branch is young. (Compute and interpret its sign and magnitude.)

L5.2 — A manager claims "we cut conflicts in half by improving code modularity, halving from to , on branches with , ." Verify whether halving actually halves . Compute both and comment.

L5.3 — Connect to DORA Metrics: explain, using the conflict model, why short-lived branches tend to improve both deployment frequency and change failure rate at once — two outcomes usually assumed to trade off against each other.

Recall Solutions — L5

L5.1 Start from the clean probability, with . Rewrite the base using the exponential, because the exponential is the one function whose rate of change is proportional to itself — exactly the behaviour "each day multiplies risk by the same factor." Using the identity : Since , we have , so the exponent is negative — this is exponential decay of safety. Now the conflict probability is one minus that: Differentiate with respect to (the chain rule brings the constant down out of the exponent): Interpreting the sign: the first factor is positive (because is negative and we negate it), and the second factor is always positive. So everywhere — conflict risk is always rising with branch lifetime; you never reduce risk by waiting. Interpreting the magnitude: the second factor is largest at (equal to 1) and shrinks as grows. Since the derivative is the constant times this shrinking factor, the derivative is biggest when the branch is young and tapers toward 0 as . In words: each extra day adds risk, but the earliest days add the most absolute risk — while there is still safety left to lose. Once risk is already near 1, extra days barely move it. This is the calculus behind "merge early" and matches the figure's steep-then-flat curve.

L5.2 Compute for both cases, then apply the formula.

  • Old (): (≈71%).
  • New (): (≈45%). Halving did not halve the conflict probability. The risk fell from 0.706 to 0.455 — a ratio of , i.e. about a 36% reduction, not the 50% the manager claimed. Because sits inside the exponent, the relationship is non-linear: you cannot scale the output by scaling one input. The improvement is real and worthwhile — just not a clean halving.

L5.3 Deployment frequency and change failure rate are two DORA Metrics usually treated as a trade-off (deploy faster → break more). The conflict model dissolves the trade-off because a single lever — short branch lifetime , which shrinks — improves both at once:

  • Deployment frequency ↑: small (small ) keeps near zero, so merges are trivial and integration is continuous. Trivial merges mean you can integrate and ship many times a day instead of batching up a risky release.
  • Change failure rate ↓: small also means each change is a small diff. Small diffs are easier to review, easier to test, and if something breaks, CI pinpoints the tiny culprit in minutes. Less code per deploy ⇒ fewer ways for a deploy to fail. Both metrics improve because both are downstream of the same quantity, . That is why elite teams score high on all four DORA metrics simultaneously rather than trading one for another — short-lived branches are a common cause, not a compromise.

Recall Final self-check — one line each

Longest-safe rule of thumb ::: Keep small; conflict risk decays exponentially, so a half-day branch is dramatically safer than a five-day one. Hotfix base and merge targets ::: Branch off main; merge into both main and develop. The prerequisite for trunk-based ::: Strong, fast, trustworthy CI that keeps trunk green. What halving does to ::: Reduces it non-linearly (only ≈36% at ) — because sits in an exponent. The two extremes of ::: ⇒ never conflicts; ⇒ certain conflict once any change lands.