FSD Frontend System Design

Part II β€” The Shield Β· Lesson 4.5

Performance Testing

In one lineTurn performance into a build gate with numbers, so regressions fail CI instead of shipping.

Where you've seen itA pull request that imports one convenience library, adds 180KB to the main bundle, and gets blocked before a human reads it.

  • Time15 min
  • DiagramBudget thresholds in CI + lab vs field
  • Chapter4 Β· Testing

Diagram

diagram
BUDGETS AS A GATE β€” the check runs on every PR

  metric              budget      this PR     result
  ─────────────────────────────────────────────────────
  LCP (p75, mobile)   ≀ 2.5s      2.4s        βœ“ pass
  INP (p75)           ≀ 200ms     180ms       βœ“ pass
  CLS                 ≀ 0.1       0.02        βœ“ pass
  main bundle (gzip)  ≀ 170KB     214KB       βœ— FAIL  ← blocks merge
  requests, 1st load  ≀ 30        27          βœ“ pass

  Fail the build, don't file a ticket. Tickets lose to features.
diagram
LAB vs FIELD β€” you need both, they answer different questions

  LAB (synthetic)                  FIELD (RUM)
  ──────────────────               ──────────────────
  Lighthouse CI, WebPageTest       real users, real devices
  same device + network each run   every device + network
  βœ“ comparable between commits     βœ“ tells you the truth
  βœ— one machine, one profile       βœ— can't run before merge

  Gate on LAB. Alert on FIELD. Never confuse the two.
diagram
WHY p75 AND NOT AVERAGE

  users sorted by load time
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  fast         median      p75          p95      slowest
                 1.9s      3.4s        7.1s

  average 2.4s "looks fine" while 25% of users wait 3.4s+
  and they are the ones who leave.

How it works

  1. Set budgets from current p75, slightly tighter β€” a budget nobody can pass gets disabled within a week.
  2. Run Lighthouse CI on a fixed device profile and throttled network so numbers are comparable commit to commit.
  3. Add a bundle-size check (size-limit, bundlesize) β€” it's faster, deterministic, and catches most regressions.
  4. Run three passes and take the median; single Lighthouse runs vary by 10–20%.
  5. Load-test the API separately (k6, Artillery) β€” a frontend that's fast at 10 users and dead at 10,000 is still broken.

Trade-offs

CheckSpeedCatches
Bundle size limitsecondsdependency bloat, bad imports
Lighthouse CI~1 minrender-blocking, unused JS, CLS
WebPageTestminutesreal device, filmstrip, waterfall
RUM (field)continuouswhat users actually experience

Interview angle

"How do you stop performance regressing over time?"

  • Make it a gate, not a goal: budgets fail CI so no regression merges silently.
  • Cheap deterministic checks (bundle size) on every PR, full Lighthouse on main, RUM alerting in production.
  • Track p75 on real devices β€” averages hide exactly the users who churn.

Recap

  • Budgets in CI beat performance sprints, because they prevent instead of repair.
  • Lab numbers gate merges; field numbers tell you the truth.
  • Median of three runs, p75 of real users.