Skip to content

9.4 — Scaling the practice

Module 9 · Lesson 4 · 🟡 Intermediate · ~30 min

What you'll learn

  • Ownership models that work at 40 engineers across 6 squads
  • Scorecards, review rituals, and how to make performance visible without nagging
  • Getting the work funded, repeatedly
  • The failure modes of performance programs, and how they die

Everything technical in this course is achievable by one good engineer in a quarter. Keeping it that way for three years is an organizational problem.


The three ownership models

Model How it works Good for Fails when
Central team A dedicated perf team owns the metrics and does the work Fast initial wins; deep expertise Becomes a bottleneck; squads stop caring
Distributed Each squad owns its pages' metrics Scales; ownership is real Inconsistent skill; no shared infrastructure
Guild + platform A part‑time guild owns tooling, standards, and coaching; squads own their metrics Most large orgs Needs a committed sponsor

The model that works at Aurora's scale:

Platform team (2 engineers, ongoing)
  · owns RUM, dashboards, CI gates, budgets
  · builds shared tooling
  · does NOT do other squads' optimization work

Performance guild (5 engineers, ~10% time, rotating quarterly)
  · reviews budget exceptions
  · runs the quarterly gate review
  · consults on hard problems
  · maintains this course's docs

Each squad
  · owns the vitals for its page types
  · has the metrics on its own dashboard and in its own goals
  · fixes its own regressions

The critical boundary: the platform team builds the instrument, the squads play it. A central team that does everyone's optimization work creates a permanent dependency, and the moment it's reassigned, the site regresses within two quarters.


The scorecard

Visibility beats process. One page, updated weekly, visible to everyone including leadership.

# Performance scorecard — week of 2026-03-16

## Core Web Vitals (p75, mobile, 28d)

| Page type | Owner | LCP | INP | CLS | Trend | Status |
|---|---|---|---|---|---|---|
| Home | @home-squad | 2.1s 🟢 | 148ms 🟢 | 0.02 🟢 | ▼ | ✅ |
| PLP | @discovery | 2.4s 🟢 | 118ms 🟢 | 0.03 🟢 | ▼ | ✅ |
| PDP | @pdp-squad | 1.9s 🟢 | 142ms 🟢 | 0.01 🟢 | ─ | ✅ |
| Search | @discovery | 2.6s 🟠 | 165ms 🟢 | 0.04 🟢 | ▲ | ⚠️ |
| Cart | @checkout | 1.8s 🟢 | 131ms 🟢 | 0.02 🟢 | ─ | ✅ |
| Checkout | @checkout | 1.7s 🟢 | 128ms 🟢 | 0.01 🟢 | ─ | ✅ |

## Budgets

| Route | JS (gz) | Budget | Headroom |
|---|---|---|---|
| /p/[slug] | 247 KB | 260 KB | 5% ⚠️ |
| /c/[slug] | 218 KB | 240 KB | 9% |
| /checkout/[step] | 162 KB | 180 KB | 10% |

## Active exceptions

| Route | Over by | Reason | Expires | Owner |
|---|---|---|---|---|
| /checkout/payment | +34 KB | EU payment SDK (regulatory) | 2026-05-15 | @checkout |

## This week
- ✅ Search facet caching shipped: search TTFB 340ms → 180ms
- ⚠️ Search LCP up 190ms — new hero image from the spring campaign; @discovery investigating
- 📋 PDP has 5% budget headroom — next feature needs a paydown plan first

What makes a scorecard work:

  1. A named owner per row. Not a team — a squad with an on‑call rotation.
  2. Trend arrows, not just current values. Direction matters more than position.
  3. Exceptions visible, with expiry dates. Debt you can see is debt that gets paid.
  4. Headroom, not just compliance. "5% headroom" is a warning; "under budget" is not.
  5. One page. If it takes scrolling, it isn't read.

Rituals

Four, and no more. Every ritual you add costs everyone's attention.

Weekly: 15‑minute perf review

Attendees: guild + one representative per squad. Agenda: 1. Scorecard changes since last week (5 min) 2. Any regression in triage (5 min) 3. Upcoming work with perf implications (5 min)

Not a status meeting. If nothing regressed and nothing is planned, it ends in 5 minutes.

Per‑PR: automated

The CI comment is the review (9.2). No human process needed for the common case.

Per‑quarter: gate review + budget review

  • Which gates caught real problems? Which produced noise? Delete the noise.
  • Are budgets still right? Has the device/network mix changed?
  • Re‑baseline the reference sheet (0.1).

Per‑feature: the design review question

Add one question to your existing design review template:

### Performance impact
- Which page types does this touch, and what are their current budgets?
- Estimated bundle impact (KB, client, gzipped):
- Does it add a client boundary, a third-party origin, or a dynamic API read?
- Does it change what the LCP element is, or add anything that loads late?
- How will we measure whether it regressed anything?

Four questions in a template catches more regressions than any amount of after‑the‑fact review, because it moves the conversation to before the code exists.


Making it part of the job

Performance dies when it's someone's side project. Three mechanisms:

1. Put it in squad goals. Not "improve performance" — a specific number: "PDP mobile p75 LCP ≤ 2.2 s by end of Q2, held for 4 consecutive weeks."

2. Make it a launch requirement. A new page type doesn't ship without budgets defined, RUM dimensions wired, and a Lighthouse CI entry.

3. Track regression MTTR. How long from detection to resolution? A squad with a 3‑week MTTR doesn't actually own its metrics, whatever the org chart says.

Regression MTTR by squad, Q1
@pdp-squad      1.2 days
@discovery      2.8 days
@checkout       0.8 days
@home-squad    11.4 days   ← this is the conversation to have

Getting it funded, repeatedly

The first project is easy to fund — the site is visibly slow and someone senior has noticed. The fifth project is hard, because the site is now fine and the work looks like gold‑plating.

The framing that keeps working:

## Q3 performance investment: 8 engineer-weeks

### What we protected last quarter
Without the budget gates and CI checks, we would have shipped:
- 340 KB of bundle growth across 23 PRs (caught and reduced to 41 KB)
- 4 routes flipping from static to dynamic (caught pre-merge)
- 2 third-party tags added without review

Modelled impact if unprevented: PDP LCP +600-900ms, roughly -1.5% to -2.5%
relative conversion ≈ $15-25M/yr.

### What we shipped
- Search TTFB 1,240ms → 180ms → measured +0.8% relative conversion on search
  sessions in a 14-day holdback ≈ $2.1M/yr
- bfcache restore rate 4% → 81% → back navigation 1,840ms → 12ms

### What we're asking for
1. Maintain the gates and dashboards (2 wks/quarter, ongoing)
2. INP work on PLP: 118ms → target 90ms (3 wks)
3. Multi-region for APAC: TTFB 890ms → target 150ms (3 wks, 14% of revenue)

### What happens if we don't
Historical drift without active maintenance: 10-20% per year.
Our gates prevent most of it, but they need an owner. Two quarters
unattended and the gates get disabled during a crunch.

Three arguments that land:

  1. Prevention has value, and it's measurable. Count what the gates blocked. This is the only way to get credit for regressions that didn't happen.
  2. Infrastructure savings are hard numbers. Finance likes "$25K/mo less origin compute" more than a conversion projection.
  3. Report null results. A team that says "we made it 2.4 s faster and conversion didn't move" is a team whose next number is believed.

How performance programs die

Five failure modes. Recognize them early.

1. The hero engineer leaves

One person did all of it and it was in their head. Prevention: document decisions in the repo (this course is that artifact), rotate the guild, and make sure at least two people can debug the RUM pipeline.

2. The gates get disabled during a crunch

Peak season, a critical feature is over budget, someone disables the check "temporarily". It never comes back. Prevention: an exception process that's faster than disabling the gate. If getting an exception takes 10 minutes and disabling the gate takes 2, people will disable it.

3. Metrics go green and attention moves on

Everything's fine, the dashboards stop being watched, and 18 months later you're back where you started. Prevention: alerts on change, not on thresholds (9.1). A green dashboard nobody reads still pages you when it regresses.

4. Ownership is nominal

The org chart says the squad owns it; in practice the platform team fixes everything. Prevention: the platform team must decline to do squads' optimization work. Coach, don't fix. This is uncomfortable and it's the single most important boundary in the model.

5. The business stops believing the numbers

Usually caused by overclaiming — a projected $40M that didn't materialize. Prevention: ranges not point estimates, discount correlational evidence, report null results, and always pre‑register the measurement plan (1.1).


Onboarding new engineers

Performance knowledge doesn't transfer by osmosis. Make it explicit:

## New engineer performance onboarding (half a day)

1. Read: docs/00-orientation/01-reference-application.md (our baseline)
2. Read: docs/01-foundations/05-performance-budgets.md (our budgets and why)
3. Do: record a throttled trace of our PDP at 4× CPU. Write down what surprised you.
4. Do: run `ANALYZE=true npm run build` and find the three biggest client dependencies.
5. Read: docs/03-rendering/02-server-components-and-boundaries.md (our biggest lever)
6. Shadow: sit in on one weekly perf review.
7. Know where things are: the scorecard, the dashboards, the triage runbook.

Step 3 is the important one. An engineer who has personally felt the site at 4× CPU throttling writes different code afterwards.


Documentation that stays true

Documentation rots. Three practices that slow it:

  1. Put decisions in the repo, next to the code they describe. A wiki page about your caching strategy will be wrong within six months; a comment in lib/cache.ts gets updated with the code.
  2. Date everything. Baselines, budgets, and measurements all have a "measured on" date. A number with no date is untrustworthy after a quarter.
  3. Make docs part of the definition of done. Changing the rendering strategy for a route means updating the route‑mode gate and the architecture note in the same PR.
<!-- docs/performance/decisions/003-pdp-ppr.md -->
# ADR 003: Partial Prerendering for the PDP

**Date:** 2026-02-20 · **Status:** Accepted · **Owner:** @pdp-squad

## Context
PDP was fully dynamic because of the member price band. TTFB p75 910ms,
CDN HTML hit ratio 4%, origin compute $34K/mo.

## Decision
PPR: static shell (images, copy, specs, list price) + three dynamic holes
(member price, store stock, cart actions).

## Consequences
- TTFB 910ms → 85ms; origin compute $34K → $9.2K/mo
- Shell must never contain personalized data — enforced by
  tests/ppr-shell-isolation.spec.ts, which MUST stay in CI
- Shell invalidation must accompany data invalidation (lib/purge.ts)

## Revisit if
- Personalization requirements grow beyond 4 holes
- Next.js changes the PPR API surface (currently experimental)

Architecture Decision Records are cheap to write and enormously valuable when someone asks "why is this like this?" two years later.


Aurora's program, 18 months in

Start 18 months
PDP mobile p75 LCP 4.6 s 1.9 s
PLP mobile p75 INP 410 ms 118 ms
Sessions with all CWV "good" 22% 81%
Origin compute $34K/mo $9.2K/mo
Engineers who can debug a perf regression 2 19
Regressions caught pre‑merge 0 47
Regressions reaching production ~1/month ~1/quarter
Measured conversion lift (cumulative, A/B) +4.8% relative

The last row is ~$48M/yr at Aurora's volume. The row above it — regressions reaching production dropping 12× — is what makes the last row durable rather than a one‑quarter spike.

The row that matters most is "engineers who can debug a perf regression: 2 → 19." Everything else follows from that, and it's the only one that survives reorganizations.


Common mistakes

Mistake Consequence
A central team doing all the work Permanent bottleneck; no squad ownership
No named owner per page type Nobody fixes regressions
Too many rituals Attention exhaustion; all of them get skipped
Scorecard longer than one page Nobody reads it
Overclaiming business impact Credibility loss; future work unfunded
No exception process Gates get disabled instead
Knowledge in one person's head Program dies when they leave
Undated documentation Trusted when it's wrong
Never reporting null results The numbers stop being believed

Lab 9.4 — Set up the practice

  1. Assign owners. Every page type gets a named squad. Publish the list.
  2. Build the scorecard. One page, weekly, automated from your RUM. Post it where leadership sees it.
  3. Add the four design‑review questions to your existing template.
  4. Start the weekly 15‑minute review. Cancel it if it runs long twice in a row — that means it's become a status meeting.
  5. Write your first three ADRs for decisions already made.
  6. Write the onboarding checklist and run it with the next new hire.
  7. Track regression MTTR by squad for a quarter. Have the conversation the data suggests.
  8. Schedule the quarterly gate and budget review in the calendar now, for the next four quarters.

Checklist

  • Ownership model chosen; platform team does not do squads' optimization work
  • Named owner per page type
  • One‑page scorecard, weekly, visible to leadership
  • Exceptions visible with expiry dates
  • Four rituals maximum
  • Performance in squad goals as specific numbers
  • Regression MTTR tracked per squad
  • ADRs for significant decisions, dated
  • Onboarding checklist including a throttled trace
  • At least two people can debug the RUM pipeline
  • Funding case built on prevention, savings, and honest measurement

Next: Module 10 — Page playbooks