BOOK DEEP READ · CONTINUOUS DELIVERY · CH 7
Continuous Delivery · Ch 7 · Jez Humble & David Farley · 2010
The app on your phone is built by programmers who hand in their "homework" to a shared codebase several times a day. Chapter 7 of Continuous Delivery is about what kind of check should run in the first few minutes after that homework arrives. That check is called the commit stage. It is the first gate of the pipeline from the previous chapter, and the only gate every developer faces personally, every single day.
Think of hospital triage. When you walk into the emergency room, a nurse spends five minutes on your temperature, blood pressure and pulse — no scans, no surgery. One job only: use the fastest, cheapest instruments available to spot who is obviously in trouble. The expensive examinations come later, and only for the people triage lets through.
Here is the counterintuitive part: the value of triage is not in how thorough it is, but in being fast enough that you are willing to stand there and wait for the result. A very thorough triage desk that takes two hours is no triage desk at all.
This check can fail in three ways, all fatal. Too slow: pile every check together and it takes two hours, so nobody waits — you hand in your work and move on, and when the report comes back red you have long forgotten what you changed. Useless: the report says only "failed", not what broke or how to reproduce it, so people quietly learn to ignore it. Dishonest: some checks pass and fail at random on identical code; after a few rounds, the team's first reaction to a red light stops being "go fix it" and becomes "just run it again" — and from that moment the gate is dead.
One: give it a time budget of ten minutes. That number is not arbitrary — it is roughly the limit of how long a person will stand there and wait. Past it, behaviour changes: people stop waiting, keep working, and several people's changes pile up so nobody can tell whose change turned the light red. Ten minutes is a design constraint, not a performance target.
Two: only run checks that are both fast and trustworthy. What is slow? Touching a database, clicking through a user interface, waiting a while before looking. Most of this chapter teaches you how to keep those three out: give the little piece of code under test a stand-in — a fake database, a clock that stops on command — so it finishes in a thousandth of a second, and thousands of them together still take seconds.
Three: once it passes, seal the box and label it. That exact box travels down the rest of the line; nobody is allowed to build a new one. The reports get filed alongside it, where the whole team can read them.
The difficulty is not in the checking — it is in the code being checked. For a small piece of code to be tested on its own with stand-ins, it has to be separable from everything around it. With tangled legacy code you will find you simply cannot write fast tests. Which is this chapter's hidden lesson: when tests are hard to write, that is usually not a testing problem — it is the design raising an alarm.
It does have a cost: this gate is fast precisely because it never touches a real database or a real network, so "green" only means nothing obviously broke — it does not mean the change is fit to ship. That verdict belongs to the slower, more expensive gates further down.
The commit stage is the pipeline's first gate: a trustworthy red or green within ten minutes. It does not aim to be thorough — it aims to be fast enough that you wait, and reliable enough that you believe it. Thoroughness is somebody else's job, further down the line.
Want the actual contents, where the ten minutes goes, and how to pick test doubles? → Switch to the deep read
The commit stage is the first gate of the deployment pipeline: triggered automatically by every commit, it compiles the code, runs the commit tests, performs static analysis, assembles a deployable binary, and returns a red or green verdict within minutes. It is the one gate every developer faces personally every day, so the real subject of this chapter is not what to test but how to make this gate fast enough that people wait for it and trustworthy enough that people believe it — ten minutes is not a performance target but a behavioural constraint: past it, developers stop waiting, and the whole discipline of continuous integration unravels behind them.
new-ing them itself, which is what makes swapping in doubles possible.This chapter follows Ch5 inside Part II. Ch5 drew the whole conveyor belt from commit to production; this chapter zooms into its first cell. Upstream it inherits the discipline of Ch3 (merge to trunk daily, stop the line when red) and the quadrants of Ch4 (the commit stage carries the technology-facing tests that support development); downstream, Ch8 takes over with automated acceptance testing. In today's world it is the thing you already stare at: the required checks on a GitHub pull request, the first stage of a GitLab CI pipeline, a Gerrit pre-submit verify, Google's TAP, Meta's Sandcastle. Those few minutes of spinner you wait on every day are this chapter.
Ch5 already established that earlier gates must be faster. What it did not say is what this particular gate should look like — and this gate has a special status: it is the only daily interface between developers and the entire pipeline. Most developers may not look at acceptance tests, capacity tests or the production release for a week at a time; they meet the commit stage several times a day. So when this gate is unpleasant, what gets bypassed is not the gate — it is the whole practice of continuous delivery.
The chapter names three concrete failure modes:
Leave this unsolved and you fall back into the world of Ch1: defects sit in the codebase for weeks, by which time their author has lost all context and diagnosis costs several times more; or the mirror image, a team dragged under by a two-hour suite that flakes, until everyone quietly agrees to route around it.
The definition is clean. Input: one commit in version control. Work: compile → run the commit tests → run static analysis → assemble deployable binaries (and, where useful, create database schemas or build installers). Exits: exactly two. Either red — notify the whole team immediately and, per Ch3, stop the line and fix it; or green — store the binaries, reports and metadata in the artifact repository as a release candidate that moves on to the acceptance stage.
Of those three outputs, the one most often neglected is the metadata: which commit this binary came from, which tests it ran, and how they went. It is what every later stage uses to decide on promotion, and the thread you follow backwards when something blows up.
The chapter keeps returning to one number: the commit stage should finish in under ten minutes (the same figure the CI discipline of Ch3 gives). Why ten rather than twenty? Because the threshold governs human behaviour, not hardware:
This is also why the chapter refuses to put "all the tests" here: the commit stage is not trying to prove the change is right, it is using the cheapest means available to keep the obviously wrong out.
A pure in-memory unit test runs in milliseconds. A test that connects to a database, creates and cleans tables easily costs 50-100 ms each. A test driving a real user interface costs seconds to tens of seconds. Multiply by volume and the gap is orders of magnitude:
Hence a very concrete set of writing rules, all serving one goal — drive the unit price of a test down to milliseconds:
sleep calls — the chapter treats such brute-force approaches as a last resort.All of the above presumes one thing: the code under test can have its collaborators swapped out. That is what dependency injection buys you — collaborators arrive through the constructor or the parameters, so a test can hand it a double instead.
The chapter adds a warning about doubles that has aged extremely well: overuse them and your tests get welded to your implementation. A stub only feeds values back, so you are still testing "is the output right"; a mock asserts "you must call these methods in this order" — refactor the internals, even with behaviour unchanged, and such tests go red in droves. Tests exist so you dare to change the code; when they make you afraid to change it, they have turned into their own opposite.
Compilation failures and failed commit tests are red beyond argument. What is arguable is static analysis: coverage below a threshold, duplication over a limit, complexity spiking, new warnings — should those fail the build?
The chapter's position is yes, and often they should — encoding a threshold the team agreed on into the build, enforced automatically, beats writing it on a wiki nobody reads. But with an important caveat: these metrics are indicators of a trend, not goals in themselves. Coverage is the dangerous one: it tells you which code was never executed, not whether the executed part is well tested — delete every assertion and coverage does not move. So use it to find obvious gaps (a new module at zero), not as a KPI to drive upward.
What comes out of a green run must be kept properly — this is where Ch5's "build your binaries only once" lands. Binaries do not go into version control: they are derived from source, and stuffing them into Git only bloats the repository. Put them in a dedicated artifact repository, named by revision, so you can always trace a package running in production back to its commit. Reports (test results, coverage, static analysis) are first-class outputs too, and belong somewhere the whole team can open them — making analysis visible has value on its own, even before you use it to fail builds.
The last principle is often skipped and is the most organisational of them: the commit stage belongs to the development team, not to a "build team". The people changing the code are the ones who know what checks to add and what a red light means; hand the scripts to a group that writes no application code and the scripts will inevitably drift behind the code, while developers stop feeling that red is their problem. The companion practice is treating build scripts as first-class code: in version control, refactored, reviewed. Very large teams can appoint a rotating build master to watch red lights, chase fixes and tend the scripts — rotating on purpose, to share the load rather than outsource the responsibility.
Table 1 · Does this check belong in the commit stage, or later?
| Check | Typical cost | Catches | Where | Price / rationale |
|---|---|---|---|---|
| Compile & package | seconds to 1 min | changes that do not even build | Commit stage | uncontroversial, the cheapest gate there is |
| Unit tests | milliseconds each | regressions in business logic | Commit stage | requires injectable code; expensive to retrofit onto legacy |
| Static analysis | seconds to minutes | suspect constructs, runaway complexity and duplication | Commit stage | thresholds set too tight produce daily false alarms, and people learn to ignore them |
| A few smoke-level integration checks | tens of seconds | components that do not connect, misconfiguration | Commit stage (strictly capped) | the category most prone to creep — cap it deliberately |
| Database integration tests | 50-100 ms each | SQL, mapping and migration errors | Secondary or acceptance stage | putting them here trades budget for fidelity |
| End-to-end / UI tests | seconds each and up | a whole business flow that does not work | Acceptance stage (Ch8) | slow and brittle; they will sink the commit stage |
| Performance / capacity tests | hours | throughput and latency regressions | A later dedicated stage | needs baselines and a dedicated environment |
Table 2 · What turns the build red, and what only warns
| Signal | Recommendation | Why | Risk |
|---|---|---|---|
| Compile or test failure | Hard red | behaviour has been falsified; nothing to discuss | — |
| Flaky test | Quarantine with a deadline | leaving it in teaches the team to ignore red | the quarantine becomes a dumping ground and defects escape |
| Coverage below threshold | Can be red; set by the team, to prevent backsliding | good at spotting a whole new module with no tests | chased as a KPI it produces assertion-free tests |
| Duplication / complexity limits | Make visible first, enforce once stable | indicators of a trend, not targets | enforced from day one it just breeds evasive coding |
| New compiler warnings | Hard red (baseline the existing ones) | warnings that cost nothing accumulate forever | with a large backlog, a blanket rule paralyses the team |
Table 3 · What commit tests do about the database
| Approach | Cost per test | Fidelity | Where | Price |
|---|---|---|---|---|
| No database at all (logic + doubles) | microseconds to ms | low: SQL and mapping untested | The bulk of the commit stage | demands a clean split between logic and persistence |
| In-memory database | ~1 ms | medium: dialect differs from the real thing | Commit stage, cautiously | wherever the dialects differ, you get false green |
| Real database in a container | tens of ms plus startup | high | Secondary / acceptance stage | expensive in 2010, cheap today — the budget still binds |
| One shared test database | unpredictable | medium | Not recommended | teams pollute each other; failures depend on order |
Table 4 · Picking a test double
| Double | What it does | Good for | Price |
|---|---|---|---|
| Stub | returns canned answers when asked | feeding inputs: clocks, config, read-only queries | almost none — the default choice |
| Mock | asserts it was called, how often, with what | verifying side effects: charging, emailing, publishing | welds tests to the implementation; refactors go red in droves |
| Fake | a lightweight working implementation (in-memory repository) | anything stateful; usually the most natural to write | it is code too — it must be maintained and can drift from the real thing |
| The real object | no substitution | pure computation with no I/O | the moment I/O appears, speed and stability collapse |
Table 5 · The commit stage broke ten minutes. Now what?
| Option | Effect | Price / precondition |
|---|---|---|
| Shard across machines in parallel | near-linear speedup, the most direct fix | tests must share no state; costs hardware |
| Move slow tests to a secondary stage | puts the commit stage back inside budget immediately | slower feedback, and what moves out tends to be neglected |
| Split the application and its pipelines | smaller scope per component | introduces cross-component version combinations (Ch13) |
| Select tests by impact | run only what this change could affect | needs a dependency graph or build system; a wrong selection lets defects through |
| Delete duplicated, low-value tests | often a surprising win | someone must actually look at the data rather than guess |
| Just raise the limit to 30 minutes | looks like the easy way out | people stop waiting, and the CI discipline dissolves with them |
All five tables rest on one criterion: every second of the commit stage must be bought with "how much earlier does this check find a problem?" A check that is both slow and only catches rare problems does not belong in this gate — but do not throw it away either; move it one stage down.
The shapes changed; the logic did not. The required checks blocking a GitHub merge button, the first stage of a GitLab CI pipeline, a Gerrit pre-submit verify, Bazel's small tests with remote caching, Google's TAP, Meta's Sandcastle — all are engineered commit stages. Artifact repositories (Nexus, Artifactory, container registries) exist for exactly the "binaries plus metadata" output described here. Merge queues supply the piece the book never developed: with many people committing concurrently, each change is re-tested on the post-merge state, so two individually green changes cannot combine into red.
In interviews and architecture reviews this is close to a standard question. Asked "how does your CI work", the answer that scores is not a list of tools but four facts: how many minutes is your commit stage? what turns it red? how do you handle flaky tests, and how many are quarantined right now? when it goes red, who owns it and how long until it is green?
1 · The commit stage is the pipeline's first gate and the only one every developer faces daily — its experience decides whether the whole delivery discipline survives.
2 · One input (a commit), two exits: red stops the line, or green emits binaries + reports + metadata into the artifact repository as a release candidate.
3 · Ten minutes is a behavioural constraint, not a performance target: past it people stop waiting, and both blame attribution and batch size go out of control.
4 · The arithmetic of the budget: unit tests in milliseconds, database tests in hundreds of milliseconds, UI tests in seconds — let each test touch a real database and five thousand of them consume most of your budget.
5 · Writing rules for commit tests: avoid the GUI, avoid the database, avoid asynchrony, fake time, minimise state — all enabled by injecting doubles for collaborators.
6 · Use doubles sparingly: stubs for inputs, mocks for side effects; overused mocks weld tests to the implementation and turn refactoring red.
7 · What should be red: compile and test failures always; static-analysis thresholds may be, but coverage is an indicator, not a goal — it only shows which code never ran.
8 · Flakiness is this gate's number one killer: a red light you cannot trust is no red light at all; Google has acknowledged roughly one in seven of its tests showing some flakiness.
9 · The gate belongs to the development team, not a build team; build scripts are first-class code, and very large teams use a rotating build master.
10 · When it no longer fits: parallelise, split, move work later, select by impact (Meta runs a third of its tests and still catches 99.9% of regressions) — not raise the budget from ten minutes to thirty.