Back to Blog
EngineeringPerformanceCI/CDWeb Vitals

Performance Budgets That Actually Work

Moving beyond Lighthouse scores to build real performance guardrails your team will respect.

Marcus Bell
Jun 20268 min read
Performance Budgets That Actually Work

Most performance budgets die in a CI dashboard nobody checks. The ones that survive share a trait: they are enforced at the point of failure, not reported after the fact. We've watched the same pattern play out at three different clients — a beautiful Lighthouse CI setup gets wired up in week one, gets ignored by week six, and by month three nobody remembers it exists until a stakeholder complains the site feels slow.

Where budgets break down

A budget that gates a deploy is a policy. A budget that only logs a warning is a suggestion. Teams treat them accordingly. The failure mode is almost never technical — it's that the budget was bolted onto an existing pipeline as an informational step, so the first time it blocks someone's Friday-afternoon deploy, someone with deploy access quietly downgrades it from 'fail' to 'warn,' and the budget is dead from that commit forward.

  • Set thresholds at the PR level, not the pipeline level — a regression caught in review costs a comment; the same regression caught post-merge costs a revert and a retest.
  • Fail the build on regressions, not just on thresholds — a team under a 250kB budget that's sitting comfortably at 180kB still shouldn't be allowed to add 60kB in one PR.
  • Track bundle size per-route, not just globally — a shared layout bloat hides inside route-level budgets that look fine in aggregate.
  • Measure real-user metrics, not synthetic lab scores — a lab score run on a fast CI runner over a fast connection tells you almost nothing about the user on a three-year-old Android phone on throttled mobile data.

A practical starting point

Start with three budgets: total JavaScript under 200 kB compressed, Largest Contentful Paint under 2.5 seconds on 4G, and Cumulative Layout Shift under 0.1. Everything else is refinement. These three cover the failure modes users actually report — a page that's slow to become interactive, a page that's slow to show its main content, and a page that jumps around while it loads.

Making the budget survive contact with a deadline

A budget's real test isn't the quiet sprint — it's the week before a launch, when someone needs to ship a feature that blows past the threshold and the deadline isn't moving. The teams that keep their budgets intact under that pressure have a pre-agreed exception process, not an ad-hoc override.

  1. Require an explicit, time-boxed waiver

    An override needs a named approver, a written reason, and an expiry date — a temporary regression that's tracked and revisited, not a permanent new baseline nobody chose on purpose.

  2. Attribute every regression to its cause in the PR

    A failed budget check should surface which dependency or code path grew, not just the final delta — otherwise every regression becomes a bisecting exercise before it can even be discussed.

  3. Review the budget itself quarterly

    Thresholds set a year ago against a different feature set either calcify as permanently too loose or get blown through every sprint — both are signs the number needs revisiting, not the team.

In closing

Performance is a feature, not a side effect. Treat it with the same rigor you give to security: automated checks, clear ownership, documented exceptions, and real consequences for regressions that slip through anyway.