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:
- A named owner per row. Not a team — a squad with an on‑call rotation.
- Trend arrows, not just current values. Direction matters more than position.
- Exceptions visible, with expiry dates. Debt you can see is debt that gets paid.
- Headroom, not just compliance. "5% headroom" is a warning; "under budget" is not.
- 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:
- 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.
- Infrastructure savings are hard numbers. Finance likes "$25K/mo less origin compute" more than a conversion projection.
- 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:
- 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.tsgets updated with the code. - Date everything. Baselines, budgets, and measurements all have a "measured on" date. A number with no date is untrustworthy after a quarter.
- 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¶
- Assign owners. Every page type gets a named squad. Publish the list.
- Build the scorecard. One page, weekly, automated from your RUM. Post it where leadership sees it.
- Add the four design‑review questions to your existing template.
- Start the weekly 15‑minute review. Cancel it if it runs long twice in a row — that means it's become a status meeting.
- Write your first three ADRs for decisions already made.
- Write the onboarding checklist and run it with the next new hire.
- Track regression MTTR by squad for a quarter. Have the conversation the data suggests.
- 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