Suite shapes
Every suite has a shape, whether or not anyone chose it. The named shapes are arguments about where the bulk of the tests should sit, and they disagree less than the diagrams suggest.
The four
Section titled “The four”The pyramid. Many unit tests, fewer integration, very few end-to-end. The original and still the default for a service with substantial domain logic. Its argument is economic: cost and flakiness increase as you go up, so keep the volume at the bottom.
The ice-cream cone. The pyramid inverted — a thin layer of unit tests under a mass of end-to-end and manual tests. Nobody designs this; it is what a team arrives at by testing the system it can see rather than the code it wrote. It is worth naming precisely because recognising it is the first step in leaving it.
The trophy. A small base of static analysis and unit tests, a broad middle of integration and component tests, a small top of end-to-end. The argument: for applications that are mostly glue — a UI, an API over a database — most of the risk is in the wiring, not in the logic, and a unit test over a component that mostly calls other components tests very little.
The honeycomb. Similar, from the microservice direction: few implementation- detail tests, many service-level tests through the service’s own boundary with its dependencies stubbed, few integrated tests. The argument: in a small service the interesting behaviour is the service’s contract with the outside.
What they actually disagree about
Section titled “What they actually disagree about”Not the top. Everyone agrees end-to-end tests are expensive and should be few.
The disagreement is about the middle, and it comes down to one question:
Where does the risk live in this system — in the logic, or in the wiring?
A pricing engine, a rules engine, a scheduler, a domain model with invariants: the risk is in the logic, and a pyramid is right. A CRUD API, a UI, a service whose job is to call three other services and reshape the result: the risk is in the wiring, and a trophy or honeycomb is right.
Most systems contain both, in different modules. The useful conclusion is that the shape is a per-module decision, not an organisational standard. A team that mandates one shape across a portfolio is answering a question it did not ask.
The rule that survives all of them
Section titled “The rule that survives all of them”From the strategy, and it is the only thing here that is not context-dependent:
Each property is checked at the cheapest level that can check it honestly, and at exactly one level.
Apply it consistently and the shape emerges on its own — it will be a pyramid for the pricing engine and a trophy for the API layer, and nobody will have had to argue about diagrams.
Two corollaries that do the real work:
- When a check moves down a level, delete the one above it. Migrations that only add produce a suite with the same shape and twice the runtime.
- A level with no tests is not automatically a gap. It is a gap only if there is a property that level alone could check.
Recognising your actual shape
Section titled “Recognising your actual shape”Not by counting tests — by measuring where the time goes and where the failures come from.
| Symptom | What it means |
|---|---|
| The suite takes more than 10 minutes on a merge | Too much weight above the ephemeral line |
| Failures are usually “re-run and it passed” | Too much weight at deployed level, or a determinism problem |
| A refactoring breaks 200 tests and no behaviour | Too many tests bound to structure — see unit |
| Every defect is found by end-to-end tests | The middle is missing |
| Coverage is high and defects still escape | Tests assert what ran, not what mattered — see coverage |
The third row is the one that gets misdiagnosed most often. It is read as “refactoring is expensive here”, and the actual finding is that the tests are over-specified.
Getting out of an ice-cream cone
Section titled “Getting out of an ice-cream cone”Not by deleting the end-to-end tests. That removes the only safety net a team in that position has, and it will be reversed within a month.
The sequence that works:
- Take the ten most frequent end-to-end failures. For each, name the level that could have caught it honestly.
- Write that lower test. Now the defect is caught in seconds instead of minutes.
- Only then delete the end-to-end test — unless its journey is on the critical list from end-to-end, in which case it stays and stops being the thing that finds this class of defect.
- Repeat. The suite gets faster in proportion to how bad it was.
This is slow and it is the only version that survives contact with a release schedule.