The Pyramid vs the Trophy — Choosing a Shape
You now know the five levels — unit, integration, system and E2E, and acceptance — and that each buys a different currency of confidence. The natural next question is a question of proportion: if you have a fixed budget of time and patience, how many tests should you keep at each level? Ten thousand unit tests and one E2E test? A thick middle of integration tests? An even split?
The industry has two famous answers to that question, drawn as two shapes. This page is about both — what each one optimizes for, and, more importantly, the principle underneath them that tells you which one your own system actually wants. The punchline up front: the shape is not a rule to copy. It is a consequence of where your bugs and your costs really live.
The question the shape answers
Section titled “The question the shape answers”A test suite has a shape — the relative count of tests at each level, stacked from cheap-and-narrow at the bottom to slow-and-broad at the top. Draw the counts as a bar chart turned on its side and you get a silhouette. That silhouette is the argument: it says “spend your effort here.”
e2e ▓ e2e ▓ ▓▓ ▓▓▓ integ ▓▓▓▓▓ integ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ unit ▓▓▓▓▓▓▓▓▓▓▓▓▓ unit ▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ static ▓▓▓▓▓▓▓▓▓▓▓▓▓
THE PYRAMID THE TROPHYBoth shapes agree on the two things you learned last part: unit tests are cheap and precise; E2E tests are expensive and meaningful. They disagree about where the fat middle should go. That disagreement is the whole debate.
The test pyramid — many at the base, few at the top
Section titled “The test pyramid — many at the base, few at the top”The test pyramid is the older and still-default prescription (popularised by Mike Cohn around 2009). It says: keep many fast unit tests at the base, fewer integration tests in the middle, and very few slow end-to-end tests at the top.
The reasoning is pure economics, and it falls straight out of the “By the numbers” figures from the overview:
- Speed. A unit test runs in single-digit milliseconds; an E2E test runs in seconds. If you want a suite that finishes before your attention wanders — the whole point of a fast feedback loop — most of your tests must be the cheap kind, or the wall-clock total explodes.
- Cost to write and maintain. Unit tests touch one small surface; E2E tests wire real infrastructure, real timing, and real UI, all of which drift. Every E2E test is a standing maintenance bill.
- Localisation. When a unit test fails, it points at one function. When an E2E test fails, the bug could be anywhere in the stack — so you pay again in debugging time.
- Flakiness. Realism brings non-determinism: networks blip, clocks tick, async races. The more real parts a test touches, the more often it fails for reasons that are not bugs. Keep those tests few, or the suite cries wolf until people ignore it.
So the pyramid is a cost-minimising shape: push confidence as far down toward the cheap, stable, precise base as you can, and buy only as much expensive realism at the top as you truly need.
worked pace — a healthy pyramid on one push 3,000 unit @ 3 ms ≈ 9 s 300 integration @ 80 ms ≈ 24 s 30 e2e @ 15 s ≈ 7.5 min ─────────────────────────────────── the suite finishes in minutes, and 3,000 failures each point at exactly one function.The testing trophy — a heavy integration middle
Section titled “The testing trophy — a heavy integration middle”The testing trophy (named by Kent C. Dodds around 2018) is the challenger, and it grew out of a specific world: I/O-heavy applications, especially front-end and full-stack web apps, where most bugs are not wrong arithmetic inside one function but wrong wiring between parts — a component that renders the wrong thing given real props, an endpoint that returns a shape the client did not expect, a query that does not match the schema.
The trophy has four tiers, bottom to top:
- Static analysis — the wide base. Types, linters, and compilers catch a whole class of bugs (typos, undefined variables, type mismatches, null misuse) for zero test-writing effort, before any test runs at all. This is “free” confidence, so make it the foundation.
- Unit — a modest layer, for the genuinely logic-heavy pieces (a date parser, a pricing rule).
- Integration — the fat middle, and the trophy’s whole thesis: test a few real parts together the way they are actually used, faking only the slow or external edges. The claim is that for I/O-heavy code, integration tests buy the best confidence-per-cost: close enough to real that a pass means something, cheap enough to keep hundreds.
- E2E — a thin top, same as the pyramid.
The trophy’s slogan — “the more your tests resemble the way your software is used, the more confidence they give you” — is a bet that heavily-mocked unit tests on glue code test the mocks, not the software. When your bugs live at the seams, test the seams.
The shape is a consequence, not a dogma
Section titled “The shape is a consequence, not a dogma”Here is the first-principles resolution, and the most important paragraph on this page. The pyramid and the trophy are not two religions to pick between. They are two answers to the same optimisation problem — maximise confidence per unit of cost — computed for two different kinds of software.
- In logic-heavy software (a compiler, a billing engine, a physics core, a consensus rule), most bugs are wrong logic inside units. There, unit tests have the highest confidence-per-cost, so the optimum is wide at the bottom: a pyramid.
- In I/O-heavy software (a typical CRUD web app, a UI, a service that is mostly glue between a database and an API), most bugs are wrong wiring between parts. There, integration tests have the highest confidence-per-cost, so the optimum bulges in the middle: a trophy.
The shape you should keep is dictated by where your bugs actually live and where your cost actually is — not by which diagram is fashionable. A team that copies the trophy onto a logic-heavy engine will under-test its arithmetic; a team that copies the pyramid onto a thin glue layer will pile up unit tests that assert their own mocks. Both failed the same way: they copied a shape instead of measuring their system.
where do your bugs live? mostly inside units (logic) ────────► pyramid mostly at the seams (I/O) ────────► trophy honestly? → measure your last 50 bugs and let them draw the shapeThe anti-patterns — shapes that hurt
Section titled “The anti-patterns — shapes that hurt”Two shapes are wrong for almost everyone, and naming them is the fastest way to read your own suite.
The ice-cream cone — top-heavy
Section titled “The ice-cream cone — top-heavy”The ice-cream cone is the pyramid upside down: a fat scoop of slow E2E and manual tests on top, tapering to almost no unit tests at the base.
▓▓▓▓▓▓▓▓▓▓▓▓▓ manual + e2e (huge, slow, flaky) ▓▓▓▓▓▓▓▓▓ integration ▓▓▓▓▓ unit ▓ static / noneIt is what you get by accident: nobody sets out to build it, but if a team tests only through the UI and never pushes checks downward, this is the shape that accretes. The result is a suite that is slow (minutes to hours), flaky (every network blip is a red build), and useless at localisation (a failure could be anywhere). Teams cope by ignoring red builds — which means the suite now costs a fortune and buys nothing. The cure is to push tests down: every time an E2E test catches a bug that a unit or integration test could have caught faster, write the cheaper test and consider retiring the expensive one.
The hourglass — a missing middle
Section titled “The hourglass — a missing middle”The hourglass has a solid base of unit tests and a heavy cap of E2E tests, but a pinched, near-empty integration middle.
▓▓▓▓▓▓▓▓▓ e2e (heavy) ▓▓▓ integration (missing!) ▓▓▓▓▓▓▓▓▓ unit (heavy)It happens when a team trusts units in isolation and then jumps straight to full-stack E2E to check “does it all work,” skipping the cheap seam-level tests in between. The cost is brutal: bugs at the seams — the mismatched contracts of integration testing — are invisible to the units (each passes alone) and only surface at the slow, flaky top, where they are most expensive to diagnose. The missing middle is exactly the layer that would have caught them cheaply. The cure is to fill it: for every “the parts don’t agree” bug that an E2E test caught, add the integration test that would have caught it in 80 ms instead of 20 s.
Reading your own suite’s shape
Section titled “Reading your own suite’s shape”You do not have to guess. You can measure the shape you have and the shape you need.
Measure the shape you have. Count tests per level (test directories, tags, or naming conventions make this easy). Time a full run and see where the seconds go. A useful smell test: if 10% of your tests eat 90% of the runtime, you are top-heavy. If a red build is met with “just re-run it,” you have flake — a top-heavy tell.
Measure the shape you need. Open your last 30–50 real bugs — the ones that reached staging or production — and ask, for each, what is the cheapest level that could have caught this? Tally the answers. That histogram, not a blog post, is the shape your system is asking for. If most bugs were logic errors, you want a pyramid. If most were seam and wiring errors, you want a trophy. If most only appeared on deploy, you have an integration/system gap to fill.
a 5-minute audit 1. count tests per level → the shape you HAVE 2. time a full run, note the hogs → where cost actually is 3. bucket your last 40 bugs by "cheapest level that'd catch it" → the shape you NEED 4. move investment toward the gap between (1) and (3)Most real, healthy suites end up pyramid-ish with a respectable middle — closer to a trophy than the strict 2009 pyramid, because static analysis is nearly free and integration tests earn their keep — but the exact proportions are yours to earn from your own bug history, not to inherit from a diagram.
The architect’s lens
Section titled “The architect’s lens”- Why does it exist? Because a fixed testing budget forces a choice of proportion across levels, and teams needed a memorable heuristic for spending it well instead of piling all their effort at whichever level felt easiest.
- What problem does it solve? It answers “how many tests at each level?” by pointing you at the level with the best confidence-per-cost for your kind of software — cheap precise units for logic-heavy code (pyramid), realistic seams for I/O-heavy code (trophy).
- What are the trade-offs? Any fixed shape trades realism against speed and stability. Weight low and you get a fast, precise suite that may miss seam and wiring bugs; weight high and you get a meaningful but slow, flaky, expensive-to-maintain suite.
- When should I avoid it? Avoid treating either shape as dogma. Do not copy a shape onto a system whose bugs live elsewhere — a trophy on a compiler, or a strict pyramid on a thin glue layer — because you will over-invest where the bugs are not.
- What breaks if I remove it? Remove the idea of a deliberate shape and suites drift into the ice-cream cone or hourglass by accident: slow, flaky, top-heavy, or missing the exact middle where the costly bugs hide.
Check your understanding
Section titled “Check your understanding”- The pyramid and the trophy disagree about one thing while agreeing about the extremes. What do they agree on, and what do they disagree about?
- State the pyramid’s reasoning from first principles: why many unit tests and few E2E tests? Name at least three of the four cost drivers.
- What does the testing trophy add at its base that the classic pyramid does not, and why does that layer count as “free” confidence?
- Explain, in one sentence, why the trophy’s fat integration middle is right for a CRUD web app but wrong for a billing engine — using the idea of confidence-per-cost.
- Name the two anti-pattern shapes, say what each is missing or over-weighting, and give the class of bug each one is blind to.
Show answers
- They agree on the extremes: unit tests are cheap and precise (keep many), E2E tests are expensive and meaningful (keep few). They disagree about where the fat middle goes — the pyramid keeps its bulk in cheap unit tests at the base; the trophy shifts weight up into a heavy integration middle.
- Because the levels differ by orders of magnitude in cost, so most tests must be the cheap kind or the suite becomes unusable. The four drivers: speed (units run in ms, E2E in seconds — total wall-clock must stay short for fast feedback), cost to write/maintain (E2E wires drifting real infra), localisation (a unit failure points at one function; an E2E failure could be anywhere), and flakiness (more real parts = more non-deterministic failures). Any three suffice.
- The trophy adds static analysis (types, linters, compilers) as its wide base. It is “free” confidence because it catches a whole class of bugs — typos, type mismatches, undefined variables, null misuse — for zero test-writing effort and before any test runs.
- For a CRUD web app most bugs live at the seams (wiring between DB, API, and UI), so integration tests have the highest confidence-per-cost; for a billing engine most bugs live inside units (wrong logic), so unit tests have the highest confidence-per-cost — same optimisation, different bug distribution, different peak.
- The ice-cream cone is top-heavy — a huge slow/flaky E2E-and-manual cap and almost no unit base; it is slow, flaky, and terrible at localisation. The hourglass is missing its integration middle — heavy units and heavy E2E but a pinched center; it is blind to seam/contract bugs, which each unit passes alone and which then only surface at the slow, expensive top.