Performance Budget: Setting and Enforcing Web Performance

A performance budget is a numeric limit on metrics that matter — page weight, JavaScript bundle size.

Performance Budget: The Constraint That Keeps Your Site From Getting Slower Every Sprint

A performance budget is a numeric ceiling on measurable performance characteristics — total JavaScript bytes, image weight, Largest Contentful Paint (LCP), Interaction to Next Paint (INP), Time to First Byte (TTFB) — that CI enforces automatically. Without a budget, performance degrades one PR at a time: each individual change adds 5KB, and six months later the site is 500KB heavier with no single change to blame. Budgets make performance a first-class engineering constraint alongside test coverage and type-checking.

What to budget

Two categories of metric. (a) Resource metrics — total JS bytes, CSS bytes, image bytes, third-party request count. These are what you can control at build time. (b) Experience metrics — the Core Web Vitals: LCP (under 2.5s), INP (under 200ms), CLS (under 0.1). These are what users actually experience and what Google ranks on. Budget both; resource budgets prevent regressions before deploy, experience budgets catch what resource budgets miss (a small script that blocks the main thread hurts INP without moving any byte count).

Setting the numbers

Start from where you are, tighten from there. Measure current values across your top pages, set the budget slightly stricter than the current p75 — enough to prevent regression but achievable. Common starting points for a SaaS marketing site: 200KB JS gzipped, 500KB total page weight, LCP under 2.5s on 4G. For a heavy web app: 400KB initial JS, 1MB total, LCP under 3s. Aspirational budgets you constantly miss produce alert fatigue; budgets aligned with reality produce discipline. Tighten quarterly as you optimize.

Enforcement in CI

Tooling: Lighthouse CI (Google) runs Lighthouse on every PR, compares against budgets defined in lighthouserc.js, comments on the PR with pass/fail. Bundle-analyzer plugins (webpack-bundle-analyzer, size-limit, bundlesize) enforce byte budgets per bundle. Both should block merge on regression above threshold, not just warn. Warnings are ignored; blocking is respected. For experience metrics that vary run-to-run, take the median of 3-5 runs to reduce false positives.

Real User Monitoring alongside synthetic

CI runs synthetic tests on a controlled machine — useful for regression detection but not representative of real user experience across devices, networks, and geographies. Complement with RUM tools (Vercel Analytics, SpeedCurve, DataDog RUM, or the free Chrome UX Report) that measure actual field metrics from actual users. When synthetic Lighthouse says LCP is 1.8s but RUM says field p75 LCP is 4.2s, the RUM number is the one users experience. Set experience-metric budgets against RUM p75, not synthetic.

Who owns performance

Distributed ownership fails — 'everyone owns performance' means nobody does. Effective model: a small performance-champions group (2-4 engineers) owns the budgets, the tooling, and the escalation for regressions; every team owns their own budget compliance. When a budget is exceeded, the team that broke it fixes it, not the performance group. The performance group's job is enabling and reviewing, not fixing everyone else's regressions. Without this structure, performance work becomes janitorial and gets deprioritized.

Frequently asked questions

Should marketing pages and app pages have the same budget?
No. Marketing pages should be aggressively lean (LCP under 2s, minimal JS) because they're where you acquire users and Google ranks you. In-app pages can carry more weight because users are already committed and the tradeoff for functionality is worth it. Set separate budgets per page category.
What about JavaScript frameworks?
Framework choice sets your floor. Next.js/React apps typically start at 90-150KB JS before any application code. Astro, Qwik, and vanilla ship substantially less. Framework choice is a performance decision as much as a DX decision; changing framework later to hit a budget is one of the highest-cost engineering projects you can take on.
How do we handle third-party scripts?
Budget them separately and aggressively. Analytics, chat widgets, and A/B testing tools are common performance killers. Load them async/defer, delay non-critical ones until interaction, and reject requests to add scripts without a business case that justifies the performance cost.

Related fundraising guides (40)

Investor directory · Fundraising library · Articles A–Z · Company funding database