Fixed scope, fixed price — and how we sleep at night.
How we estimate, what we put in the contract, and what we do when scope changes. Spoiler: we don't pretend it doesn't.
Every project we ship has the same five-line CI step at the end of the design phase: a perf budget, a JS bundle ceiling, an LCP target on a throttled 3G connection, an a11y axe pass, and a 200 ms TTFB cap on the marketing pages. Miss any one of them and the build fails. Designers see it. Engineers see it. Clients see it.
Stop calling it a target.
A target is something a team aims for at the end of the quarter. A budget is something the system enforces on the way there. The difference between the two is who pays the bill when a stakeholder asks for "just one more script."
We treat performance the way we treat a database migration: write it down, version it, and let a tool gate the deploy. Below is a copy-paste version of the budget we keep on every project.
“Any budget that doesn't fail the build is a wishlist.”
The five rules
- Total JS on landing < 90 KB gzipped.
- LCP < 1.8 s on a Moto G4 throttled to slow 3G.
- CLS < 0.02 on every route under test.
- Zero axe-core serious or critical issues.
- TTFB < 200 ms from edge cache (HTML + Next/Image).
None of this is fancy. The trick is making it boring enough that nobody argues with it on day 90.
Next read
Design systems for teams of three.