The Method
A frontend system is a set of numbers and the simplest design that meets them, revised each time a number grows past what the design can carry, with the trade-off named every time. This part is what makes frontend design its own discipline, the loop every part runs, the budgets the numbers are measured against, the five currencies a trade-off is paid in, and the nine systems ahead.
What a frontend system design is
"Design the transactions page. Ops needs to filter, sort and export. It should be fast."
That is a brief, not a design, and it is how every system in this course arrives. A frontend system design is the sequence of decisions that turns a brief into something that meets specific numbers on specific devices, revised each time a number grows past what the current design carries. The backend design disciplines apply (requirements, estimation, the simplest thing, the increment, the trade-off); the frontend adds the device's budgets: heap, main-thread time, bytes over the connection, decode time, and the milliseconds a person notices.
- The machine is the user's, not yours: a mid-range phone with a 4 GB heap limit shared with the DOM, one main thread, a 4G connection with 80 ms round trips, and a battery. You do not get to add a server.
- The budgets are perceptual: 100 ms to feel instant, 16.7 ms per frame, a layout shift the eye catches. Throughput matters less than latency at the moment of interaction.
- State is distributed across two machines (the server's truth and the client's copy) and sometimes more (other users, other tabs, offline queues), and the client is the one that can be wrong.
- Bytes are a cost paid per user per load, which makes "add a library" a design decision.
- The failure modes are visible: a frozen tab, a spinner, a jump, a stale number. The user sees the design's limits directly.
- Nine systems, each from a brief and a set of numbers, grown through increments as the numbers change, with the trade-off named each time.
- The same loop every time (next chapter), so that after a few parts the loop is yours and the systems are examples.
- Mechanisms from the sibling courses: the browser course for the pipeline and budgets, the JS course for memory and parse, the algorithms course for windowing and diffing and scheduling, the React course for state and data. This course points at them rather than repeating them.
The loop: brief, questions, numbers, v1, the break, v2
Every part runs one loop. It is the Core Banking module's rounds applied to the client: a brief, the questions that pin it down, requirements with numbers, the simplest design that meets them, the number that breaks it (shown with arithmetic), the fix, and the cost of the fix. Then the next number. The design document is the sequence of rounds.
- The brief. Vague by nature: what, for whom, with adjectives. Accept it as the starting point; do not design from it.
- The questions. How many (rows, users, items, messages) today and in two years. How big (bytes per thing). How fresh (seconds, minutes, never). How fast (the slowest acceptable response per action). On what (devices, connections, browsers). What happens when it is wrong (the cost of staleness or a lost update). What must work offline. These are the first deliverable.
- Requirements with numbers. Functional (what it does) and non-functional with a number each (how much, how fast, how fresh, how reliable). "Fast" is replaced by "filter in under 200 ms at 10M rows on an office laptop".
- v1. The simplest design that meets today's numbers. Often embarrassingly simple: fetch everything, hold it in memory, render a window. Write down which number breaks it.
- The break. One number grows. Show, with arithmetic, how v1 fails: bytes × rows exceeds the heap; items × nodes exceeds the DOM budget; messages × size exceeds the connection; work × time exceeds a frame. Name the symptom the user would see.
- v2 and its trade-off. The smallest change that carries the new number, with the cost named in one of five currencies (next chapter). Then v2 is the new v1, and the loop continues.
- Stop when the numbers stop growing or when the next trade-off costs more than the number is worth. Not every system needs v5.
the shape of a round, as the parts write it: number: 10,000 rows → 2,000,000 rows v1: fetch all, filter in memory, virtualised table break: 2M × 200 B = 400 MB JSON → ~1.2 GB parsed → the tab dies; before that, a 60 s fetch v2: server-side filter/sort/paginate; the client holds one page pays: +100 ms per filter (latency) · a query protocol + indexes (complexity, money) · no cross-page client ops (capability) next break: "select all" across pages; freshness under a minute with 200 users; an export of 100k rows one number, one break, one fix, one cost. the final design is the sum.
The numbers, and the budgets they are measured against
A frontend design is decided by comparing the brief's numbers to the device's budgets. The budgets are physical constants of the platform (with a device-class multiplier), and knowing them is what lets you say "v1 works until 2M rows" before writing v1.
| Budget | Laptop (office) | Mid-range phone | Where it bites | Course |
|---|---|---|---|---|
| Heap usable by your data | ~1 GB of a 4 GB limit (DOM and framework take the rest) | ~300 MB of a 1 to 2 GB limit | Rows in memory; parsed JSON is ~3× its text size | JS P5 |
| JSON parse | ~100 MB/s | ~20 MB/s | A 50 MB payload is 2.5 s of main thread on a phone | JS P6 |
| Network | Fibre: 100+ Mbps, 1 to 5 ms RTT | 4G: 10 to 50 Mbps, 50 to 100 ms RTT; 3G worse | Payload size per interaction; dependent requests | Browser P1 |
| Main thread per frame | ~10 ms of 16.7 after the browser's work | ~5 to 8 ms (slower CPU, same frame) | Anything per frame: scroll handlers, live updates, animations | Browser P6 |
| Interaction response | 100 ms instant · 200 ms INP good · 1 s with feedback · 10 s lost | Every click, keystroke, drag | Browser P12 | |
| DOM nodes | ~10k before layout is visibly slow | ~3k | Lists, tables, trees without windowing | Algorithms P1 |
| Layout and paint area | Full-viewport repaint ~1 to 5 ms; with blur or shadow ×10 | Live updates that invalidate large areas | Browser P5, Algorithms P4 | |
| Image decode | 12 MP JPEG ~15 ms | ~40 to 60 ms | Galleries; many images in one frame | Browser P11 |
| Worker startup and messaging | Start ~30 to 50 ms; clone ~0.5 GB/s; transfer O(1) | Offloading compute; chunked pipelines | Browser P7 | |
| WASM | Streaming compile ~tens of MB/s; near-native loops; a call boundary ~ns | Codecs, parsers, numeric kernels | Browser P7 | |
| Storage | IndexedDB writes ~MB/s; OPFS sync access much faster; localStorage 5 MB | Offline data; large caches | Browser P8 | |
| Concurrent connections | 6 per origin (HTTP/1.1); multiplexed (HTTP/2, 3); one WebSocket is one connection | Real-time fan-in; many small requests | Browser P1, FSD M8 |
Naming the trade-off
An increment that is not costed is a surprise waiting for production. Every increment in this course is written as a sentence that names what it buys (the number) and what it pays, in one or more of five currencies. The currencies are the same for every system; the amounts differ.
- Bytes: bundle, payload, assets. Paid by every user, every load, against their connection and parse budget.
- Complexity: code, states, protocols, failure modes the team now owns. Paid forever, against the team's capacity to understand it.
- Consistency: what the user may see that is not true: stale, optimistic, merged, locally validated. Paid in trust, against the cost of being wrong for this data.
- Latency: round trips and critical-path work added. Paid per interaction, against the 100 / 200 / 1,000 ms thresholds.
- Money: servers, bandwidth, pipelines, infrastructure, people. Paid by the business, against what the feature earns.
- "vN buys ___ (the number) and pays ___." With each blank filled by a currency and an amount: "+100 ms per filter", "a 300 KB library", "one-minute staleness on counts", "a transcoding pipeline at ~$X per hour of video".
- If a blank cannot be filled, the increment is not designed. If the amount is unknown, measure it (a spike, a benchmark, a quote) before committing.
- If the cost exceeds the number's value, do not build it; find a cheaper increment or accept the limit. "We will not support select-all over 1M rows; the UI says so" is a design.
- Client memory versus server round trips (M1, M6, M7): hold more locally for instant interactions, or fetch per interaction for scale.
- Main thread versus worker boundary (M2, M4): responsiveness for the cost of serialisation and a message protocol.
- Bytes versus quality versus decode (M3): smaller images cost quality; better codecs cost decode time or a shipped decoder.
- Consistency versus availability (M5, M7, M8): show something possibly stale, or show nothing until sure.
- Latency versus correctness (M9): check locally and show now, or wait for the server and show the truth.
The nine systems, and how to read them
- M1 Data-intensive: a transactions table from 10k rows to 10M. Memory, pagination, normalisation, server state, cross-page selection, exports, freshness.
- M2 Compute-intensive: a report generator and a file parser that must not freeze the tab. Slicing, workers, WASM, progress, cancellation, memory limits.
- M3 Media-intensive: a photo and video product. Responsive images, uploads with resumability, client-side transcoding versus a pipeline, adaptive streaming, decode budgets.
- M4 Visualisations and dashboards: live charts at 60 fps. Canvas versus SVG versus WebGL, data reduction, update batching, layers.
- M5 Change-intensive: a collaborative document. Last-writer-wins to OT to CRDT, presence, conflict UI, offline edits and sync.
- M6 Lists at scale: a million-item list with selection, variable heights, infinite scroll, keyboard navigation, stable identity across refetches.
- M7 Social media: a feed. Ranking the client sees the end of, fan-out, infinite feeds with media, notifications, the social graph on the client.
- M8 Real-time: the subscription layer under M5, M7 and M9. WebSockets versus SSE, backpressure, reconnection, ordering, presence, at 100k concurrent.
- M9 Trading: order books and price streams at tens of updates per second per instrument, order entry with client-side risk checks, a latency budget per hop, degraded modes.
- Read the brief and write your own questions before reading the course's. Compare.
- Do the arithmetic for each break yourself from the numbers given; the figures show the course's.
- At each v2, write the trade-off sentence before reading it.
- Then apply the loop to a system you own: its brief, its numbers, its v1, its next break. That is the exercise at the end of every part.