Home › Blog › Design Live Comments

Design a Live Comments System (Facebook Live-Style)

How to push comments to everyone watching a live video in about a second: SSE vs WebSockets, pub/sub by video, hot streams, ordering, moderation and back-pressure.

A live comments system lets everyone watching the same live video post comments and see each other's within about a second. The standard design writes each comment over plain HTTP to a comment service that stores it, then pushes it to viewers over a long-lived connection (Server-Sent Events or WebSockets), using pub/sub partitioned by live video ID so each server only receives comments for the streams its viewers are watching. Interviewers then dig into hot streams with huge audiences, catching up after a dropped connection, and moderation that does not slow the feed down.

1. Clarify requirements and size the problem

Functional: viewers post a text comment on a live video, see others' comments in near real time, and see recent comments (with scroll-back) when they join late. Leave threaded replies, reactions, search and the video pipeline out of scope unless asked.

Non-functional:

The number that matters is deliveries, not writes. Deliveries per second for one stream equal comments per second times concurrent viewers. LinkedIn engineer Akhilesh Gupta used this arithmetic in a QCon London 2020 talk on live-video likes: 100 likes per second on a video with 10,000 viewers is about one million deliveries per second. In the same talk, LinkedIn cited roughly 100,000 persistent connections per frontend machine, a useful figure for estimating how many connection servers a big event needs.

2. API and data model

Split the write path from the read path. Posting is an ordinary request; receiving is a stream.

# Write (user comes from the auth token, not the body)
POST /v1/videos/{videoId}/comments   { "text": "this is great" }
  -> 201 { "commentId": "...", "createdAt": "..." }

# Read: live stream of new comments
GET  /v1/videos/{videoId}/comments/stream     (SSE, or a WebSocket upgrade)

# Read: history for late joiners and scroll-back
GET  /v1/videos/{videoId}/comments?before={commentId}&limit=50
FieldNotes
video_idPartition key. All comments for a stream live together.
comment_idSort key. A time-sortable unique ID (Snowflake-style), so sorting by ID is sorting by time.
author_id, textPut the display name and avatar URL in the pushed event so clients never look up profiles.
video_offset_msPosition in the broadcast when the comment was posted, for lining comments up on replay.
statusVisible, removed or pending review, so moderation can retract a comment.

A wide-column or key-value store such as Cassandra or DynamoDB fits this pattern: append within a partition, read the newest N, page backward. Use cursor pagination on comment_id; offsets get slow on long streams and repeat or skip items while new comments keep landing.

3. Choose the transport: polling, SSE or WebSockets

OptionFit for live comments
Short pollingSimple and cacheable, but wasteful and laggy. Useful as a fallback or a snapshot mode for huge streams.
Long pollingWorks through strict proxies, but every batch costs a new request.
Server-Sent EventsStrong default. One HTTP response stays open, server to client only, with automatic reconnect and resume through event IDs.
WebSocketsFull-duplex connection opened with an HTTP Upgrade (RFC 6455). Best when clients also send frequent events or the app already runs a socket gateway.

The read path is one-way, so SSE for delivery plus HTTP POST for writes is a clean answer, and it is the approach LinkedIn described for live-video likes: an EventSource connection per viewer. SSE details worth knowing:

With WebSockets you write reconnect and resume logic yourself. Either way, the connection layer resembles the gateway in a chat app design; the difference is that chat routes each message to a few recipients, while live comments broadcast to everyone watching.

4. The fan-out topology

Viewers of one video are spread across many connection servers (gateways). Every gateway holding at least one viewer of that video needs a copy of each comment, and no other gateway should get one. Walk through three options:

  1. Broadcast to every gateway. Simple, but each gateway processes the whole platform's comment volume and drops most of it. It stops scaling quickly.
  2. Pub/sub channel per video. Each gateway keeps an in-memory map of videoId → local connections, subscribes to comments:V when its first viewer of video V arrives, and unsubscribes when the last one leaves. A Layer 7 load balancer that routes viewers by a consistent hash of the video ID keeps each audience on fewer gateways.
  3. Dispatcher with a subscription table. Gateways register "gateway G has viewers of video V" in a shared key-value store, and a dispatcher tier forwards each comment only to those gateways. This is the shape LinkedIn described, including multi-region delivery: a dispatcher forwards each event to peer dispatchers in other data centers, which deliver it only if they have local subscribers.

The write path, end to end: the comment service authenticates, rate-limits, runs fast moderation checks and assigns a comment_id; persists the comment and appends it to a short recent-comments buffer (for example, a capped list in Redis); publishes it to the video's channel or dispatcher; and each subscribed gateway pushes it to local viewers.

Which pub/sub? Redis Pub/Sub is a common pick because channels are cheap and dynamic. Its documentation is explicit that delivery is at-most-once: a disconnected subscriber loses the message. Since Redis 7.0, sharded Pub/Sub (SSUBSCRIBE, SPUBLISH) keeps each message within one cluster shard instead of sending it to every node. At-most-once is acceptable because the database, not the push channel, is the source of truth. Kafka fits the durable side (moderation, analytics, persistence) better than one topic per short-lived stream; Gupta made a similar point, noting a Kafka-based push path would add guarantees at the cost of speed and frontend scalability.

5. Hot streams: one video, a huge audience

Facebook's 2015 engineering post on Live said the system had to handle the potential of more than a million people watching one broadcast at the same time. At that size the fan-out cost explodes, and nobody can read thousands of comments a second anyway.

State the trade-off plainly: on a hot stream you trade completeness for liveness. Viewers get a fast, representative feed, and the full history stays available through the paginated API.

6. Ordering, duplicates and reconnects

7. Moderation without killing latency

StageChecksLatency cost
Synchronous, in the write pathAuth, per-user and per-stream rate limits, length, blocked terms, users the broadcaster banned, slow mode, followers-only modeMilliseconds, using cached data
Asynchronous, after publishML classifiers for spam, harassment and links; user reports; account trust signalsNone for the viewer
RetractionPublish a comment_removed event on the same channel; clients drop it; storage status changesOne small event

Rate limiting doubles as spam control and load control; the patterns in our rate limiter walkthrough apply directly. Name the trade-off: holding every comment for a classifier is safer but slower, while publish-then-retract stays live but lets a bad comment show briefly. A middle path is risk-based routing: trusted accounts publish immediately, new or flagged accounts wait for review.

8. Back-pressure and failure handling

Telling followers that someone just went live is a separate fan-out; treat it as a notification system. Ingesting and transcoding the video belongs to the video pipeline, not this design.

Trade-offs to state, and how to pace the answer

DecisionDefaultSwitch when
TransportSSE for delivery, HTTP POST for writesClients send frequent events too: use WebSockets
Fan-outPub/sub channel per videoYou need tighter routing or multi-region delivery: add a dispatcher
Hot streamsPush every commentAudience or comment rate passes a threshold: batch, sample or use CDN snapshots
ModerationFast sync checks, async classifiersHigh-risk streams or new accounts: hold for review

For a 45-minute round, a workable split is about five minutes on requirements and numbers, five on the API and data model, fifteen on the high-level design and fan-out, and the rest on the deep dives the interviewer picks. Interviewers steer differently, so follow their lead.

The fastest way to find gaps is to explain this design out loud against a clock. A free AI mock interview in CoPilot Interview lets you include system design in the question mix, answer aloud, and get a score on each answer with what worked and what to improve.

Practice this design out loud

Paste a job description and your résumé, add system design to the question mix, and answer out loud. Each answer gets a score with what worked and what to improve, and the free plan gives you 15 minutes a day with no credit card.

Try a free AI mock interview

FAQ

Should I use WebSockets or Server-Sent Events for live comments?

SSE is a strong default because comment delivery is one-way, writes can be ordinary HTTP POST requests, and the browser reconnects and resumes with the Last-Event-ID header for you. Choose WebSockets if clients also send frequent events over the same connection or the product already runs a WebSocket gateway.

How do you handle a live video with millions of viewers?

Fan out in two levels, one copy per gateway server and then to local connections in memory, and batch comments on a short timer. Past a threshold, show a ranked sample instead of every comment, or serve a snapshot of recent comments from a CDN that clients poll about once a second.

Do live comments need to be in perfect order?

No. Viewers need a sensible order within one stream, not a global order, so time-sortable comment IDs plus a short client-side reorder buffer are usually enough.

What happens when a viewer's connection drops?

The client reconnects with backoff and jitter and sends the last comment ID it saw, which SSE does automatically when the server sets event IDs. The server replays newer comments from a short recent buffer, and the client drops duplicates by comment ID.

How do you moderate live comments without adding latency?

Run cheap checks such as rate limits, blocked terms and banned users synchronously, and run heavier classifiers after the comment is published. If a comment is flagged later, publish a removal event on the same channel so every client drops it.