Many interview questions have a line buried in the requirements like “users should see new messages instantly” or “the dashboard updates in real time.” That single phrase changes the shape of your design: a plain request/response API cannot push, so you need a deliberate strategy for getting fresh data to clients. This is a pattern — a reusable technique that shows up across chat, notifications, live feeds, dashboards, collaborative tools and multiplayer games — so it is worth learning once, properly.
The core tension is that the web is built around clients pulling data, but real-time features need the server to push. Everything below is a different way to resolve that tension, trading simplicity against latency and connection cost.
In the interview: when you spot a real-time requirement, name the four options out loud and justify your pick in one sentence — “this is a one-way live feed, so SSE is enough; I would reach for WebSocket only if the client also needs to send a steady stream.” That signals you know the menu, not just the one tool you remember.
The menu: four ways to deliver updates
Short polling
The client asks “anything new?” on a fixed timer — every few seconds — and the server answers immediately, usually with nothing. It is trivial to build on top of a normal REST endpoint and needs no special infrastructure.
The cost is waste and lag. Most requests return empty, burning bandwidth and server capacity, and an update can sit for up to one poll interval before the client sees it. Short polling is the right answer only when near-real-time is acceptable and simplicity genuinely matters — a page that refreshes a status every 30 seconds, say.
Long polling
The client makes a request and the server holds it open until there is data (or a timeout), then responds; the client immediately reconnects. The user sees updates with almost no lag, and it still works over ordinary HTTP with no new protocol.
The downside is that every waiting client ties up a connection, and each update costs a full request/response round trip to re-establish. Long polling is a reasonable fallback where WebSocket is unavailable, but it has largely been superseded by SSE and WebSocket.
Server-Sent Events (SSE)
SSE opens a single long-lived HTTP connection over which the server streams events to the client as they happen. It is one-directional (server → client), text-based, runs over normal HTTP/2, and the browser’s EventSource automatically reconnects and can resume from the last event id.
GET /feed/stream (client opens an EventSource)
<-- data: {"type":"new_post","id":9931}
<-- data: {"type":"new_post","id":9932}SSE is the sweet spot for one-way live feeds: notifications, activity streams, price tickers, dashboards, progress bars. It is simpler than WebSocket and plays well with existing HTTP infrastructure.
WebSocket
WebSocket upgrades an HTTP connection into a persistent, full-duplex channel — both sides can send messages at any time with very low overhead. It is the right tool when the client needs to send a continuous stream too, not just receive: chat, collaborative editing, multiplayer games, live trading.
The cost is that it is stateful and heavier to operate. Each connection pins memory on a server, load balancers must support the upgrade and sticky routing, and you must handle reconnection and authentication on reconnect yourself.
| Option | Direction | Latency | Cost / complexity | Use when |
|---|---|---|---|---|
| Short polling | pull | up to interval | lowest | near-real-time is fine |
| Long polling | pull (held) | low | medium | WebSocket unavailable |
| SSE | server → client | low | medium | one-way live feeds |
| WebSocket | full-duplex | lowest | highest | two-way, chat/games |
Scaling the push
Choosing WebSocket or SSE is the easy part. The hard part is pushing to millions of simultaneously connected clients, and the key realization is that a connection is stateful: the live socket lives on one specific server. When an event is produced somewhere else in the system, you have to get it to exactly the servers holding the interested clients’ sockets.
The standard architecture separates two concerns:
- Connection servers — stateful nodes whose only job is to hold open sockets (tens of thousands each) and forward messages to the clients attached to them. They are horizontally scalable: add more as your connection count grows.
- A pub/sub broker — a stateless fan-out layer (Redis Pub/Sub, Kafka, or a managed bus) that decouples whoever produces an event from whoever delivers it. A publisher writes an event once; the broker delivers it to every connection server that has a subscriber for it.
The missing piece is routing: when an event is destined for user 42, which connection server holds user 42’s socket? Two common approaches are to publish on a per-user (or per-room) channel that only the relevant connection servers subscribe to, or to keep a registry (in Redis) mapping user_id → connection_server that the fan-out consults. For broadcast scenarios like a public live feed, every connection server simply subscribes to the one channel.
Reconnection and backfill
Real connections drop — phones change networks, laptops sleep, servers restart. A robust real-time design treats disconnection as normal, not exceptional, and answers one question explicitly: “what did the client miss while it was gone?”
- Resume from a cursor. The client remembers the id/timestamp of the last event it saw (SSE’s
Last-Event-IDdoes this for you). On reconnect it sends that cursor, and the server replays everything since. - Backfill from the source of truth. For a chat app, on reconnect the client fetches any messages newer than its last-seen id from the regular (non-real-time) API, then resumes the live stream. The live channel delivers new events; a normal paginated query fills the gap.
- Heartbeats and timeouts. Periodic pings detect dead connections quickly so the server can free resources and the client can reconnect, rather than silently believing it is still live.
In the interview: after you draw the connection servers and broker, add one line about reconnection and backfill. Candidates who forget it end up with a design that silently drops messages whenever a network blips — which is always.
Where this pattern shows up
- Chat / messaging — WebSocket, per-conversation channels, backfill on reconnect.
- Notifications and activity feeds — SSE or WebSocket fanning out from a pub/sub layer.
- Live dashboards / metrics — SSE streaming aggregated updates.
- Collaborative editing — WebSocket, broadcasting each edit to everyone in the document “room.”
- Multiplayer / live trading — WebSocket for the lowest round-trip latency.
Key Takeaways
-
A real-time requirement needs a push strategy. Normal request/response can’t push, so pick deliberately from the menu — short polling, long polling, SSE, WebSocket.
-
Choose by direction and latency. SSE for one-way live feeds; WebSocket when the client also sends a steady stream (chat, games); polling only when near-real-time is acceptable.
-
Connections are stateful — separate concerns to scale. Stateless connection servers hold the sockets; a pub/sub broker fans events out and decouples producers from delivery.
-
Routing is the real problem. Map each user/room to the connection server holding their socket, via per-channel subscriptions or a registry.
-
Design reconnection and backfill up front. Clients drop constantly; resume from a cursor and backfill the gap from the normal API so no updates are silently lost.
