How it was built
Building Formula One Explorer
The largest project on this site: 31 planned tasks across 8 phases, 26 commits, 96 unit tests, and a full product — learning platform, live race center, and 75 years of racing history — running on public APIs with zero configuration.
The idea
Formula One has two audiences that never get served by the same product: newcomers drowning in jargon, and fans who want timing screens and historical depth. The brief was one application that treats both as first-class — an interactive course with the rigor of a textbook, next to a live race center with the density of a broadcast timing feed. And one constraint above all: clone, install, run. No API keys, no database to provision, no accounts.
Under the hood
Three public APIs, one resilient fetch layer
All data comes from free, keyless APIs: Jolpica (the Ergast
successor) for every season since 1950, OpenF1 for live session
timing, and Open-Meteo for circuit weather. Every request goes
through a single fetchJson wrapper with timeouts, exponential-backoff
retries, and typed ok / error results — so a failing API degrades one
panel with an inline notice instead of crashing a page. Raw API shapes never leave
the API layer; pages only ever see typed domain models.
Tiered caching instead of a database
The insight that removed the database: completed seasons never change. Historical data is cached indefinitely, current-season data revalidates hourly, schedules every 15 minutes, and live timing is polled client-side every 4 seconds — each tier expressed as a Next.js fetch revalidation policy. Jolpica allows ~500 requests per hour; the cache tiers keep the app comfortably inside that while feeling instant.
The live timing engine
OpenF1 exposes raw event streams — positions, intervals, laps, stints, pit stops,
race control — that arrive unordered. A pure buildLeaderboard function
folds them into the timing screen: latest position per driver, formatted gaps
("+1.234", "+1 LAP"), current compound and tire age, stop counts. It was built
test-first, which caught a classic off-by-one (a tire fitted on lap 19 and running
to lap 31 has done 13 laps, not 12). Verification against the real API also caught
OpenF1 rate-limiting parallel bursts — the fix serializes the eight stream fetches
with 200 ms gaps and retries 429s.
The course as typed content
All 22 lessons — sections, quizzes, flashcards, misconceptions, ten hand-drawn SVG diagrams — live as typed TypeScript modules, validated by an integrity test: every lesson must have enough sections, valid quiz answer indices, resolvable "next lesson" links. Same pattern for the 100+ term glossary, corner-by-corner circuit guides, stylized track-map SVG paths, and 75 years of champions. Content bugs fail CI like code bugs.
Business logic as pure functions
Career statistics aggregation, championship progression math, the "who can still
win" simulator, a linear tire-degradation strategy model, cross-era comparison
normalization (per-start rates, so Fangio's 52-race career compares fairly with
Alonso's 400+), and the search index — all pure functions under
lib/services/, all unit tested. The UI is a thin layer over logic
that provably works.
Key design decisions
Keyless over "production stack"
The original spec suggested Supabase, PostgreSQL and Mapbox. Choosing public APIs plus cache tiers instead means anyone can run the full app in two commands — and there's nothing to leak, bill, or rotate.
Degrade panels, never pages
Every data call returns a typed result instead of throwing. A dead API turns into one inline notice; the other panels on the page keep working. The live center falls back from LIVE to REPLAY with an honest status badge.
TDD where math lives
Timing gaps, points accumulation, tire ages, title permutations — the places where silent bugs erode trust got failing-test-first treatment. 96 tests run in under a second.
Motion with an off switch
Track maps draw themselves in and leaderboard rows animate their overtakes — but every animation respects the OS reduced-motion preference plus an in-app toggle.
The Claude Code process
This was the most heavily planned build on the site: a brainstormed design spec
(two decisions put to me: keyless APIs vs. full backend, and phased-full scope vs.
core-only), then a 31-task implementation plan with per-task verification gates —
typecheck, lint, tests, production build — and a commit after every task. The final
phase ran the real app in a browser against live data, which is how the font
fallback bug, the OpenF1 rate limit, and a mislabeled theme toggle were caught
before shipping. The spec and plan are committed in the repo under
docs/superpowers/, so the whole process is auditable.