ADR-0004: Adopt Trunk-Based Development on a Single Main Branch
Status: Accepted
Date: 2026-05-15
Deciders: Team
Context
The repos used three long-lived branches — dev, test, main — as a promotion pipeline, each tied to an environment. At our scale the overhead outweighs the isolation it buys:
- Promotion overhead. Every change crossed two extra merges (
dev → test → main), bundling many changes per release. - Branch drift. The branches diverged; conflicts surfaced at promotion time, and "works on
dev, breaks ontest" was recurring. - Unclear deployed state. No single ref told you what was running in prod vs. staging vs. integration.
- Hotfix friction. Urgent fixes hit
main, then had to be back-merged todevandtest— a manual step that was easy to forget. - Tooling already assumes
main. Docs deploy frommainvia Cloudflare Pages (see ADR-0003) and the verified-field stamper runs on merge tomain. The three-branch model added ceremony around amainthe tooling already treated as source of truth.
Decision
Collapse the three branches into a single trunk: main.
mainis the only long-lived branch and must always be releasable.- All work happens on short-lived feature branches off
main, merged back via pull request only — no direct pushes. - A PR merges only when required CI checks pass and at least one review approves, enforced by branch protection.
- Feature branches live days, not weeks.
Alternatives Considered
- Keep three branches — rejected: the overhead, drift, and hotfix friction are inherent to the pattern and duplicate a gate the
main-keyed automation already provides. - Two branches (
dev+main) — rejected: halves the overhead but doesn't remove it. The problem is having more than one long-lived branch, not the count. - Trunk plus short-lived release branches — deferred: a known escape hatch if
mainever needs to freeze for a release. No current need; addable later without reversing this decision.
Consequences
devandtestare deleted; branch protection onmainenforces no direct pushes, passing CI, and one approving review.mainmust always be releasable. CI is now the only gate between a change and a deployable trunk. Test coverage and check reliability are a prerequisite, not a follow-up.- QA no longer has a
testbranch. Pre-production verification must move to PR preview deploys, ephemeral environments, or feature flags. Open item: a follow-up should record how pre-prod testing is handled post-migration. - Environment promotion is no longer expressed by branches. Mapping a commit to an environment needs an explicit mechanism — tags, releases, or deploy config.
- Incomplete work reaches
mainearlier. Short-lived branches require feature flags or small, independently shippable changes. - Docs pipeline, Cloudflare Pages deploy, and verified-field stamper now align with the branching model.
- Hotfixes apply directly to
mainthrough the normal PR flow, with no back-merging.