Teams often argue about how much to test as if it were one dial. It is not. There are different kinds of tests with very different costs and payoffs, and the useful question is not "how many tests" but "what mix". The testing pyramid is a simple mental model for that mix, and getting it roughly right is the difference between a suite that helps and one that everyone dreads.
The shape matters because tests are code you also have to maintain, and slow, brittle tests get ignored or deleted — taking their protection with them.
Three layers, three costs
At the base are unit tests: they check one function or module in isolation, run in milliseconds, and pinpoint failures precisely. They are cheap to write and cheap to run, so you can have thousands. In the middle are integration tests, which check that several parts work together — a service and its database, say. They are slower and catch a different class of bug: the wiring between components.
At the top are end-to-end tests, which drive the whole system the way a user would. They give the most realistic confidence and are the slowest, flakiest and most expensive to maintain. The pyramid shape — many unit tests, fewer integration tests, a handful of end-to-end tests — reflects those costs.
Why an inverted pyramid hurts
When a team relies mainly on slow end-to-end tests, the suite takes an age to run, fails intermittently for reasons unrelated to the code, and points vaguely at "something broke somewhere". Developers stop trusting it, stop running it, and stop adding to it. The protection quietly erodes even though the test count looks healthy.
A broad base of fast unit tests inverts that experience: failures are quick and specific, the suite runs on every save, and it is pleasant enough that people keep it green. The heavier tests then guard the few critical user journeys where realistic, full-stack confidence genuinely earns its cost.
Test behaviour, not implementation
The most durable tests check what a unit does, not how it does it — its inputs and outputs, its observable behaviour. Tests coupled to internal details break every time you refactor, punishing exactly the cleanup work you want to encourage. Behaviour-focused tests survive refactors and keep protecting you through change.
A practical target: cover the logic that would be costly to get wrong, keep the fast layer broad, and reserve the slow layer for the journeys that must never break. A suite you trust and actually run beats an exhaustive one that everyone skips.