Skip to content

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.

  1. Page inventory: list your page types with % of sessions and % of revenue influence (analytics: sessions by page template, and assisted‑conversion by template).
  2. 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.
  3. Device mix: device category and effective connection type distribution.
  4. Money line: sessions × conversion × AOV, and what +1% relative conversion is worth per year.
  5. 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.