Not every test belongs at the same stage — running a full end-to-end suite against a real staging environment on every single push doesn't scale, and stops giving fast feedback long before it stops being useful feedback. The test pyramid is the usual mental model for how to spread test types across a pipeline:
| Layer | What it checks | Speed | Where it typically runs |
|---|---|---|---|
| Unit tests | Individual functions/components in isolation, dependencies mocked | Milliseconds each, thousands can run in seconds | Every push, every PR — first gate |
| Integration tests | Multiple real units together (a service + a real test database) | Seconds each | Every push, often in the same job as build |
| End-to-end (E2E) tests | The whole system through its real interface (a browser driving the actual UI) | Minutes, can be flaky | Before merge, or on a schedule/nightly — too slow for every keystroke |
A pipeline that only runs unit tests is fast but has a blind spot for anything that only breaks when real pieces interact (a database migration that doesn't match the ORM's expectations, a genuine network timeout). A pipeline that runs a full E2E suite on every commit is thorough but slow enough that developers start ignoring or working around it. Most real pipelines run fast checks (lint, unit tests) on every push, a heavier integration suite before merge, and the slowest, most end-to-end checks on a schedule or as a required-but-parallelized gate right before a release.