Command Palette

Search for a command to run...

Hectal
PHASE 18Advanced ~31 min· topic 3 of 5

Topic 18.3

Frontend System Design: a Metrics Dashboard

In one line

A frontend system-design round asks you to plan a whole product screen, not write one function. You practise the order of a good answer on a live metrics dashboard: requirements, data flow, rendering choice, real-time updates, states, accessibility and a performance budget with numbers.

Think of it like this

An architect's first meeting. They do not start with bricks. They ask who will live in the house, how many people, what the land is like and what the budget is, then sketch rooms and the pipes between them. A system-design round is the same: questions first, a sketch second, details where the interviewer points.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

functional requirement
What the product must do: show the stations that are empty, filter by district.
non-functional requirement
How well it must do it: how fast, how fresh, how many viewers, how accessible.
data flow
The path the numbers take from where they are stored to the pixels on screen.
polling
Asking the server for the data again every N seconds.
SSE (server-sent events)
One long-lived HTTP connection over which the server pushes updates to the page.
stale data
Data that is older than it should be. A good screen says so instead of hiding it.
performance budget
A written limit (seconds, kilobytes, milliseconds per frame) that the team agrees not to exceed.

Step by step

011. Ask, then write the numbers down

Five questions cover most of it. Who watches, and on which screens? How fresh must the data be? How much data is there, today and in a year? What must work offline or when the server is slow? Who must be able to use it with a keyboard or a screen reader?

For Lumen: about 50 viewers (10 wall screens, 40 phones), 25 stations that change every 10 seconds, one year of history, and Meera among the daily users. Write them where the interviewer can see them.

022. Draw the data flow

One picture, left to right. The status.json snapshot and the history files come from storage. The browser fetches them, cleans them (Topic 4.4), keeps them in one state object, and each chart draws from that state. Filters live in the same state, and in the URL, so a view can be shared (Topic 16.2).

2. Draw the data flowdiagram
Rendering diagram…

033. Choose the rendering, chart by chart

KPI tiles and the rides line: SVG, because the marks are few, labelled and focusable. The trips map with thousands of dots: Canvas, with a quadtree for hover and an accessible summary beside it. Say the crossover is measured on a real device (Topic 12.1), not assumed.

044. Decide how data arrives

Polling a 1.3 KB file every 10 seconds is cheap (the first example computes 458 KB per hour per open tab). It is simple, cacheable and robust. Move to SSE only when the freshness requirement is under a few seconds or the viewer count makes polling costly. Add Cache-Control so a CDN can answer most polls, and a timeout and a retry with growing delays.

Late and duplicate data are normal. Insert points by time, ignore duplicates, keep a window. The second example does this in eight lines.

055. List the states, then handle each one

Draw the state table: loading, empty, ok, stale, error with old data, error without data. Each gets a defined look. A chart whose request failed keeps its last good picture and shows an "offline" badge, so the wall screen never goes blank (Topic 16.4).

066. Add accessibility and the performance budget

Accessibility: a sentence stating each chart's finding, a table twin, arrow-key navigation, and no colour-only signals. Budget: first useful screen within about 2.5 seconds on a mid-range phone, interactions answered within 200 ms, redraws inside one frame, and a recorded JavaScript size limit for the chart code, checked in the build (Topic 16.5).

Close by saying what you would build first, what you would measure, and what you left out on purpose. That is how a senior answer ends.

Try it yourself

  1. 1

    Shrink the window

    In the second example, change size = 5 to size = 3. The window is 7,8,9. A smaller window uses less memory and draws faster, but the line shows less history. Choosing the window size is a requirement question: "how many minutes must the live chart show?"

  2. 2

    Add the case the table forgot

    Add { request: "loading", rows: [1, 2], ageSeconds: 3 } to cases. It prints ok: draw 2 points for a request that is still loading, because the first check only handles rows === null. Decide what the screen should do while refreshing with old data, and fix the function: the usual answer is to keep drawing and show a small spinner.

Code & diagrams

Numbers for the answer: how big is the data, and what does a poll cost? New tab

Sizes are the uncompressed text, before gzip. In the interview, say that: servers compress text, so the real transfer is several times smaller. The decision they support (poll the small file, aggregate the big one) stays the same.

Console

(nothing was printed)

Expected console output

stations.csv            1.4 KB, 25 rows
daily.csv              13.1 KB, 365 rows
trips-10k.csv         475.9 KB, 10000 rows
status.json             1.3 KB
ardenvale.topojson      2.6 KB
status.json every 10 s: 458 KB per hour per open tab
10 wall screens + 40 phones: 5 requests per second
trips-10k.csv is 36x the size of daily.csv
The live part: a rolling window and every screen state New tab

The events arrive as 1, 2, 4, 3, 4, 9, 5, 6, 7, 8: out of order, with a duplicate, and with a gap. The window ends up sorted, de-duplicated and 5 long.

Console

(nothing was printed)

Expected console output

window: 5,6,7,8,9
a point older than the window is dropped: 5,6,7,8,9
loading no data  -> loading: show a skeleton
error   no data  -> error: message and a retry button
ok      0 rows   -> empty: say what is missing and why
ok      3 rows   -> ok: draw 3 points
ok      3 rows   -> stale: badge 'data is 95 s old'
error   3 rows   -> stale: keep the old chart, badge 'offline, last update 95 s ago'

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget to sort late points

In pushPoint, replace const next = [...win, point].sort((a, b) => a.t - b.t); with const next = [...win, point];.

terminal
$ Console
── what you'll see ──
window: 9,5,6,7,8
a point older than the window is dropped: 5,6,7,8,1

Myth vs fact

Myth

A system-design round is only for backend engineers.

Fact

Frontend rounds ask for the same thinking: requirements, data flow, failure, scale and trade-offs, with the browser, the network and the user's device as the constraints.

Myth

Real-time means WebSockets.

Fact

Real-time means the data is fresh enough for the decision. For a screen that changes every 10 seconds, polling a small cached file is often the better design: simpler, cheaper and easier to debug.

Myth

Performance is something you check at the end.

Fact

Budgets are set at the start and measured at every step, because the biggest costs (the data size, the mark count, the re-render rate) are design decisions.

Interview problem

The problem

Design the Lumen live operations dashboard

Design a dashboard for Lumen Bikes' operations team. It runs on a wall screen all day and on phones. It shows where the bikes are, which stations are nearly empty, the day's rides so far and the year's trend. Walk through your design.

You're given

  • About 50 viewers: 10 wall screens and 40 phones
  • 25 stations with a status update every 10 seconds
  • A year of daily history and 10,000 trips for the explorer
  • The wall screen runs for weeks: no blank boxes, no leaks
  • Usable by a screen-reader user

The interviewer follows up

01

The wall screen's memory grows all day. Where do you look?

02

Meera says the live announcements are too frequent. What do you change?

03

How would you add a second city with 400 stations?

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    State the failure you most worry about and how you detect it: "if the poll fails three times we show 'offline', and we send an event to monitoring, because a wall screen showing old data silently is worse than a clear error". Interviewers listen for observability, not only features.

  • ▸

    The URL is part of the design. Filters and the selected range in the query string make views shareable and make the back button work. It also gives you a free way to reproduce a bug report: "open this link".

  • ▸

    Say what you would not build in the first version: write access, saved views, per-user settings. Cutting scope on purpose, and saying so, shows judgement.

  • ▸

    If your team builds this screen in React, the React course's Topic 25.8 (Design a Data Dashboard) runs the same round with a shared query cache, one error boundary per widget and filters in the URL. Read it after this topic: the data flow here is the same, and React supplies the machinery for the state box.

Remember this

  1. 1

    The problem this solves: asked to "design a metrics dashboard", many candidates start with a chart library and run out of time before saying where the data comes from, what happens when it fails, or how many viewers there are. A fixed order of answer keeps every important part in the room, and lets you go deep where the interviewer is curious.

  2. 2

    Start with requirements and numbers. Functional: which questions must the dashboard answer (where are the bikes, which stations are empty, how did the year go)? Non-functional: how many viewers, how fresh must the data be, which devices, how long can it take to load, who needs accessible access. Ask for numbers. "50 viewers, 25 stations, updates every 10 seconds, always on at a wall screen" gives a different design from "5,000 viewers, 100,000 sensors". The first example below measures the real sizes of Lumen's data, the kind of number you should bring.

  3. 3

    Draw the data flow in one picture. Source (database or object store), a thin API or files, the browser's data layer (fetch, clean, cache), a single state, and the charts that read from it. Keep one place that owns the data and one that owns the filters. Charts are pure views: given the state they draw, and they never fetch on their own. This is the shape of the Lumen project: load.js and clean.js feed main.js, which calls every draw… function.

  4. 4

    Choose rendering from the mark count and the interaction. A few hundred marks that need focus, labels and screen-reader names: SVG. Tens of thousands of dots with a pointer lookup: Canvas with a quadtree. Hundreds of thousands, or all marks animating: WebGL (coming in a later Hectal course). A dashboard usually mixes them: SVG tiles and bars, one Canvas layer for the trips map. Say which charts get which, and why (Topic 12.1).

  5. 5

    Real-time is a choice with costs. Polling (ask every N seconds) is simple, works through every proxy, and wastes requests. Server-sent events keep one connection open for updates from the server to the page. WebSockets are two-way and need more infrastructure. For a dashboard that only displays 25 stations every 10 seconds, polling a small JSON file is a defensible answer, and you should say so. Whatever you choose, plan for late and duplicate data, and throttle redraws to one per frame (Topic 16.3). The second example keeps a rolling window that survives both.

  6. 6

    Every state is a screen, and the budget is a number. Loading (a skeleton), empty (a sentence that says what is missing), partial failure (one chart fails, the others stay), error (a message and a retry), stale (the old chart with a badge). Accessibility is a requirement from the start (Phase 15): a text finding, a table twin, keyboard access. Then set targets you can measure: a first useful screen within about 2.5 seconds (Google's "good" threshold for Largest Contentful Paint), interactions answered within 200 ms (the "good" threshold for Interaction to Next Paint), a redraw inside the 16.7 ms frame at 60 fps. Say how you would measure them (Topic 16.5).

Explain it without notes

01

How do you start a frontend system-design answer for a metrics dashboard?

02

When would you choose polling, SSE or WebSockets for the live data?

03

Which states does a dashboard chart need, and why?

04

How do you set and check a performance budget?

Practice

01

Draw the data flow for a page showing stations on a map, with a filter and a table twin. Name the one place that owns the filter state.

02

For a dashboard with 5,000 viewers instead of 50, and data that changes every second, what changes in your answer to "how does data arrive"? Give two changes.

03

Write the screen-state function for a chart that has partial failure: the rides line loaded, the station map failed. What does each chart show?

Trade-offs

  • ↔

    Polling is simple and cacheable but wastes requests when nothing changes; SSE is efficient but needs a server that holds open connections; WebSockets add two-way power at the highest operational cost.

  • ↔

    One shared state keeps the charts consistent but couples them; independent charts are easier to build and test but can disagree. For dashboards, consistency usually wins.

  • ↔

    A strict performance budget keeps the page fast but limits features; say which feature you would cut first when the budget is exceeded.

Done when you can

  • I can open a system-design answer with requirements and real numbers.

  • I can draw the data flow from storage to pixels with one state owner.

  • I can choose SVG, Canvas or WebGL per chart and say why.

  • I can choose between polling, SSE and WebSockets and handle late and duplicate data.

  • I can list every screen state and what each looks like.

  • I can set a performance budget with numbers and say how to measure it.