0.1 — The reference application: Aurora Market¶
Module 0 · Lesson 1 · 🟢 Foundational · ~15 min
What you'll learn¶
- The architecture, traffic shape, and page inventory we optimize for the rest of the course
- Baseline performance numbers per page type, and the targets we're driving to
- The three structural problems that most of this course exists to fix
Why a reference application¶
Performance advice without context is useless. "Use static generation" is right for a marketing page and wrong for a cart. "Memoize it" is right in a 3,000‑row grid and noise in a header. Every recommendation in this course is anchored to a concrete system with concrete constraints, so you can map it onto yours.
Aurora Market is fictional but assembled from the failure modes that show up over and over in real large commerce codebases. Its numbers are illustrative and internally consistent — you can follow the arithmetic in every business case. Where we cite published industry results, they're labelled as citations.
The business¶
| Dimension | Value |
|---|---|
| Category | Fashion + home goods, own‑brand and marketplace |
| Catalog | 2.4M SKUs · 380K parent products · 11K categories · 40 markets / 14 locales |
| Traffic | ~38M sessions/month · 68% mobile · 22% on 4G or worse · 31% on devices ≥3 years old |
| Commerce | AOV $92 · conversion 2.4% · ~$1.0B/yr GMV |
| Peak | Black Friday ≈ 14× median RPS; code freeze from Nov 1 |
| Team | 40 frontend engineers across 6 squads; no dedicated performance team |
The arithmetic you'll see repeatedly:
38,000,000 sessions/mo × 2.4% conversion × $92 AOV ≈ $83.9M/mo ≈ $1.007B/yr
→ +0.1pp conversion (2.4% → 2.5%) ≈ +$3.5M/mo ≈ +$42M/yr
→ +1% relative conversion lift ≈ +$839K/mo ≈ +$10.1M/yr
That last line is why a 300 ms LCP improvement is a funded project, not a chore.
The architecture¶
┌──────────────────────────────────────────┐
Browser ──────────▶ │ CDN (Fastly-style) │
│ · static assets, immutable, 1y │
│ · HTML: SWR for PLP/PDP, bypass for cart│
│ · surrogate keys per SKU/category │
└──────────────┬───────────────────────────┘
│ miss
┌──────────────▼───────────────────────────┐
│ Next.js 15 App Router (Node runtime) │
│ Kubernetes, 3 regions, 60–400 pods │
│ + middleware (geo, AB, auth cookie) │
└──────────────┬───────────────────────────┘
│
┌──────────────┬───────────────┼──────────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
GraphQL BFF Search svc CMS (SaaS) Pricing svc Payments SaaS
(catalog, (Algolia- (homepage, (promos, (Stripe-style)
cart, user) style) landing) segment px)
│
┌────┴─────┬──────────┐
▼ ▼ ▼
Catalog Inventory Orders
Postgres Redis Postgres
Client‑side third parties (all injected through one tag manager owned by marketing): analytics, consent banner, A/B testing, session replay, live chat, affiliate pixel, two ad retargeting tags, a reviews widget, and a "recently viewed" personalization SDK.
That tag manager is, on a typical Aurora PDP, 41% of main‑thread time and 58% of third‑party bytes. It is also the single hardest thing to change politically. This is normal. See 2.3 Third‑party scripts.
Page inventory¶
The unit of performance work is the page type, not the site. Aurora Market has seven that matter, with wildly different constraints:
| Page type | % sessions | % revenue influence | Cacheability | Dominant risk |
|---|---|---|---|---|
| Home | 14% | High (entry point) | Static + hourly ISR | Hero image, CMS payload, carousels |
| PLP / category | 27% | Very high | Static shell + dynamic facets | Long lists, filter interactions (INP), image grid |
| PDP | 31% | Highest | Static shell + dynamic price/stock | LCP image, variant switching, reviews widget |
| Search results | 12% | High | Not cacheable per‑query | Query latency, typeahead INP |
| Cart | 8% | Critical | Never cacheable | Personalized data, promo recalculation |
| Checkout | 5% | Critical | Never cacheable | Payment SDKs, form INP, no room for error |
| Account / orders | 3% | Low | Never cacheable | Authenticated data waterfalls |
Rule from this table: 72% of sessions land on Home/PLP/PDP, which are mostly cacheable. That's where rendering architecture wins. Cart/checkout are 13% of sessions but ~100% of completed revenue, so they get a different playbook: fewer bytes, zero risk, no third parties.
Baseline: where Aurora starts¶
Field data (75th percentile, mobile, 28 days, from CrUX‑style RUM):
| Page type | LCP | INP | CLS | TTFB | JS transferred |
|---|---|---|---|---|---|
| Home | 4.1 s 🔴 | 240 ms 🟠 | 0.21 🔴 | 780 ms 🟠 | 612 KB |
| PLP | 3.8 s 🔴 | 410 ms 🔴 | 0.14 🟠 | 690 ms 🟠 | 688 KB |
| PDP | 4.6 s 🔴 | 320 ms 🔴 | 0.26 🔴 | 910 ms 🔴 | 734 KB |
| Search | 3.4 s 🟠 | 380 ms 🔴 | 0.09 🟢 | 1,240 ms 🔴 | 651 KB |
| Cart | 2.9 s 🟠 | 190 ms 🟢 | 0.05 🟢 | 850 ms 🟠 | 598 KB |
| Checkout | 3.2 s 🔴 | 260 ms 🟠 | 0.03 🟢 | 820 ms 🟠 | 812 KB |
Thresholds: LCP 🟢≤2.5s 🟠≤4.0s · INP 🟢≤200ms 🟠≤500ms · CLS 🟢≤0.1 🟠≤0.25 · TTFB 🟢≤800ms.
Targets by end of the course:
| Metric | Baseline (p75 mobile) | Target | Mechanism |
|---|---|---|---|
| PDP LCP | 4.6 s | ≤ 2.2 s | Static shell + PPR, image pipeline, third‑party diet |
| PLP INP | 410 ms | ≤ 180 ms | State architecture, transitions, virtualization |
| PDP CLS | 0.26 | ≤ 0.05 | Reserved space, font fallback metrics, no late banners |
| Search TTFB | 1,240 ms | ≤ 500 ms | Streaming shell, parallel fetches, edge cache |
| JS on PDP | 734 KB | ≤ 260 KB | RSC boundaries, dependency diet, facades |
The three structural problems¶
Ninety percent of Aurora's performance debt traces back to three architectural decisions. Most large commerce codebases have the same three. Each gets a dedicated module.
Problem 1 — Everything is dynamic because something is dynamic¶
The PDP shows a personalized price band ("Members save 15%"). Because of that one component,
the entire route opted out of static rendering: cookies() is read at the top of the layout, so
every request re‑renders 40+ components server‑side, hits the BFF 6 times, and can't be cached at
the CDN. TTFB is 910 ms and the CDN hit ratio on HTML is 4%.
The fix is not to remove personalization — it's to isolate it. → 3.6 PPR & personalization, 3.4 Caching layers
Problem 2 — The client bundle carries the whole app to every page¶
A shared <AppProviders> in the root layout is a Client Component wrapping the tree. It imports
the cart store, the analytics SDK, the i18n runtime, the design system barrel file, and a date
library — so every route, including the mostly‑static homepage, ships and hydrates all of it.
The PDP's 734 KB is 61% shared chunk.
The fix is boundary hygiene, not micro‑optimization. → 3.2 Server Components & boundaries, 4.3 Dependency diet
Problem 3 — Interaction cost grows with catalog size¶
Toggling a PLP filter re‑renders 120 product cards, each recomputing price formatting and badge logic, inside a context provider that also holds the mini‑cart. p75 INP on PLP is 410 ms; p95 is 1.2 s on mid‑tier Android.
The fix is state architecture plus concurrency, not React.memo sprinkled on cards.
→ 5.3 State architecture,
5.5 Concurrent React
The device reality¶
Never optimize for your laptop. Aurora's real mix:
| Segment | Share | Representative device | CPU factor vs M‑series laptop |
|---|---|---|---|
| High‑end mobile | 24% | Recent flagship | ~2× slower |
| Mid‑tier Android | 38% | 3‑year‑old mid‑range | ~6× slower |
| Low‑end mobile | 6% | Budget device, 3–4 GB RAM | ~12× slower |
| Desktop | 32% | Office laptop | ~1.5× slower |
Set your DevTools CPU throttling to 4×–6× and leave it there. A 40 ms task on your machine is a 240 ms task for the median Aurora shopper — over the INP budget on its own.
Lab 0.1 — Build your own reference sheet¶
Before lesson 1.1, produce the equivalent of this page for your application. You'll use it in every later module.
- Page inventory: list your page types with % of sessions and % of revenue influence (analytics: sessions by page template, and assisted‑conversion by template).
- Baseline table: p75 LCP/INP/CLS/TTFB per page type from field data. If you have no field data, stop and do 9.1 RUM implementation first — everything downstream depends on it.
- Device mix: device category and effective connection type distribution.
- Money line: sessions × conversion × AOV, and what +1% relative conversion is worth per year.
- Name your three structural problems. If you can't name them yet, you will be able to after Module 3 and 4 — leave it blank and come back.
Keep it in your repo as docs/performance/baseline.md and date it. You will re‑measure quarterly.
Checklist¶
- Page types enumerated with traffic and revenue weight
- p75 field metrics per page type recorded, with a date
- Device/network mix known; DevTools throttling set to match the median user
- Value of +1% relative conversion written down and agreed with a stakeholder
- Third‑party inventory listed with an owner name against each
Next: 0.2 Impact/effort matrix — everything in this course, scored and ranked.