Trunk-based development

One long-lived branch. Work happens on short-lived branches that live for hours or a day or two, and merge back with a review. The branch is always deployable.

For a website this is almost always the right model. Content changes are small, independent and low-risk, and the main cost of a heavier model — merge conflicts in long-running branches — is pure waste when the changes were never going to interact.

The whole workflow

git switch -c content/autumn-services-update
# edit, commit
git push -u origin content/autumn-services-update
# open a pull request, review the preview, merge, delete the branch

Release branches

Work merges to the trunk continuously, and a branch is cut when a release is prepared. Fixes are made on the release branch and merged back.

This exists to solve a problem websites rarely have: shipping a coordinated version to an audience that cannot be updated afterwards. If you can publish a fix in four minutes, you do not need a release branch, and maintaining one costs you a permanent merge-back obligation that people forget.

Environment branches

Branches named development, staging and production, each wired to an environment, with changes promoted by merging forward.

It is intuitive and it is the model that causes us the most trouble in practice. The environments drift, because a hotfix applied to production is merged back inconsistently. Promotion becomes a merge with conflicts, which means the thing promoted is not quite the thing tested.

Watch out

If you need environment promotion, promote an artefact rather than a branch. The artefact is immutable; a branch is not. This gets you the governance without the drift.

Comparing them

Branching models compared
Model Suits Fails when
Trunk-based Small teams, frequent small changes Review is slow, so branches live for weeks
Release branches Versioned, coordinated releases Merge-backs are skipped
Environment branches Nothing we would recommend for a website A hotfix goes straight to production

Conventions that matter more than the model

Whichever you pick, a handful of conventions do most of the work:

  • Protect the deployment branch. No direct pushes, review required, status checks required.
  • Name branches by intent. content/, fix/, feature/ — enough to read the repository at a glance.
  • Squash on merge. The trunk history becomes one entry per change, which makes both review and revert straightforward.
  • Tag what you publish. A tag turns "what was live in June" from an archaeology exercise into a lookup.
  • Delete merged branches. A branch list of ninety entries hides the four that matter.

Note

Squash merging discards the intermediate commits on the branch. If your team relies on granular history for bisecting, prefer a merge commit and keep the branch tidy instead. The important thing is that everyone does the same one.

Reverting is the safety net

The reason to keep the trunk clean is so that git revert on a single squashed commit is a complete, reviewable undo. Combined with a pipeline that deploys on merge, the recovery path for a bad publication is a two-minute pull request rather than a conversation about what to do.

That is the actual argument for all of this. Branching models are not about tidiness; they are about how quickly you can undo something in front of an audience.