Design Facebook Likes with live updates
Design the like counter system and live-updating like button for Facebook posts, covering accurate counting at scale for high-profile posts, real-time push to all viewers, and the data consistency vs latency trade-offs that make it interesting.
The Problem Statement
Interviewer: "Design the Like button system for Facebook. When a user clicks Like on a post, all other users viewing that post should see the counter update in real time. The system must handle viral posts that get 50,000 likes per second."
This question tests three things: your ability to design a high-write-throughput counter system, your understanding of real-time push mechanisms, and whether you can reason about the consistency trade-offs between showing an exact count versus a fast-updating approximate count.
Most candidates design a simple "increment a row in the database" system. That works fine for normal posts. But a celebrity announcement or breaking news post getting 50,000 likes per second turns a single counter into a write hot-spot that will melt any database. The interesting part of this problem is handling the 0.01% of posts that generate 99% of the write load.
I like this question because the naive solution is correct for 99.99% of cases. The depth comes from reasoning about the extreme tail: viral posts, celebrity accounts, and breaking news moments where write throughput must scale by 1000x for a single entity.
Estimating the Scale
Let me put some numbers on this before designing the system.
| Metric | Estimate | Reasoning |
|---|---|---|
| Daily active users | 2 billion | Facebook's actual scale |
| Likes per day | ~5 billion | Average 2-3 likes per user per day |
| Average likes per second | ~58,000 | 5B / 86,400 seconds |
| Peak likes per second (global) | ~200,000 | 3-4x average during peak hours |
| Likes/sec on a viral post | 10,000-50,000 | Obama's post got 4M likes in hours |
| Active viewers on a viral post | 100,000-1,000,000 | Concurrent viewers at peak |
| Like record size | ~50 bytes | user_id, post_id, timestamp |
| Storage per day | ~250 GB | 5B likes x 50 bytes |
The write path is the hard part. 50,000 writes per second to a single counter is beyond what a single database row can handle. A PostgreSQL row with row-level locking tops out at roughly 5,000-10,000 updates per second. Redis INCR can handle 100,000+ per second on a single key, but even Redis has limits when you add deduplication.
The read path is simpler because it is just reading a cached counter. The push path (real-time updates to all viewers) is the second hard part: fanning out counter updates to 100,000+ WebSocket connections.
Facebook reportedly handles over 6 billion likes and reactions per day. The system must handle this baseline while also absorbing traffic spikes where a single post receives orders of magnitude more engagement than the average.
My Approach
I structure this design around three core components:
- The like write path: How we accept a like, prevent duplicates, and increment the counter without creating a single-key hot-spot
- Sharded counters for viral posts: How we distribute write load across multiple counter shards for posts that exceed single-key throughput
- Real-time fan-out to viewers: How we push counter updates to everyone currently viewing the post without overwhelming the WebSocket layer
The key insight: normal posts and viral posts need different strategies. A post from your friend getting 12 likes does not need sharded counters or batched fan-out. A post from a celebrity getting 50,000 likes per second does. The system needs to detect the transition and adapt dynamically.
The Architecture
Here is the full system architecture for the like button, from click to counter display:
Let me walk through the flow.
Step 1: Optimistic UI. When the user taps Like, the client immediately shows the heart filled and increments the displayed count by 1. This happens before the server even receives the request. If the server call fails, the client rolls back. This gives a perceived latency of 0ms.
Step 2: Deduplication. The API Gateway sends the like request to the write service. First, we check if this user has already liked this post using a Redis SET NX (set if not exists) on the key liked:{user_id}:{post_id}. If the key already exists, the like is a duplicate and we return success without incrementing (the user already liked it, so the UI is correct).
Step 3: Counter increment. If the like is new, we increment the counter in Redis using INCR likes:{post_id}. For normal posts, this is a single key. For viral posts, this is a sharded counter (I will explain this in the next section).
Step 4: Async persistence. The like event is published to Kafka. A consumer writes the like record to the database (for durability and historical queries) and periodically checkpoints the counter from Redis to the database (so the count survives a Redis failure).
Step 5: Real-time push. A separate fan-out service reads like events from Kafka, batches them per post (instead of pushing every single like), and sends updates through the WebSocket gateway to all users subscribed to that post.
For your interview: emphasize the separation of the write path (Redis for speed) from the persistence path (Kafka + DB for durability). This is a classic CQRS pattern where the read/write model is optimized separately.
The Like Write Path and Deduplication
The write path must solve two problems simultaneously: prevent duplicate likes (a user can only like a post once) and increment the counter atomically. These two operations must be coordinated.
The deduplication strategy is critical. Let me walk through the options.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.