The boring CI checks that keep paying off
Our pipeline runs the usual things: unit tests, end-to-end tests in a real browser, a build. Those catch regressions in behaviour we already thought about. The three checks below catch a different thing – mistakes nobody would write a test for – and between them they take about fifteen seconds.
A dependency licence list, regenerated and diffed
A script walks the installed dependency tree, collects the licence of every package including transitive
ones, and writes a manifest. It runs on postinstall, so the file is always current locally,
and CI regenerates it and fails if the result differs from what is committed.
Two things fall out of this. First, a dependency that changes its licence – or a transitive addition under terms we cannot ship – shows up as a red build on the pull request that introduced it, rather than during a review months later. Second, the application has an acknowledgements screen listing the open source it uses, and that screen is generated from the same manifest. It cannot drift out of date, because the build fails when it does.
Legal hygiene that maintains itself is rare. This one does, for maybe a hundred lines of script.
A theme-token check
A short script scans components for raw colour literals outside the design-token definitions and exits
non-zero when it finds one. It exists because "please use the tokens" is not enforceable by review at any
consistent rate, and one stray #1F2933 in a dark-theme component is invisible until a user
reports a patch of the wrong grey.
We wrote it in an afternoon. It has caught more theme regressions than the test suite, because it checks a rule instead of an example.
A type gate with no warnings allowed
tsc --noEmit
eslint . --max-warnings 0
The second flag is the load-bearing part. A warning budget above zero converges on being ignored – the count grows, nobody knows which entries are new, and the signal is gone within a quarter. At zero, every warning is either fixed or explicitly suppressed with a reason, and the suppression is visible in the diff where a reviewer will see it.
The same reasoning made us type third-party imports we had been leaving loose. Deep imports from an icon package were untyped, so a typo produced a runtime crash on one screen instead of a compile error. Adding the declarations took an hour and moved that whole class of mistake to build time.
Why these and not more
Each of these checks shares three properties: it runs in seconds, it never flakes, and its failure message tells you exactly what to change. That combination is what makes a gate survive contact with a deadline. A check that takes four minutes and fails intermittently gets disabled the first week someone is in a hurry, and it never comes back.
Tests tell you the thing you built still works. These tell you the thing you built is still the kind of thing you meant to build. They are cheaper, and they age better.← All notes