What the pipeline is for
A test suite that runs only on the machine of whoever remembers is not a safety net. Continuous integration is the arrangement where every change is proved against the whole suite before it lands — and the value comes entirely from it being automatic, fast and trusted.
Fast, because a twenty-minute pipeline changes behaviour: people batch changes, stop waiting, and merge on a hope. Trusted, because a suite that fails at random teaches everybody to ignore red.
The order to run things in
Failing at step two costs a developer ninety seconds; failing at step six costs ten minutes. Order by cost and by probability, and let the slow stages run in parallel with each other where they do not depend on one another.
1. install (cached) seconds
2. lint + type check seconds — catches the typo before the tests do
3. unit tests seconds
4. integration tests a minute or two
5. build a minute
6. end-to-end a few minutes
7. deploy
Making it fast
- Cache the install. A lockfile hash is the cache key; this is usually the single biggest win.
- Parallelise by time, not by count. Splitting 400 tests into four groups of 100 gives four unequal workers; splitting by recorded duration gives four equal ones.
- Run only what changed, in a large repository — with a periodic full run, because the dependency graph lies eventually.
- Keep the slow ones out of the fast path: end-to-end on merge and nightly, not on every push to a branch.
What should fail the build
| Signal | Blocks? |
|---|---|
| A failing test | yes — that is the point |
| A lint error | yes, if it is a rule you agreed on; format automatically instead of complaining |
| Coverage below the ratchet | yes, as a floor — never as a target |
| A new flaky test | it should be visible, and owned; blocking is a team decision |
| A known-quarantined test | no — but the list has a name and a date against each one |
If the pipeline retries failures silently, a test that fails half the time looks green and the production race behind it never gets found. Retry if you must, and report the retry count as a metric somebody watches.
The environment
Pin the runtime version, fix TZ=UTC and the locale, seed anything random, and make the database per-run rather than shared. "It passes locally" is nearly always one of those four differences — and every one of them is a line of configuration.