Skip to content
CAMPUX Cloud Bootcamp Phase Three · Class Nineteen
Phase Three — DevOps Core
4 drills · 2 situations · 2 Labs
Aligned to AZ-400
Class Nineteen

Branching strategies

A branch is a place to work, not a release plan. Learn how small changes reach production, how teams patch a live version, and when a separate release branch earns its cost.

Phase Three · DevOps Core · Class 19 of 42 · 30 min · Assumes Class 17 and Class 18

After this you can choose a branching model for a team and say what trunk-based development costs and buys.

New to this? Read Class 17 and Class 18 first. Already done them, or know the material? Carry on.

● Screen walkthrough Not yet recorded · ~6 min
Reel · 00:00 / 06:00

Branch, merge, and resolve a conflict live — the red text everyone panics at, fixed calmly.

Placeholder — the class below stands alone until the reel lands
§1

The question every team fights about

You now know how to make a branch and how to guard main. The next question is how should branches be arranged over time? When do you cut one, how long does it live, and how does its work reach production? This is a branching strategy — a team's agreed way to manage changes. Start by asking how often the team ships, then ask which versions it must support and how it tests releases.1

Branching strategy
A team's shared rules for how branches are created, how long they live, and how their work is integrated and released. Not a Git feature — a social contract the tooling then enforces.

Deployment frequency means how often a team puts a new build in production. A team shipping daily needs to integrate small changes often. A team maintaining several supported versions may also need a branch for each version it patches. Release cadence, supported versions, test automation, and approval requirements all affect the choice. This class compares GitFlow and trunk-based development so you can explain the tradeoffs, not pick a winner from a diagram.

§2

GitFlow: what it solved in 2010

In 2010 a developer named Vincent Driessen published a branching model that became so popular it got a name: GitFlow. To understand why it looks the way it does, remember the world it was born into. Software shipped on a schedule — versioned releases, every few weeks or months, often installed by customers rather than deployed by you. In that world you needed somewhere to stabilise the next release while ongoing work continued, and somewhere to patch the version already in the wild. GitFlow gave each of those a long-lived branch.

main
Holds only released, tagged versions — the history of what actually shipped.
develop
The long-lived integration branch where finished features accumulate between releases.
feature/*
One per feature, branched off develop and merged back when done — often living for weeks.
release/* and hotfix/*
A branch to stabilise the next version, and a branch to patch the current one in production.

For its moment, this was genuinely good engineering: it gave a versioned, ship-it-in-boxes product a clear place for every kind of work. But look at what it costs. Feature branches live long, so they drift far from develop and merge back in painful, conflict-heavy heaves. There are five kinds of branch to keep straight and a ceremony for moving between them. All of that friction is the price of decoupling "written" from "released" — and that price only makes sense if you release rarely. The moment your team wants to ship every day, GitFlow's careful machinery becomes the thing slowing you down.2

§3

Trunk-based: the straight line most cloud teams run

The modern default for teams that deploy often is almost the opposite of GitFlow. In trunk-based development there is one long-lived branch — the trunk, usually main — and everyone integrates into it constantly. You still use branches, but they are short-lived: a branch cut this morning, opened as a PR, reviewed, and merged back the same day. Nothing is allowed to drift for weeks. The trunk is kept releasable at all times, and shipping is a matter of deploying the trunk, not of merging a pile of aged branches together.

Figure 1: two branching strategies side by side. On the left, GitFlow, with several long-lived lanes — main, develop, and feature branches — whose merges tangle together, with a red pen circle around a knot of conflicting merges. On the right, trunk-based development, a single straight trunk with small short-lived branches that pop off and merge back almost immediately. GitFlow — long-lived lanes main dev merge conflicts pile up Trunk-based — one line trunk always releasable
Figure 1 The same work, two shapes. GitFlow's long-lived lanes decouple "written" from "released," but the longer a branch lives the further it drifts, and the merges knot together where the red pen circles. Trunk-based keeps one line and lets branches live only hours, so integration is small and constant instead of large and dreaded. Neither is wrong — but only one stays sane when you ship every day.

Why does short-lived win for cloud teams? Because the pain of merging grows with the age of a branch, and trunk-based simply refuses to let branches get old. Small, frequent integrations mean small, frequent conflicts — the kind resolved in a minute, not a war. It leans hard on the machinery you already have: a protected trunk, required reviews, and automated checks on every PR, so that constant integration does not mean constant breakage. Feature flags do the rest, letting unfinished work sit merged-but-hidden on the trunk rather than marooned on a branch.3 The whole approach is built for the team that deploys many times a day and cannot afford a merge ceremony each time.

One more name belongs in your vocabulary. GitHub Flow is GitHub's simple branch-and-PR workflow: create a branch from main, make a change, open a pull request, review and merge it. The team then decides when and how to deploy. This workflow and trunk-based development can look similar; the useful distinction is whether changes are integrated in small pieces frequently and whether the trunk is kept releasable. A PR by itself does not guarantee either.

What "protected" actually means

Trunk-based development only stays sane because the trunk is not a branch anyone can push to on a whim — it is protected, and the protection is a set of rules the platform enforces on every attempt to change it. On GitHub these are branch protection rules (Azure DevOps calls the same thing branch policies); the names differ, the ideas are identical, and an interviewer expects you to name them without notes.

Require a pull request
No direct pushes to the trunk. Every change arrives through a PR, so every change has a place to be reviewed and a record of why it went in.
Require approvals
At least one reviewer — often two, or a specific owner — must approve before merge. This is the human gate, and it is why nobody merges their own unreviewed work into what production ships.
Require status checks
The build, the tests, the linter, the security scan must pass on the PR before the merge button lights up. The machine gate — a red check blocks the merge, not just frowns at it.
Require up-to-date branches
The branch must be current with the trunk before merging, so checks run against what will actually land, not against a stale starting point.
CODEOWNERS
A file mapping paths to required reviewers, so a change under /infra automatically pulls in the platform team. Ownership becomes policy, not a favour you remember to ask.

These are controls you can choose and configure, not guarantees that appear automatically when a team says "trunk-based." In your solo practice repo from Class Eighteen, you may require a PR and available checks while setting required approvals to zero until another reviewer exists. On a team, require the reviews and checks its risk calls for, inspect who may bypass the rule, and test the protection. A ruleset can block a direct push to main; it cannot prove the code is correct. Keeping the trunk releasable still takes small changes, useful tests, and a rollback plan.

§4

Releases and environments

"Ship the trunk" raises an obvious question: ship it where, and how do you gain confidence before it reaches customers? One common answer is environments: build a tested commit once, then promote that build through dev, test, and production. Production may still run an earlier commit while a newer build is being checked. The deployed commit or release tag tells you what is live; the branch name alone does not.

Table 1 — Two ways to reach production
GitFlow-styleTrunk + environments
Where work stabilisesOn a long-lived branch (develop, release/*)In running environments (dev → test → prod)
What differs between stagesDifferent branches of codeThe same build, different config
How a fix reaches prodMerge across branches, in orderMerge to trunk, promote the build forward
Failure modeBranches drift; merges rotA bad build is caught in an early environment

The distinction to hold is promotion, not re-branching. A change merges once to the trunk, produces one artifact, and that same artifact is deployed to dev, then to a test or staging environment, then — behind an approval gate — to production. The code does not change as it moves; only the environment it runs in and the configuration it reads do. Some teams keep a thin release/* branch for a slower cadence or a supported version, and that is a legitimate hybrid — but the everyday flow is trunk to artifact to environments. This is exactly the shape Build III will construct: a pipeline that deploys the trunk to non-prod on merge and waits for a human to approve production. Environments, not branches, are where release confidence is earned.

Figure: promotion, not re-branching. On the left a short trunk with three commits produces, via a build-once arrow, a single artifact box. The same artifact then moves right through a dev environment box and a test environment box, passes an approval gate drawn across the arrow, and lands in prod. A note underneath records that the code does not change as it moves — only the environment and its configuration do. trunk build once one artifact dev config only test config only approval prod customers the code never changes as it moves — only the environment does
Figure — Promotion, not re-branching One merge, one build, one artifact — then the same bits move right through dev and test and wait at a human approval before prod. GitFlow stabilised on branches of code; this stabilises in environments of runtime, and the trunk stays the single source of truth. This is the exact shape Build III constructs.

The same machinery handles a production hotfix. First identify the commit or release tag that is actually deployed. If production runs current main, a short-lived fix branch can start there, go through a fast PR, and follow an expedited, tested promotion path. Teams may call that branch hotfix/*; the name does not make it a permanent lane. If production runs an older release, patch that supported version and bring the fix forward too. The point is to isolate the change and verify the build you deploy, especially during an incident.

When a release branch still earns its keep

None of this makes the release/* branch obsolete — it makes it a specialist tool rather than the everyday spine, and the distinction is exactly what a good answer draws. The honest case for a release branch is supporting more than one version in the field at the same time. If Campux's platform is only ever the single thing running in Azure, there is nothing to support but the trunk, and a release branch is pure overhead. But the moment you ship software that customers install and pin — a big client stays on v2 for a year while the trunk races ahead to v3 — you need a living line where v2 can receive security fixes without inheriting v3's new behaviour. That line is a release branch, cut at the release and kept alive as long as the version is supported.

Fixing a bug in that world means writing the fix once on the trunk and then backporting it to each supported release branch, which is what git cherry-pick is for: it copies one specific commit onto another branch without dragging along everything around it. The cost is real and worth stating plainly — every supported version is a branch you must test, build, and patch, so two supported versions is roughly double the release work and three is triple. That arithmetic is the whole reason cloud teams, who run exactly one version of their own service, avoid release branches entirely, while a vendor shipping installed software cannot. Choose the branch when you are genuinely carrying versions; refuse it when you are not — and be able to say which situation you are in, because that sentence is the difference between an engineer and a ritual.

Play

Play it through

Four minutes, two things to do. Checkout is down, and a develop branch and a feature branch are both mid-flight. Cut the hotfix from the one that is actually live, ship it, then bring the fix home. It plays on its own and stops when it needs your hands.

§5

The machinery, and the bill

Everything so far has been about the shape of history. The rest of what a senior engineer is expected to say lives underneath that shape: how you hold "shipped" apart from "shown to a customer," how you give a release a durable name, how you keep a busy trunk from breaking under many hands at once, and what all of it costs the pipeline in minutes. None of these is a branching strategy on its own — they are the parts that make trunk-based actually survive contact with a real team.

Deploy is not release

Start with the sentence that reorganises how you think about shipping: deploying code and releasing a feature are two different events. Deploy means the bits are running in production. Release means a user can see the behaviour. §4 kept them apart with environments; a feature flag keeps them apart within production itself — a runtime switch that lets code sit deployed but dark until you choose to turn it on. This is the mechanism behind the answer you gave in Situation 02: unfinished work merges to the trunk switched off, so "on main" never means "live to customers." The branch strategy stops mattering the moment the flag, not the merge, decides what a user sees.

Feature flag
A named runtime switch, read from configuration rather than compiled into the code, that turns a code path on or off without a redeploy. It moves the release decision out of the pipeline and into an operator's hands.

On Azure the sanctioned home for these switches is App Configuration's Feature Manager — a central store where a flag is data, not code, so flipping it takes effect without rebuilding or redeploying anything. Its value is not the on/off; it is the targeting: turn a feature on for internal users first, then for one region, then for five percent of stores, then for all. That progressive turn-up is exactly what deployment strategies like canary and blue-green rollouts are built on, which a later class treats in full4 — and it is only possible because the code was already deployed, waiting behind the flag. The flag, not a branch, is where release risk is now managed.

Semantic versioning: giving a release a name

"Deploy the trunk" is incomplete until someone asks which exact commit is running right now. Branch pointers move. A release tag can label a specific commit, but tags can also be moved or deleted by someone with permission, so protect them and check deployment records. Many projects use semantic versioning: MAJOR.MINOR.PATCH describes intended compatibility. The number is a claim by the project, not proof of what production deployed.

Semantic versioning
A convention for release numbers — MAJOR.MINOR.PATCH — whose positions communicate the project's intended compatibility. Check the change and your tests before upgrading.
MAJOR
Bumped for a change that breaks compatibility — callers must alter their code. 1.9.0 to 2.0.0 is a warning, not a routine update.
MINOR
Bumped for a backward-compatible feature — new behaviour that does not break the old. 2.0.0 to 2.1.0.
PATCH
Bumped for a backward-compatible fix — the hotfix from §4 lands here. 2.1.0 to 2.1.1.

This is what turns §4's abstract v2 and v3 into real objects. The release branch that supports a pinned customer is cut at a tag — v2.4.0 — and every security fix backported onto it is another PATCH bump, v2.4.1, while the trunk races toward v3.0.0. The version string is doing the work of a whole conversation: a customer reading 2.4.1 knows it is their line, patched, with nothing new to fear. For a cloud team running one version of its own service the tag is mostly a receipt — what shipped, when, so an incident can name it — but even a receipt is worth having at three in the morning.

The merge queue

Branch protection from §3 checks each pull request against the trunk on its own. That is enough until the trunk gets busy, and then a subtle failure appears: two PRs are each green against main separately, yet break the moment both land, because neither was ever tested against the other. One renames a function; the other adds a caller of the old name; both pass; the merged trunk does not build. At a few merges a day you shrug and re-run. At forty an hour, the trunk is broken more often than not, and "always releasable" quietly becomes a lie.

A merge queue is the fix, and it is worth being able to describe. Instead of merging approved PRs immediately, the platform lines them up and tests each one against the trunk plus the PRs already ahead of it in the queue — the exact state it will create. Green, and it merges in order; red, and it is evicted from the queue without ever touching the trunk, so the offending pair is caught before it lands rather than after. GitHub offers this as a built-in merge queue; Azure DevOps has no direct equivalent, so teams there approximate it with stricter build-validation policies and a habit of merging one PR at a time on a busy trunk. Where a real merge queue exists, it is the piece that lets many people integrate into one trunk all day and keep the "protected, always releasable" promise literally true — the machine version of the discipline §3 described.

What the strategy costs your pipeline

A branching strategy is also a bill, paid in pipeline minutes, and pretending otherwise is how a team is surprised by its own invoice. Trunk-based produces many small pull requests, so the pipeline runs often — but each run is small and fast, and feedback arrives in minutes. Long-lived branches invert it: fewer runs, but each is a monster, and a break blocks everyone downstream. A merge queue buys a trustworthy trunk by spending more compute, because every candidate is retested against the queue ahead of it. There is no free option here; there is only knowing which one you are buying.

Table 2 — What each choice costs the pipeline
ChoicePipeline minutesTrunk stabilityFeedback speed
Long-lived branchesFewer runs, each largeFragile — a stale merge can break it for everyoneSlow — problems surface at merge, weeks late
Short-lived + trunkMany runs, each smallGood — small changes, fast to revertFast — minutes per PR
Add a merge queueHighest — every candidate retested in orderHighest — the trunk is tested as it will landSlightly delayed by the queue, but reliable

The tuning is real engineering, and it connects straight to money: the runner minutes you spend here are the FinOps line a later class prices, and the way you shave them — path filters so a docs change does not rebuild the world, caching, right-sized runners — is a skill in itself. But do the cheap thing first. A team drowning in CI cost usually has branches that are too long and PRs that are too big, not a runner that is too small; the batch size you chose in §1 is upstream of the bill you pay here.

§6

Choose by frequency, not ideology

Now the decision, made honestly. You do not pick a branching strategy because it is "enterprise" or "what Google does" — you pick it by asking how often your team ships, then working backward.

Choose by frequency, not by fashion.

Ship many times a day
Trunk-based with short-lived branches and environment promotion. Long-lived branches would knot; you cannot afford a merge ceremony per release.
Ship on a slow, versioned cadence
A GitFlow-style model, or trunk plus a release/* branch, earns its overhead — you genuinely need to stabilise a version and patch old ones.
Somewhere in between
Most teams: trunk-based as the spine, with a light release branch only where a supported version demands it. Start simple; add branches only when a real need appears.

The failure mode to recognise in yourself and others is cargo-culting — adopting GitFlow's five-branch dance for a team that deploys daily, or forcing bleeding-edge trunk-based onto a team shipping firmware once a quarter. Both are the same mistake: choosing by identity instead of by deployment frequency. For most cloud infrastructure work — which is where you are headed — the honest answer in an interview is "trunk-based development with short-lived branches, protected trunk, and promotion through environments," because that is what deploying infrastructure many times a day actually requires. Know GitFlow well enough to explain what it solved and why it fell out of favour; that pairing — knowing both, and knowing why you chose one — is what separates an engineer from someone reciting a blog post.

Case File · Campux Retail

Picking the flow the platform will live by

campux-platform goes trunk-based

Campux's new lead, fresh from a big enterprise, opens with "we should run GitFlow — it is what serious companies use." You ask the question this class is built on: how often will we ship? The answer, for infrastructure that changes almost daily, settles it. GitFlow's develop and long release branches would have the team merging aged, conflicting changes into campux-platform constantly — the exact tangle Figure 1 circles in red. Campux commits to trunk-based: one protected main, branches that live hours not weeks, and every change reviewed on the way in.

Release confidence moves off branches and onto environments. The trunk builds one artifact; that artifact deploys to a non-prod environment on merge, and reaches production only behind a human approval. This is the shape Build III will implement for real — and it is a decision made on numbers, not on whose last employer did what. When the lead asks how they will support an older version if a big client ever pins one, you keep the door open: a thin release/* branch, added only if and when that need actually arrives.

Watch · Microsoft Learn

The official module, and a CAMPUX overview

Read or work the module first; watch the overview to see it move
Microsoft Learn · Module

Design and implement branch strategies and workflows
learn.microsoft.com/training/modules/manage-git-branches-workflows/

GitHub Docs · Understanding the GitHub flow
docs.github.com/get-started/using-github/github-flow

CAMPUX overview video

A short walkthrough of GitFlow's tangle versus a trunk-based straight line, and how environments replace release branches, will live here. Video to be added.

Lab 1 · Trunk-based flow

Short-lived branch, in and out the same session

~8 minutes · the campux-platform repo · one protected trunk

You cut the hotfix from main above. Now do it for real. Feel the trunk-based loop at its natural speed: branch, change, PR, merge, delete — all in one sitting. The point is that the branch never gets old enough to drift.

  1. Start from an up-to-date trunk, then cut a short-lived branch:

    git switch main
    git pull
    git switch -c add-contributing
    What just happened: you branched from the latest main. Because you pulled first, the branch starts life with zero drift — the ideal it will try to keep by not living long.
  2. Make one small change and push the branch:

    echo "## Contributing" >> README.md
    echo "Branch, PR, review, merge, delete." >> README.md
    git commit -am "Document the contribution flow"
    git push -u origin add-contributing
    On screen: GitHub prints a link to open a PR. One focused change on a fresh branch is exactly what a reviewer can read in minutes.
  3. Open the PR, inspect the changed lines, wait for any configured checks and reviews, and merge it — then delete the remote branch when GitHub offers. In your solo Class Eighteen repo, required approvals should still be zero; do not expect to approve your own PR.

    What to notice: the branch existed for minutes and is now gone. Its work lives on the trunk; the PR keeps the history. Nothing is left to rot.
  4. Bring your trunk up to date, then remove the branch on your own machine too — GitHub's delete button only removed the remote copy:

    git switch main
    git pull
    git branch -d add-contributing
    git branch
    What to notice: -d checks whether the branch's commits are ancestors of main. After a squash or rebase merge, GitHub may have created different commit IDs, so -d can refuse even though the PR is merged. Verify the PR and its change are on main; only then use git branch -D add-contributing to remove your local copy. Other local branches from earlier labs may still appear. The lesson: that is the whole rhythm — start fresh, change small, integrate fast, delete on both ends. Repeat it many times a day and merges never become events.
Lab 2 · The cost of drift

Make a long-lived branch bite you

~10 minutes · separate local practice repo · no remote pushes

Now feel how branches can conflict. Make a disposable local repository so the protected main from Class Eighteen stays untouched. Two branches will change the same line; after one is merged, updating the other will require a choice. A conflict can happen quickly too — age raises the chance of overlapping work, but does not cause every conflict.

  1. In a new folder outside your campux-platform repository, create a local practice repo and two branches that will touch the same line:

    mkdir branch-conflict-practice
    cd branch-conflict-practice
    git init -b main
    git config user.name "Practice Learner"
    git config user.email "practice@example.invalid"
    echo "# Campux Platform" > README.md
    git add README.md
    git commit -m "Start conflict practice"
    git switch -c edit-title-a
    echo "# Campux Platform A" > README.md
    git commit -am "Title edit A"
    What just happened: branch A rewrote line one. Keep it unmerged for a moment — it is about to become the "old" branch that drifted.
  2. Make a competing change reach the trunk first, via its own branch and merge:

    git switch main
    git switch -c edit-title-b
    echo "# Campux Platform B" > README.md
    git commit -am "Title edit B"
    git switch main
    git merge edit-title-b
    On screen: this local main now says "B" on line one. Branch A still says "A". No change was made to the protected GitHub repo.
  3. Try to bring branch A up to date — and hit the conflict:

    git switch edit-title-a
    git merge main
    On screen: CONFLICT in README.md. Git cannot know whether line one should read A or B, so it stops and hands you both, wrapped in <<<<<<< markers.
  4. Resolve it by editing the file to the line you actually want, then finish the merge:

    echo "# Campux Platform" > README.md
    git add README.md
    git commit --no-edit
    What happened: replacing the whole file removed the conflict markers because this practice file contains only one line. In a real file, open it in an editor, keep the intended content from both sides where needed, remove all markers, then stage and commit. Shorter branches reduce the amount of change you must reconcile. You can leave this disposable folder for later practice.
Note · Run this from a folder that is not already inside another Git repository. The commands use only local Git and simple file writes; they work in PowerShell, Git Bash, and common Unix shells.
Think in systems

Zoom out: a branching model is a promise about how often you integrate pain

You can run trunk-based or a release-branch flow. Now reason about what the model does to the whole team, because a branch is deferred integration, and deferred pain compounds.

Feedback loops

Long-lived branches defer merge pain, so they diverge, so the eventual merge is a weekend of conflicts nobody wants to own — which encourages even longer branches. What shrinks the batch back down?

Dependencies & coupling

main is the shared truth everyone depends on; a broken main blocks the whole team, so the model exists first to keep it releasable.

The constraint

Integration frequency is the real lever; the strategy is just how you make small, frequent, reviewed merges the path of least resistance.

Second-order effects

A model tuned for a library’s stability — release branches, careful merges — suffocates a web app that wants to ship daily, and the reverse is equally true.

What breaks when it scales

A flow one team coordinates verbally needs written protection and automation before ten teams share the same main without treading on each other.

The engineer who ships is asked "did it branch clean?" The engineer who gets promoted is asked "and how long until it rejoins the truth?" — and kept the batch small.

On the job

How the team avoids a Friday

You · Cloud Engineer · two features are colliding

Two people are building on top of each other and stepping on toes. You untangle it with a branching strategy that fits the team — short-lived branches off main, merged small and often — so integration happens continuously instead of in one dreaded big-bang merge the night before release. The strategy is not dogma; it is how the team avoids a Friday.

Class Nineteen

Examination

Four drills, then two situations. The situations have no marking scheme — write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored; this is between you and the page.

Drill 01Recall · strategy matching
A team deploys infrastructure changes many times a day. Which branching strategy fits, and why?
Marked

A. High deployment frequency is the fact that decides it: long-lived branches drift further the longer they live, so a team shipping daily under GitFlow spends its life reconciling aged branches. B chooses by prestige, not frequency — the exact cargo-cult this class warns against. C is GitFlow's failure mode taken to its extreme. D throws away review and protection entirely. Trunk-based keeps branches too young to drift, which is what shipping many times a day actually requires.

Drill 02Recall · environments
In a trunk-based team, how does a change gain confidence before it reaches customers?
Marked

B. Trunk-based moves release confidence off branches and onto environments: the trunk produces one artifact, and that same artifact is promoted forward, changing only its configuration, until a human approves production. A and C are the branch-based habits it replaces; D is the dangerous version — building a different artifact per environment means you never actually tested what you shipped. Promotion, not re-branching, is the idea to hold — and it is the exact shape Build III implements.

Drill 03Select three
Which three are true of trunk-based development?
Marked

Short-lived branches, feature flags for hidden work, and small frequent conflicts. The two false options are the common misreadings: trunk-based is not "commit to main with no review" — it leans harder on protection and checks than any other model, because constant integration would be constant breakage without them. And it does not remove the protected trunk; the protected trunk is precisely what makes it safe to integrate all day. Short branches plus strong guardrails, not a free-for-all.

Drill 04Spot the error
A developer describes their plan for a big feature. One line is the decision that will hurt most. Which?
FEATURE PLAN — as proposed

1.  Cut a feature branch off main today
2.  Build the whole feature over the next 6 weeks on it
3.  Keep it off main until it is 100% finished and perfect
4.  Rebase on main at the very end, then open one big PR
5.  Merge the ~40-file branch in a single review
Marked

Line three. The six-week isolation is the decision every other problem flows from. While the branch sits off main, the trunk moves under it every day; by the end it has drifted so far that the "rebase at the very end" in line four is a minefield and the "single review" in line five is a rubber stamp on forty files nobody can hold in their head.

The fix is not a better merge — it is to never let the branch get old. Slice the feature into small pieces that each merge within a day, hidden behind a feature flag until the whole thing is ready. The work still takes six weeks; it just integrates continuously instead of colliding all at once. Branch age is the enemy, and line three is where the team chose to make it their enemy.

Situation 01Write before you reveal
A new team lead announces the team will adopt GitFlow "because that's what serious enterprises do." Your team deploys infrastructure changes several times a day. You think it is the wrong fit. Make your case — then say what you do once the decision is made.
A strong answer argues on the number, not on taste — and knows when the arguing ends.
Reasoning

Argue on frequency, not identity. Do not say "GitFlow is old" or "trunk-based is better" — those are taste, and taste loses to a lead. Say the number: we deploy several times a day, and GitFlow's long-lived develop and release branches assume a slow, versioned cadence we do not have. Name the concrete cost — daily merges of drifting branches, the exact tangle we are trying to avoid — and offer the alternative in the same breath: trunk-based with a protected trunk and environment promotion, which gives the same safety GitFlow was reaching for, at our speed.

Bring evidence, not just opinion. Point at how the team actually ships today, and if you can, at what GitFlow would have cost on a recent change. The case is strongest when it is about this team's deployment frequency, not a blog post either of you read.

Then commit to the decision, whichever way it lands. If the lead still chooses GitFlow, you implement GitFlow well — disagreeing and committing is a senior trait, not a betrayal of your view. You made the argument on the merits; now the team needs one strategy executed cleanly, not two half-followed. Revisit it later with data if the pain you predicted shows up. Being right is worth less than being someone the team can decide with.

Situation 02Write before you reveal
A stakeholder hears you are moving to trunk-based development and objects: "So half-finished code will be sitting on main — won't that ship broken features to our customers?" How do you answer?
The fear is real; the premise that "on main" equals "in front of customers" is the thing to separate.
Reasoning

Concede the fear before you dissolve it. The worry is legitimate — unfinished work reaching customers would be bad. The move is to separate two things the objection has fused: being merged to main and being visible to a customer. Trunk-based keeps them apart deliberately, so "on main" never means "live to users."

Name the two mechanisms. First, feature flags: unfinished work merges to the trunk switched off, present in the code but dark to users until we choose to turn it on. Second, environments: the trunk deploys to dev and test long before production, and production sits behind an approval gate, so nothing reaches customers unreviewed. Merged, tested, and released are three different events.

Turn it back into the benefit. This is actually safer than the alternative: a giant feature branch merged after six weeks lands as one big, poorly understood change, while small flagged increments are each reviewed and integrated continuously. Customers see a feature the day we flip the flag — not a moment sooner — and by then it has been on the trunk, tested, the whole time. The stakeholder wanted safety; trunk-based with flags is how you give it to them.

Examination record · first attempt
0/4
Class Nineteen · Complete
Retain this much

Five things worth carrying out of this class

  1. Start with how often you deploy, then ask which versions you support and what release checks you need.
  2. GitFlow's long-lived lanes (main, develop, feature, release, hotfix) suit a slow, versioned cadence; the price is drift and painful merges.
  3. Trunk-based keeps one protected trunk and short-lived branches, so integration is small and constant instead of large and dreaded — the modern default for cloud teams.
  4. Release confidence lives in environments, not branches: promote one build through dev → test → prod. Feature flags keep unfinished work merged but hidden.
  5. Explain a branching choice using the team's actual release and support needs, not a preferred diagram.
Notes
  1. "Deployment frequency" is not a slogan — it is one of the four DORA metrics, the research-backed measures of software delivery performance (alongside lead time for changes, change failure rate, and time to restore service). The finding that elite teams both deploy far more often and fail less is what retired the old assumption that speed and safety trade off. Trunk-based development is strongly associated with the high-performing end of that data. Treat the exact rankings with a little suspicion — they are survey-based — but the direction is well replicated. ↩
  2. GitFlow's own author, Vincent Driessen, later added a note to his 2010 post urging readers not to treat it as a default — for teams practising continuous delivery on web apps, he now points them toward simpler, trunk-based flows. This is a healthy thing to cite in an interview: the model is not "wrong", it is a good answer to a question fewer teams are asking. Knowing that history signals you understand the why, not just the diagram. ↩
  3. "Trunk-based development" is sometimes used loosely to mean "commit straight to main", which is a caricature that gives it a bad name. The disciplined version — short-lived branches, a protected trunk, PR review, and automated checks on every change — is the one the research and this class mean. If someone dismisses trunk-based as reckless, they are usually arguing against the caricature; the real practice is more guarded than long-branch models, not less. ↩
  4. Semantic versioning is a convention, not something Git or Azure enforces — nothing stops a team tagging a breaking change as a PATCH bump, and plenty do by accident. Its value is a shared promise, and a promise only holds if everyone keeps it; treat a project's version numbers as a claim to verify, not a guarantee, until you have watched the team honour them once. The canary and blue-green rollout mechanics gestured at here get their own full treatment in the later deployment-strategies material — this class only needs you to see that the flag is what makes them possible. ↩