FSD Frontend System Design

Part I β€” The Pipes Β· Lesson 2.2

Short Polling

In one lineAsk the server every N seconds. Simple, stateless, and mostly wasted requests.

Where you've seen itThe unread-count badge in a webmail tab, quietly hitting /unread every 60 seconds whether or not anything changed.

  • Time12 min
  • DiagramTimeline: 6 requests, 1 carries data
  • Chapter2 Β· Communication

Diagram

diagram
SHORT POLLING β€” the client drives the clock

  0s        5s        10s       15s       20s       25s
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  REQ       REQ       REQ       REQ       REQ       REQ
   β”‚         β”‚         β”‚         β”‚         β”‚         β”‚
  304       304       304       304       200β˜…      304
  empty     empty     empty     empty     DATA      empty

  6 round trips Β· 1 useful Β· worst-case staleness = the interval (5s)
diagram
THE COST AT SCALE

  100,000 clients Γ— 1 request / 5s  =  20,000 req/s
  ...to deliver maybe 50 real updates per second.

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘  β”‚
  β”‚ 99.75% empty responses      0.25% payload  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How it works

  1. setInterval (or better, a setTimeout chain) fires a request.
  2. The server answers immediately β€” with data or with nothing.
  3. Send ETag / If-None-Match so empty answers cost a 304 and no body.
  4. Back off when the tab is hidden (document.visibilityState) and on repeated empty responses.
  5. Chain timeouts rather than using setInterval, so a slow response can't stack requests on top of each other.
js
async function poll(url, ms = 5000) {
  while (document.visibilityState === 'visible') {
    const res = await fetch(url, { headers: { 'If-None-Match': etag } });
    if (res.status === 200) render(await res.json());
    await new Promise((r) => setTimeout(r, ms));   // wait AFTER the response
  }
}

Trade-offs

Use whenAvoid when
Updates are rare and staleness is fineYou need sub-second freshness
You want zero server stateClient count is large (request storm)
Infrastructure is plain HTTP + CDNBattery and mobile data matter
The feature must ship todayMany clients poll on the same boundary

The thundering-herd trap: every client polling on a round interval synchronises after a deploy and hits the server in one spike. Add jitter β€” ms + Math.random() * 1000.

Interview angle

"What's wrong with setInterval(fetchData, 1000)?"

  • Responses slower than the interval stack up; use a setTimeout chain that waits for the response.
  • It keeps polling in a hidden tab β€” gate on visibilityState and back off.
  • No jitter means synchronised clients hammer the server in waves.

Recap

  • The client owns the clock; freshness is capped by the interval.
  • Cheap to build, expensive to run β€” cost scales with clients, not with events.
  • Fix the three defaults: chain timeouts, honour visibility, add jitter.