Engineering team
Web practice
Why budgets in CI
A performance budget that lives in a Notion doc is a wish. A budget that runs in CI on every PR, blocks the merge if exceeded, and pages the engineer who shipped it — that's an enforced budget.
The friction is intentional. If your team is fighting the budget every PR, the budget is too tight or the team is shipping things that shouldn't ship. Either is information.
The thresholds we ship with
Defaults we use unless customer constraints push tighter:
- LCP — under 1.5s on the median page, under 2.0s on the slowest 10%.
- INP — under 200ms on the median, under 350ms on the slowest 10%.
- CLS — under 0.10 on every page, target 0.00.
- TBT (synthetic) — under 200ms on the median page.
- Bundle size — under 180kb gzipped for the initial JS payload on every route.
What the team agrees when we set it
Setting a budget in CI is a social contract, not just a tool config. We sign off on three things explicitly with the team: (1) PRs that blow the budget are blocked, no override; (2) the budget can be raised by changing the threshold in a PR — but the PR has to explain why; (3) regressions in real-user data trigger an investigation, not just a Slack ping.