Trunk-Based Development, TDD & Pairing
This is how we work, not a set of options to pick from. We integrate to a single trunk often, behind short-lived branches. We drive code with tests (TDD). We prefer pairing over slow, asynchronous pull-request queues. The goal is fast feedback: small changes, a main branch that is always green, and review that happens while the code is being written, not days later.
Long-lived feature branches, large merges, and review queues that take days slow the team down. They hide work, cause painful merge conflicts, delay feedback until it is expensive to act on, and let main drift away from what people are building. Trunk-based development, test-driven development, and pairing share one goal: shorten the gap between writing code and knowing it is good.
These are deliberate team standards. They keep the codebase always integrable and always releasable (see CI/CD & Deployment). They turn tests into a design tool, not an afterthought (see Testing Strategy). And they make review a live conversation, not a bottleneck (see Code Review). On a regulated platform, fast feedback also means security and correctness problems are caught in minutes, not in an old PR nobody wants to read again.
Trunk-based development
- AlwaysIntegrate to the single shared trunk (main) often, at least daily, behind short-lived branches that last hours or a day, not weeks.
- DoKeep main always releasable. Every commit on trunk is small, reviewed, and passes the full automated pipeline (build, tests, security gates).
- DoUse feature flags to merge incomplete work safely behind a switch, rather than leaving it on a long-lived branch.
- DoPull from trunk often and resolve differences early, so integration stays continuous and conflicts stay small.
- ConsiderReleasing directly from trunk, with flags controlling what users see, rather than keeping separate release branches.
- NeverKeep a long-lived feature branch that drifts from trunk for days or weeks. That is not our model, and it brings back the merge conflicts and hidden work that trunk-based development removes.
Test-driven development
- AlwaysTreat tests as part of the change, not a follow-up. Every behaviour ships with the tests that prove it, especially security and compliance-critical logic.
- DoUse the TDD loop: red, green, refactor. Write a failing test that states the intent, make it pass simply, then improve the design with the test as a safety net.
- DoLet tests drive the design. Code that is hard to test usually means the design needs clearer boundaries (see Inversion of Control).
- DoAdd a failing test that reproduces every bug before you fix it, so the fix is proven and the bug cannot return.
- ConsiderWriting the test first even for exploratory work. It forces you to state what 'done' and 'correct' really mean.
- NeverShip behaviour with no test because 'there was not time'. Untested code on a regulated platform is unchecked risk, and with TDD the test is the cheap part, written first.
Pairing over slow PR queues
- DoPrefer pairing (or mobbing on the hard problems) as the main way we review and share knowledge. Review happens live, as the code is written.
- DoTreat a paired change as already reviewed. When two engineers build it together, it does not also need to sit in a multi-day async queue.
- DoUse asynchronous PR review only when pairing is not practical. When you do, keep changes small and return reviews fast: hours, not days.
- ConsiderPairing on purpose across experience levels and across the codebase, to spread context and remove single points of knowledge (see Collaboration & Teamwork).
- Do notLet changes pile up in a slow review queue as the normal path. Long-lived branches plus slow reviews is exactly the bottleneck we are avoiding.
- NeverMerge to trunk before the change has been reviewed, either by a pair in real time or by a fast async review, and before it passes the automated gates.
feature/new-onboarding (3 weeks off main, 4,000 lines)
→ opened as one giant PR
→ sits in the review queue for 5 days
→ 200 merge conflicts on rebase
Work was hidden for weeks, feedback came far too late to act on cheaply, and integration became painful. This is the problem that trunk-based development, TDD, and pairing remove.
Pair writes a failing test for one slice → makes it pass → refactors
→ integrates to trunk behind a feature flag the same day
→ already reviewed (built as a pair), pipeline green
repeat in small increments until the flag is switched on
Feedback is continuous, main never drifts, the behaviour is proven by tests, and review happened while the code was written. Steady flow instead of a bottleneck.
Self-review checklist
- AskIs my branch short-lived and integrating to trunk today, or quietly drifting away?
- AskDid I write the test first, and does every behaviour here have one?
- AskWas this reviewed live by a pair, or is it about to sit in a slow queue?
- AskIs main still releasable after this change, with all gates green?