cf.completefrontend
My progress
FUNDAMENTALS · MODULE 5

Choosing a real-time technique

How to pick between short polling, long polling, server-sent events, WebSockets, and WebRTC for each feature, starting from who talks, how often, and how fresh the data must be, then checking scale, proxies, battery, and the fallback you will ship.

The problem: five tools, one feature

Your team is adding three features to a project management app in the same quarter. A small badge shows the number of unread notifications, a board view shows cards moving as teammates drag them, and a new AI assistant streams its answer word by word. A product manager asks one simple question: "Do we need WebSockets?"

The honest answer is "for which feature?". The badge changes a few times an hour and nobody minds if it is 30 seconds late. The board needs updates within a second, mostly from server to browser, and the assistant streams one long answer to one request. Three features, three different shapes of traffic, and very possibly three different techniques.

The five tools you already know

The earlier lessons in this module covered each technique on its own. This lesson does not teach them again. It puts them side by side and gives you a way to choose. A quick reminder of what each one is:

  • Short polling (see the short polling lesson): the browser asks "anything new?" on a timer, for example every 10 seconds. Plain HTTP, simplest of all, and wasteful when nothing changes.
  • Long polling (see the long polling lesson): the browser asks, and the server holds the request open until there is news or a timeout, usually 20 to 30 seconds. Near-instant delivery over plain HTTP.
  • Server-sent events, or SSE (see the server-sent events lesson): one GET response that stays open and carries a stream of text events from server to browser. EventSource reconnects on its own and resumes with Last-Event-ID.
  • WebSockets (see the WebSockets lesson): one upgraded connection where both sides send messages at any time, with 2 to 14 bytes of framing each. You own the protocol, the reconnects, and the scaling.
  • WebRTC (covered in a later lesson): direct browser-to-browser connections for audio, video, and data, built for very low latency media. It is the only option here that can skip your server for the data itself.

Two neighbors also come up in these conversations. Webhooks (see the webhooks lesson) are server-to-server callbacks, so they never reach a browser directly. Push notifications (covered in a later lesson) reach a device even when your page is closed. Both often sit next to the five above in a real design.

How teams choose badly

Most bad choices come from picking the tool first and the requirement second. Three patterns show up again and again:

  • WebSockets for everything. A notification badge gets a stateful connection layer, sticky load balancing, a pub/sub bus, and a reconnect protocol, for data that changes twice an hour. Polling every 30 seconds would have cost a few lines of code.
  • Polling for everything. A live board polls every second to feel fast. With 20,000 open tabs, that is 20,000 requests per second, almost all of them answered with "nothing new".
  • Ignoring the network in between. A WebSocket design works in the office and fails at a customer whose corporate proxy blocks the upgrade, with no fallback planned.

The fix is a short list of questions asked in a fixed order: who sends data, how often, how fresh it must be, how many clients there are, and what the network and device allow. The next chapter turns those questions into one picture.

The mental model: describe the traffic, then pick the pipe

Think of the five techniques as pipes of different sizes and shapes. You would not choose a pipe before you know what flows through it, how much, and in which direction. So the first step is always to describe the traffic of one feature in plain numbers. The choice usually falls out of that description.

Five questions that describe any real-time feature

  1. Who sends? Only the server, only the client, or both. Count the messages per minute in each direction for one active user.
  2. How fresh must it be? The longest delay a user would accept before the feature feels broken: 100 ms for a cursor, 1 second for a chat message, 30 seconds for a badge, minutes for a report.
  3. How many receive the same data? One user (their own order), a small group (a chat channel), or a huge audience (a live score). This is called fan-out.
  4. Where is the other end? Your server, or another user's device (audio, video, files between people).
  5. Must it reach a closed page? If the user should hear about it with no tab open, no in-page technique can help.

Write the answers as a small profile. For the three features from the last chapter it looks like this:

A traffic profile per feature. The numbers are rough on purpose: you need the order of magnitude, not the exact value.
FeatureWho sendsFreshnessFan-outOther endClosed page
Notification badgeServer, about 3 per hour30 s is fineOne userServerNo (push is a separate feature)
Live boardServer, 1 to 20 per minute; clicks go by POSTAbout 1 sTeam of 5 to 50ServerNo
AI answer streamServer, 20 to 50 chunks per second, for one requestEach chunk at onceOne userServerNo

The two axes that matter most

Of the five questions, two separate the techniques most clearly: how much the client sends and how fresh the data must be. Plot a feature on those two axes and you land in the zone of one technique. The other three questions then confirm the choice or move you to a neighbor.

A map with freshness needed on the vertical axis (tens of milliseconds, under 1 second, a few seconds, 30 seconds or more) and how often the client sends on the horizontal axis (nothing, a few per minute, many per minute). Tens of milliseconds for media between people is WebRTC. Under 1 second with little client traffic is SSE with actions by POST. A few seconds is SSE or long polling. 30 seconds or more is short polling. Many client messages per minute at under 1 second or a few seconds is WebSockets; at 30 seconds or more it is batched POSTs.freshness neededtens of msunder 1 sa few s30 s or moreWebRTC: media between peopleSSE, actions by POSTSSE or long pollingShort pollingWebSocketsbatched POSTsnothinga few per minmany per minhow often the client sends
Plot a feature by its freshness need and its client traffic. The zone is your starting point; fan-out, networks, and battery then confirm it or move you to a neighbor.

Read the map from the bottom left. Data that can be 30 seconds or more late, with little sent by the client, belongs to short polling. Make it fresher, down to a few seconds or under one, and you move to SSE, with long polling as its fallback.

Add frequent messages from the client and you move to WebSockets. Audio and video between people need delays of tens of milliseconds, and WebRTC is the tool built for that.

"The client sends something" is not enough for a WebSocket

A common slip is to pick WebSockets because the client sends anything at all. Almost every feature has some client actions: posting a comment, moving a card, voting. If those happen a few times a minute, a normal POST is fine, and the updates can still come down an SSE stream.

The question is whether the client sends often, many times a minute, in small pieces where per-request headers would dominate. Typing indicators, cursor positions, and game inputs pass that test. A comment form does not.

The same goes for freshness. "Real-time" in a product spec often means "within a few seconds". Ask what delay would actually hurt. If the answer is 10 seconds, the cheapest technique that meets 10 seconds wins, and that is often polling.

Check your understanding · Exercise 1

A delivery app shows the courier's position on a map while the order is on its way. The position updates every 5 seconds and the customer only watches. Which statements about its traffic profile are right? Select all that apply.

The decision tree

The map gives a feeling for the zones. The decision tree turns it into a fixed order of questions you can walk in an interview, out loud, in under a minute. The order matters: the first questions catch the special cases that no amount of tuning can fix, and the last ones pick between close neighbors.

A decision tree with four questions asked in order. Media between devices: yes leads to WebRTC. Reach a closed page: yes leads to web push plus an in-page technique. Client sends often: yes leads to WebSockets. Needed within a few seconds: yes leads to SSE with long polling as fallback. Four noes lead to short polling. A note adds: huge fan-out of the same data, consider polling a CDN-cached URL.Media between devices?yesWebRTCnoReach a closed page?yesWeb push + in-pagenoClient sends often?yesWebSocketsnoNeeded within a few s?yesSSE + long pollnoShort pollinghuge fan-out of the same data?poll a CDN-cached URL
Ask the questions in this order. The first two catch cases no tuning can fix; the last two choose between close neighbors. The note is the one adjustment to make afterwards.

Step 0: does the server need to speak first at all?

Before the tree, check that you need real-time delivery. A product page's stock count only matters when someone looks at it or adds it to the cart, so a fresh fetch at that moment is enough. A settings page needs nothing. Real-time pays off only when something changes while the user is watching and seeing it late has a cost you can name: a missed chat message, a stale price, two people editing the same card.

Steps 1 and 2: the special cases

  • Media or data between users' devices? Voice, video, screen sharing, or large files sent person to person need WebRTC. It is built for media, can send over UDP so a lost packet does not stall the ones behind it, and can connect browsers directly. The WebRTC lesson later in this module covers it. You still need a server channel (often a WebSocket) for signaling, the messages that set the call up.
  • Must it reach a closed page? Use web push, covered in the push notifications lesson. It is never the only channel: once the user opens the page, an in-page technique takes over for live updates.

Steps 3 to 5: the main branch

  • Does the client send often, many times a minute? Pick WebSockets. One connection carries both directions with a few bytes of framing per message.
  • Must updates arrive within a few seconds? Pick SSE. It streams server-to-client over plain HTTP, reconnects on its own, and resumes with Last-Event-ID. Keep long polling as the fallback for networks that buffer streams.
  • Otherwise, when a delay of tens of seconds is fine, pick short polling, with an interval that matches the freshness need, ETag checks so unchanged answers are a cheap 304, and a pause in hidden tabs.

Two adjustments come after the tree. If the same data goes to a huge audience, consider polling a CDN-cached URL even when the tree said SSE, because the cache absorbs the fan-out (the scale chapter shows the math). And if events start on another company's server, they come to your backend by webhook first and then reach the browser by whichever technique the tree picked.

Walking the tree for three features

FeaturePath through the treePick
Notification badgeNo media, no closed page, client rarely sends, 30 s is fineShort polling every 30 to 60 s, paused when hidden
Live boardNo media, no closed page, client sends a few times a minute (POST), needs about 1 sSSE, with long polling as fallback
AI answer streamNo media, no closed page, client sends one question, chunks must arrive at onceA streamed fetch response in SSE format
Multiplayer whiteboardNo media, no closed page, client sends cursor moves 10+ times a secondWebSockets
Support video callMedia between peopleWebRTC, with a WebSocket for signaling

Note the AI row. EventSource only sends GET requests with no body and no custom headers, so it cannot send a long prompt. AI chat clients usually send a POST with fetch and read the response body as a stream in the same text/event-stream format. It is still server-sent events on the wire, just without the EventSource object and its automatic reconnect.

Check your understanding · Exercise 2

Walk the tree for each feature. Which picks are right? Select all that apply.

Side by side: the comparison table

The tree tells you where to start. The table tells you what you are signing up for. Each column is a technique, and each row is a question an interviewer, or your future on-call self, will ask about it.

Server cost and battery assume one idle client: nothing is happening, and the channel is only staying alive.
QuestionShort pollingLong pollingSSEWebSocketsWebRTC
DirectionClient asks, server answersClient asks, server answers when readyServer to client; client uses normal requestsBoth ways on one connectionDevice to device, both ways
Typical delayUp to one interval, plus a requestAbout one network trip, plus a short gap after each answerTens of ms after the eventTens of ms after the eventLowest: tens of ms, media-grade
Server cost per idle clientRequests: 6 per minute at a 10 s intervalOne held request, plus a new one every 20 to 30 sOne open response, plus a heartbeat every 15 to 30 sOne open socket, plus a ping every 20 to 30 sSignaling only; bandwidth if a TURN relay is used
Proxies and firewallsWorks everywhereWorks almost everywhere; idle timeouts need careFine over HTTPS; some proxies buffer the streamUpgrade can be blocked on old or strict proxies; wss:// helpsUDP often blocked; TURN over TCP 443 as last resort
Battery on phonesEach poll wakes the radioEach re-request wakes the radioHeartbeats wake the radioPings wake the radioHeavy while a call runs, nothing otherwise
Reconnect and resumeNothing to reconnect; send since or ETagYour loop with a cursor and backoffBuilt in: auto reconnect and Last-Event-IDAll yours: backoff, resume message, resubscribeICE restart; app must renegotiate
Browser supportEverywhereEverywhereAll modern browsersAll modern browsersAll modern browsers; codecs and features vary
HTTP/1.1 connection useBrief, per requestHolds one of 6 per host (fine on HTTP/2)Holds one of 6 per host (fine on HTTP/2)Its own connection, not counted in the 6Its own transport

Reading the rows that people get wrong

Delay is not only the network trip. With short polling, an event that happens just after a poll waits the whole interval, so the average delay is half the interval and the worst is a full one. With long polling, the server answers at once, but there is a gap while the next request travels.

SSE and WebSockets push the moment the server knows, so their delay is close to one network trip, often 20 to 100 ms. The race below delivers the same four server events with each technique; change the polling interval and watch requests trade against waiting.

Short polling asks every
Four server events at 2.6, 8.4, 9.1, 15.7 seconds, delivered by four techniques over 20 seconds. Short polling every 5 seconds makes 5 requests and each event waits until the next poll. Long polling makes 5 requests. Server-sent events and WebSockets make one connection and deliver almost at once.serverShort pollingasks every 5 sLong pollingasks again after answersServer-sent eventsone stream, server to clientWebSocketone connection, both ways0s5s10s15s20s
Top dots are events on the server; dots on each line are when the browser sees them; ticks are requests the browser makes. Short polling trades delay against requests: ask more often to wait less.
TechniqueRequests in 20 sAverage wait per event
Short polling52.3 s
Long polling5almost none
Server-sent events1almost none
WebSocket1almost none

Server cost is a different kind of cost per column. Polling costs requests per second: CPU, logs, auth checks, and database reads, even when the answer is empty. Persistent techniques cost open connections: memory per connection (a few KB to tens of KB on an event-loop server), file descriptors, and load balancer slots. Neither is free, and which one is cheaper depends on how often data changes compared with how often you would ask.

Reconnect and resume is where the work hides. EventSource reconnects by itself and sends the last event id it saw. A raw WebSocket does neither: when it closes, it stays closed until your code opens a new one and tells the server where it left off. That code is easy to get subtly wrong, and it is the code that runs during your worst outages.

A bar chart of what one idle client sends per minute when nothing changes, assuming about 1 KB of headers per request and answer. Short polling every 10 seconds: 6 requests, about 6 KB. Long polling with a 25 second timeout: 2.4 requests, about 2.4 KB. SSE with a 30 second heartbeat: 2 lines, under 0.2 KB. WebSocket with a 30 second ping: 2 pings and pongs, under 0.2 KB.Short polling6 requests, about 6 KBLong polling2.4 requests, about 2.4 KBSSE2 heartbeat lines, under 0.2 KBWebSocket2 pings and pongs, under 0.2 KB
When nothing happens, polling still pays full request cost while open connections only pay heartbeats. The open connections cost memory instead, which this chart does not show.

What the table leaves out

  • Binary data. SSE is text only (UTF-8). If you need binary, encode it (base64 adds about 33 percent) or use WebSockets, which carry binary frames.
  • Message ordering. Within one response or one connection, every technique here delivers in order, except WebRTC data channels, which can be set to unordered for lower delay. Across reconnects, ordering is yours to restore with sequence numbers.
  • Caching. Only short polling plays well with HTTP caches and CDNs. A held request or an open stream is never cached.
  • Authentication. Polling and long polling send cookies and headers like any request. EventSource sends cookies (cross-origin only with withCredentials: true) but no custom headers, so it relies on the session cookie or a short-lived ticket in the URL. WebSocket sends cookies on the handshake but no Authorization header, so teams use the cookie with an Origin check, a short-lived ticket in the URL, or a token in the first message (all covered in the WebSockets lesson).

Check your understanding · Exercise 3

Your app polls every 20 seconds. A teammate says "so updates arrive within 20 seconds, 10 on average". What else adds delay, or changes the numbers? Select all that apply.

Scale: requests, connections, and fan-out

At small scale every technique works, and the choice is about simplicity. At large scale the cost model decides. Polling turns users into requests per second, and persistent techniques turn users into open connections. A design review usually starts with one quick calculation of each.

The two numbers to compute

  • Polling load = online clients divided by the interval. 100,000 tabs polling every 10 seconds is 10,000 requests per second. If data changes once a minute, 5 out of 6 of those answers say "nothing new".
  • Connection count = online clients, all the time. 100,000 tabs means 100,000 open connections. An event-loop server (Node.js, Go, Java with Netty, async Python) can typically hold tens of thousands each (an assumption to measure on your own stack), so that is a handful of machines, plus the pub/sub layer that tells each machine which events its clients care about.

Connections are often cheaper than they sound, and requests are often more expensive. Each poll pays for TLS work, auth, routing, logging, and usually a database or cache read. An idle open connection costs memory and a heartbeat every 20 to 30 seconds. Try the numbers yourself:

Online users
Poll every
Data changes every
For 100,000 users polling every 15 seconds while data changes every 60 seconds: 6,667 poll requests per second, 75 percent of them empty. With a stream instead: 1,667 pushed events per second and 3,333 heartbeats per second over 100,000 open connections, about 4 servers at an assumed 25,000 connections each.Poll requests6,667 per s, 75% find nothingPushed events1,667 per s, all usefulHeartbeats3,333 per s, a few bytes each
Bar lengths use a log scale, so read the numbers rather than compare lengths. A poll is a full request with headers and auth; a heartbeat is a few bytes on an open connection.
ApproachLoadAverage delay
Short polling6,667 requests per secondabout 7.5 s
SSE or WebSocket100,000 open connections, about 4 servers at an assumed 25,000 eachunder 0.2 s

Many polls come back empty. Compare the request bill with running the connection fleet below.

Fan-out changes everything

Fan-out is how many clients receive the same message. A direct message goes to 2 people, a channel message to 50, and a goal in a cup final to 2 million people at the same second. The same technique can be cheap at a fan-out of 50 and painful at 2 million, because every message must be written to every open connection.

For huge fan-out of identical data, polling a CDN can beat any push technique. Put the score at GET /match/481/score with Cache-Control: public, max-age=2. Two million clients polling every 5 seconds send 400,000 requests per second, but they all land on the CDN.

Turn on request collapsing (the CDN sends one origin request for many clients waiting on the same expired URL), and each CDN location asks your origin about once every 2 seconds. Your servers then see a few hundred requests per second. The cost is a delay of up to about 7 seconds (2 seconds of cache age plus the 5 second interval), which a score page can accept and a chat cannot.

Left: one origin fleet holds an open connection to every viewer, 2 million connections. Right: viewers poll a CDN cache, and with request collapsing each CDN location asks the origin about once every 2 seconds.OriginSSE or WebSockets2M open connectionsOriginCDN cache1 req per 2 sPolling a CDNmost polls end at the CDN
For the same data going to millions of people, a CDN turns millions of connections into a few hundred origin requests per second, at the price of a few seconds of delay.

Decision A live score is watched by 2 million people at once. How do you deliver it?

SSE or WebSockets from your servers

Every viewer holds a connection to your fleet

Each goal is written to 2 million connections through pub/sub

Pros

  • Updates in well under a second
  • No wasted requests between goals

Cons

  • 2 million connections: 40 to 100 servers at an assumed 20,000 to 50,000 each
  • A reconnect storm after any deploy or blip
  • Most of the fleet sits idle between events

Short polling through a CDN

Clients poll a cacheable URL every 5 s

max-age=2 lets the CDN answer almost every poll

Pros

  • Origin sees a few hundred requests per second
  • No stateful fleet, no reconnect storms
  • Works through every proxy

Cons

  • Up to about 7 seconds of delay
  • Phones wake their radio every 5 s

A managed real-time service or CDN push product

Clients connect to a provider's edge network

You publish one message; the provider fans it out

Pros

  • Sub-second delivery without running the fleet
  • Fan-out handled by the provider

Cons

  • Cost per connection or per message at this scale
  • Another vendor in the critical path

What we pick: For a score page where a few seconds is fine, CDN polling is the cheapest and sturdiest. For live betting, where seconds cost money, pay for push, and buy it from a provider unless real-time is your core product.

Reconnect storms

Persistent connections have a cost that polling does not: they all fail together. A deploy, a load balancer restart, or a short network cut closes 50,000 connections at once, and 50,000 clients try to reconnect in the same second. Each reconnect pays for a TLS handshake, an auth check, and a resubscribe, so the fleet can fall over just as it comes back.

The defenses are the same for every technique: exponential backoff with random jitter so reconnects spread over 10 to 30 seconds, a server that can ask clients to wait (retry: in SSE, Retry-After on HTTP), gradual deploys that drain connections a slice at a time, and resume by cursor so each client asks only for what it missed. The retries and backoff lesson covers the formulas.

Check your understanding · Exercise 4

An internal dashboard has 2,000 users. Data changes every few seconds and should show within about 2 seconds. Which arguments are sound? Select all that apply.

The network in between: proxies, firewalls, and timeouts

Your connection does not go straight from the browser to your server. It may pass a home router, a carrier's network, a corporate proxy, a CDN, and your own load balancer. Each of them was built mostly for short request and answer traffic, and each can quietly break a long-lived one. This is the part of the choice that a demo on office Wi-Fi never shows.

Three ways the middle breaks real-time traffic

  • Dropped upgrades. A WebSocket starts with an HTTP request carrying Upgrade: websocket. An old or strict proxy that does not understand it may strip the header or reject the request, and the socket never opens.
  • Buffering. Some proxies, and some server setups such as nginx with its default proxy_buffering on, collect a response before passing it on. A stream of small SSE events then arrives in clumps, or not until the buffer fills. Set X-Accel-Buffering: no for nginx, and test through your real proxy chain.
  • Idle timeouts. Load balancers and proxies close connections that send nothing for a while; 60 seconds is a common default, for example on AWS Application Load Balancers and in nginx's proxy_read_timeout. A quiet stream or a long-poll held too long is cut without warning.

Each break has a matching fix. Heartbeats every 15 to 30 seconds keep idle timeouts from firing: an SSE comment line, a WebSocket ping, or a long-poll timeout answered before 30 seconds. Disabling buffering fixes clumps. And for dropped upgrades, the main fix is encryption.

A browser behind an office proxy, talking to a server. A plain ws:// WebSocket is stopped at the proxy, which drops the Upgrade header. An encrypted wss:// connection passes through. An SSE stream through a TLS-inspecting proxy that buffers arrives in clumps. A long poll over https passes through.BrowserProxyServerws://Upgrade droppedwss://passes throughSSE *arrives in clumpslong pollpasses through* through a TLS-inspecting proxy that buffers responses
A proxy can only change what it can read, so encryption gets most connections through. A TLS-inspecting proxy can read everything, and buffering is the usual way it hurts streams; a long poll still works because each answer is a complete response.

Why TLS fixes most of it

A proxy can only change what it can read. Over https:// and wss://, the traffic is encrypted end to end, so a proxy in the middle sees an opaque stream on port 443 and passes it along. That is why wss:// gets through far more networks than ws://. Always use wss://; plain ws:// is both unsafe and less likely to get through.

There is one exception. Many companies run TLS-inspecting proxies that decrypt traffic with their own certificate, which the company installs on its laptops. Those proxies can read and change everything, so they can still block upgrades, buffer streams, or cut connections after a fixed time. If your product sells to large companies, assume some customers sit behind one.

WebRTC and blocked UDP

WebRTC prefers UDP, because media can skip a lost packet instead of waiting for it. Many corporate and hotel networks block outgoing UDP, and some routers block direct paths between devices. When no direct path works, WebRTC uses a TURN relay: a server you run (or rent) that forwards the media.

The last resort is TURN over TCP or TLS on port 443, which looks like ordinary HTTPS to a firewall. It works almost anywhere, at the cost of more delay and your bandwidth bill. The WebRTC lesson covers STUN and TURN in detail.

Check your understanding · Exercise 5

Users at one enterprise customer say live updates arrive in bursts every 30 seconds or so, while everyone else sees them at once. Your app uses SSE over HTTPS. What are likely causes? Select all that apply.

Phones, batteries, and background tabs

On a laptop plugged into the wall, a request every few seconds costs nothing anyone notices. On a phone it can cost a visible share of the battery. The reason is not the bytes. It is the radio.

The radio tail

A phone's cellular radio has a low-power idle state and a high-power active state. Sending or receiving anything moves it to the active state, and after the transfer it stays active for several seconds in case more data comes, before it drops back to idle. That waiting period is called the tail. Its length is set by the mobile network and varies by carrier and generation (one widely cited textbook example uses 10 seconds), so a 300-byte poll can cost seconds of full-power radio time.

Two timelines of a phone radio over 60 seconds. Waking every 15 seconds, for a poll, a ping, or a heartbeat, turns the radio on 4 times, and each wake-up keeps it on high power for about 10 seconds, so it is on high power 40 of 60 seconds. Waking every 60 seconds turns it on once: 10 of 60 seconds.radio on high power 40 of 60 severy 15 shigh power 10 of 60 severy 60 s0s15s30s45s60s
Each small transfer keeps the radio on high power for seconds afterwards (about 10 here, for illustration; the real tail depends on the network). It does not matter whether the wake-up is a poll or a heartbeat.

So the rule on mobile is fewer, larger wake-ups. Ten small messages spread over a minute cost far more than the same ten sent together. This applies to every technique: a poll every 15 seconds, a WebSocket ping every 15 seconds, and an SSE heartbeat every 15 seconds all wake the radio just as often. A persistent connection is not automatically kinder to the battery than polling; what matters is how often anything moves.

Moves that save battery

  • Batch on the server. Send presence and counter updates at most once every few seconds, merged, instead of one message per change.
  • Stretch heartbeats. Use the longest heartbeat your proxies allow. If the load balancer cuts at 60 seconds, 25 to 30 seconds is enough; 5 seconds is waste.
  • Back off when idle. If the user has not touched the page for a minute, poll less often or slow the stream's updates.
  • Stop in hidden tabs. Close the stream or pause polling when the page is hidden, and catch up with one request when it is visible again.
  • Use push for closed pages. The device or browser already keeps one shared connection to its push service for all apps and sites, so web push adds no connection of yours.

Background tabs: the browser decides for you

Browsers already limit work in tabs the user cannot see. Timers in hidden tabs are slowed, in some cases to once a minute, and on phones a background tab may be frozen or discarded entirely. A polling loop built on setTimeout slows down without telling you. A WebSocket or SSE stream may stay open, be frozen, or be closed by the system, and none of this is consistent across browsers, so do not rely on it.

Take control instead. Listen for visibilitychange, pause on hidden, and on visible do one catch-up request from your cursor before resuming the live channel. This works the same for every technique, which is a good reason to put it in your shared real-time client rather than in each feature:

ts
let hiddenTimer: ReturnType<typeof setTimeout> | undefined;

document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    hiddenTimer = setTimeout(() => realtime.pause(), 30_000);
  } else {
    clearTimeout(hiddenTimer);
    realtime.resume({ after: store.lastEventId }); // catch up, then go live
  }
});
Wait 30 s before pausing, so a quick tab switch does not drop the connection. On return, resume from the last event id.

Check your understanding · Exercise 6

A fitness app's web page keeps a WebSocket open with a ping every 10 seconds and sends step counts as they change. Users complain about battery. Which changes help most? Select all that apply.

Fallbacks: a ladder of transports

Because the failures live in the network, a production real-time layer rarely bets on one technique. It keeps a ladder: the best transport first, then simpler ones that get through more networks. The usual order is WebSocket, then SSE, then long polling, and short polling as the very last step. A one-way feature starts the ladder at SSE.

A state machine. Try WebSocket: if it opens, go to Live; if it does not open within 10 seconds, go to Try SSE. Try SSE: if the hello event arrives, go to Live; if no hello within 5 seconds, go to Long polling. Long polling: on the first answer, go to Live. Live: on a drop, go to Reconnect with backoff, which resumes after the last event id and returns to Live. A note says a step down is remembered for the session and every rung resumes after the last event id.Try WebSocketTry SSELong pollingLiveReconnectbackoff + jitterno open in 10 sno hello in 5 sopenhello eventfirst answerdropresumea step down is remembered for the session;every rung resumes after the last event id
Failure is detected by deadlines, not by feature detection. A drop while live reconnects on the same rung; only a rung that never works is stepped down from.

How to tell that a rung failed

You cannot ask the browser "will this work here?". You find out by trying, with deadlines:

  • Open timeout. If a WebSocket has not fired open within about 5 to 10 seconds, treat it as blocked.
  • First-message timeout. Have the server send a hello event right after connecting. If an SSE stream opens but no hello arrives within a few seconds, something is buffering it.
  • Repeated early drops. If the connection opens but dies after the same short time, again and again, a proxy is cutting it. Two or three such drops in a row means step down.

When a rung fails, step down and remember it for the session, for example in sessionStorage, so every page load does not pay the same 10-second timeout again. Try the better rung again later, perhaps on the next session, because the user may have moved from the office network to home Wi-Fi.

One message format on every rung

A fallback is only safe if switching rungs never loses or repeats a message. That needs the same rules on every transport: each event has an increasing id, the client keeps the last id it handled, and every rung can resume with "give me everything after id N". With that, a step down is just a reconnect on a different pipe. Hide all of it behind one small interface, so components never know which rung is in use:

ts
type RealtimeEvent = { id: number; topic: string; data: unknown };

interface Transport {
  connect(after: number): Promise<void>; // resume after this event id
  onEvent(handler: (e: RealtimeEvent) => void): void;
  send?(topic: string, data: unknown): void; // WebSocket only; others use fetch
  close(): void;
}

// Components only ever see this:
realtime.subscribe("board:42", (e) => applyBoardEvent(e.data));
The ladder lives inside realtime. Features subscribe to topics and never see a transport.

Build, use a library, or buy

Decision Who builds the ladder, reconnects, and fan-out?

Build it yourself

Your own transports behind one interface

Your own pub/sub (for example Redis) between servers

Pros

  • Full control of protocol and cost
  • No extra dependency

Cons

  • Reconnect, resume, and fallback code is yours to get right
  • Weeks of work that is not your product

A library with built-in fallback

Socket.IO starts on long polling and upgrades to WebSocket when it can

SignalR negotiates WebSockets, then SSE, then long polling

Pros

  • Fallback and reconnect already solved
  • Rooms or groups for fan-out

Cons

  • Its own protocol on both ends, so both must use the library
  • You still run and scale the servers

A hosted real-time service

Pusher, Ably, PubNub, and cloud equivalents

Your backend publishes; clients connect to the provider

Pros

  • Global edge, presence, and history included
  • No connection fleet to run

Cons

  • Cost grows with connections and messages
  • Data passes through a third party

What we pick: For most product teams, a library or a hosted service is the right first step. Build your own only when real-time is the product itself, or the bill at your scale clearly pays for the team.

Check your understanding · Exercise 7

Your chat app falls back from WebSocket to long polling for some users. Which designs keep messages from being lost or shown twice when it switches? Select all that apply.

In real products

Real products mix techniques, one per feature, each the cheapest that meets that feature's needs. The table shows typical choices for well-known kinds of product. Companies change their stacks over time and rarely document every detail, so read these as common, publicly described patterns rather than exact current internals.

Kind of productFeatureTypical techniqueWhy it fits
Team chat (Slack-style)Messages, typing, presenceWebSocketsMany small messages in both directions, all day long
Design tools (Figma-style)Multiplayer editing and cursorsWebSocketsEvery edit and cursor move goes both ways within milliseconds
AI chat assistantsThe answer as it is writtenStreamed fetch in SSE formatOne POST per question, then text flows one way for many seconds
Gaming chat (Discord-style)Events and voiceWebSocket for events, WebRTC for voiceEvents go through servers; voice needs the lowest delay
Video meetings (Meet-style)Calls and screen shareWebRTC, with a server channel for signalingReal-time media between people
Public APIs (GitHub-style)Activity and notificationsPolling with ETag and a server-suggested intervalAny client can do it, and 304 answers are cheap
Payments (Stripe-style)Payment events to merchantsWebhooks, then the merchant's own choice to the browserServer to server, with retries and signatures
Live sports pagesScoresPolling a CDN-cached URL, or a push providerHuge fan-out of identical data

One app, many pipes

Go back to the project management app from the first chapter and put all its features together. A realistic design ends up with four techniques, each behind the same shared client, and only one stateful connection fleet:

One project management app. In the browser: the badge polls the API servers every 60 seconds; the AI assistant reads a streamed fetch from the API servers; the board holds an SSE stream to an SSE gateway; the service worker receives web push from the browser vendor's push service.BrowserServersAPI serversrequestsSSE gatewayopen streamsPush servicebrowser vendor'sBadgeunread countpoll every 60 sAI assistantanswer streamstreamed fetchBoardcards move liveSSE streamService workerclosed-page alertsweb push
Each feature gets the cheapest pipe that meets its needs. Board moves still go up as plain POST requests, and only the SSE gateway holds long-lived connections.
  • Badge and inbox: short polling every 60 seconds, paused when hidden, plus web push for mentions when the page is closed.
  • Board: an SSE stream per open board, with moves sent by POST. If the team later adds live cursors, this one feature moves to a WebSocket.
  • AI assistant: a streamed fetch response per question, closed when the answer ends.
  • Integrations: webhooks from GitHub or a calendar arrive at the backend, which publishes them to the board's topic, so they reach the browser through the same SSE stream.

Lab: set the requirements, get the pick

Set the requirements of a feature you know, or one from an interview prompt. The lab walks the same tree as this lesson, names a primary technique and a fallback, and shows which answer decided it. Try changing one control at a time to find where the pick flips.

Other end
Client sends
Freshness
Same data goes to
Reach closed pages
Strict corporate networks
Five techniques in a row: polling, long polling, SSE, WebSocket, WebRTC. The pick is Server-sent events, with Long poll as the fallback.PollingLong pollSSEWebSocketWebRTCpickfallback
Pick: Server-sent events. The bold box is the pick, the hatched box the fallback.
  • Updates flow from server to client and must arrive within seconds, so a one-way stream fits.
  • The client's few actions go as normal POST requests.
  • Fallback: long polling, speaking the same message format with event ids.

Check your understanding · Exercise 8

A stock trading app shows live prices on the watchlist and lets users place orders. Which split is sensible? Select all that apply.

Using it in a system design interview

Almost every frontend system design prompt has a real-time part: a chat, a feed, notifications, a dashboard, a collaborative editor. Interviewers rarely care which technique you name. They care whether you can justify it from requirements, say what it costs, and say what happens when it fails.

When to bring it up

Ask the traffic questions during requirements, before you draw anything: "How fresh must this be? Does the user send often, or mostly watch? How many people see the same update? Should they hear about it with the tab closed?". The answers set up the choice, and asking them shows you know the choice depends on them.

Then make the choice in the API or interface part of the design, one feature at a time. Say it as a sentence with a reason and a cost: "Board updates go over SSE: they flow one way, need about a second, and SSE reconnects and resumes by itself. Moves are plain POSTs. If the corporate proxy buffers the stream, we fall back to long polling."

What to draw

  • The browser's real-time client as one box that all features use, with the transport inside it.
  • The connection tier (gateway servers holding the connections) separate from the API servers, with a pub/sub box between them and the services that produce events.
  • An arrow for resume: the client sends its last event id on reconnect, and the server answers from a short event log.

How deep to go by level

LevelWhat a strong answer covers
JuniorPicks a technique that fits the direction and freshness, and explains why polling or a socket would be worse here.
Mid-levelAdds reconnects with backoff and jitter, resume by event id, pausing in hidden tabs, and the HTTP/1.1 limit of 6 connections per host for streams.
SeniorAdds the cost model (requests versus connections, fan-out, CDN polling for huge audiences), proxies and idle timeouts, a fallback ladder with one message format, battery, and a build-or-buy call with reasons.

Summary

  • Choose per feature, not per app. Describe the traffic first: who sends, how fresh, how many receive it, where the other end is, and whether the page is open.
  • The decision tree: media between people means WebRTC; a closed page means web push; frequent client messages mean WebSockets; server updates needed within seconds mean SSE; otherwise poll.
  • Polling costs requests per second; persistent techniques cost open connections. Compute both before choosing.
  • For huge fan-out of identical data, polling a CDN-cached URL can beat any push technique, at the cost of a few seconds.
  • Proxies and firewalls break long-lived connections: use TLS everywhere, heartbeats under idle timeouts, and a fallback ladder that detects failure by timeouts.
  • On phones, every wake-up of the radio costs battery. Batch, stretch heartbeats, and pause in hidden tabs.
  • Every rung of the ladder must speak one message format with increasing ids and resume by cursor, so switching transports never loses or repeats a message.

In the interview

Questions interviewers ask about this. Answer each out loud first, then compare.

Question 1

We're adding live updates to a dashboard used by about 5,000 people. How would you choose the technique?

Question 2

Why not just use WebSockets for everything?

Question 3

A live event page will have 3 million viewers for 2 hours. What would you do?

Question 4

Some enterprise customers say real-time updates don't work for them. How do you investigate and fix it?

Question 5

How would you design the real-time parts of a video calling app?

Practice it

These design problems lean on choosing a real-time technique.

References

Read next