ESC

Type to search the knowledge base.

Performance Budget System Design

System design for performance budgets — metrics, CI gates, RUM feedback loops, ownership, and enforcement.

intermediate4 min read
  • system-design
  • interview
  • architecture
  • performance

Scope the problem

A performance budget system is how an org decides limits, measures them, and blocks regressions — not a one-time Lighthouse run.

In scope: which metrics, lab vs field, CI integration, dashboards, ownership, exception process.

Goals

  • Prevent “death by a thousand KB”
  • Make perf regressions visible before prod
  • Align product tradeoffs with user impact (CWV)

Metric layers

Layer Examples When
Resource JS KB gzip per route, image weight CI, PR
Lab CWV Lighthouse LCP/INP/CLS CI sample URLs
Field RUM p75 LCP/INP/CLS by route production
Custom TTI login, time-to-ATC product-critical

Budgets without field data optimize lab fiction.

Example budgets

Surface Budget
Marketing landing JS ≤ 90KB gzip initial
App shell JS ≤ 150KB gzip
PDP LCP (4G lab) ≤ 2.5s
Checkout INP p75 field ≤ 200ms
CLS p75 ≤ 0.1

Different routes → different budgets. Global single number is too blunt.

System architecture

PR build
  → bundle analyzer stats
  → size compare vs base branch
  → Lighthouse CI on key URLs
  → pass / warn / fail
Prod
  → RUM SDK vitals
  → daily report by route owner
  → alert on regression

CI enforcement design

// conceptual check
if (routeSizes["checkout"] > budgets.checkout.jsGzip) {
  fail("Checkout JS exceeded budget");
}

Modes:

  1. Hard fail on critical routes
  2. Warn on experimental
  3. Temporary waivers with expiry owner

Waivers without expiry = budgets dead.

Bundle analysis

  • Track per-route entry chunks
  • Fail on unexpected new large dependency
  • Duplicate package detection (lodash vs lodash-es)

Lab vs field differences

Lab Field
Controlled device real devices
Cached warm often cold cache users
No third-party variance tags, consent, ads

Use lab for PR signal; field for truth and OKRs.

Ownership model

Route / product surface → team → budget → dashboard

Platform team owns tooling; product teams own their numbers. Perf is a feature requirement, not a platform-only hobby.

Feedback loops

  1. PR blocks egregious regressions
  2. Weekly review of field p75
  3. Release annotation on RUM charts
  4. Postmortems when CWV hurts SEO/revenue

Tooling components

Piece Role
webpack/vite stats sizes
Lighthouse CI lab
web-vitals + backend RUM
bundlesize / size-limit PR gates
Source maps + traces debug

Handling third parties

  • Budget third-party bytes separately
  • Tag managers load after interaction when possible
  • Contract with marketing on script weight

Tradeoffs

  1. Strict CI vs ship speed — start with top 5 routes
  2. Synthetic only misses real pain
  3. Optimizing averages vs p75/p95 users
  4. Micro-optimizations vs architectural splits

Interview close

Define multi-layer budgets → per-route ownership → CI size + Lighthouse gates with waiver policy → RUM verification → third-party accounting. Tie to business metrics (conversion, SEO).

Example PR comment bot

❌ checkout.js 184KB gzip (budget 150KB) (+22KB vs main)
⚠️  LCP lab /product/sku 2.8s (budget 2.5s)
✓  CLS 0.02

Engineers should see which dependency grew (why-did-you-bundle / analyzer link).

Organizational policy

  • Budgets live in repo (perf-budgets.json) reviewed like code
  • Changing a budget is a conscious PR, not a silent config tweak on CI day
  • Pair with design system: new heavy components need platform review

Mobile-first emphasis

Field INP and LCP on mid-tier Android often dominate pain. Don’t celebrate desktop lab greens while p75 mobile burns. Split dashboards by device class.

Further reading