LESSON 31.2
News feed end to end
Applying the delivery framework to design a scalable social timeline with low-latency feed generation and viral celebrity handling.
6 min read
Designing a social timeline (like Twitter, Instagram, or LinkedIn) is one of the most frequently asked distributed systems interview questions. The problem challenges candidates to handle extreme read-write asymmetries, fan-out write amplification, and high-concurrency caching strategies.
Here is the complete end-to-end walkthrough using the delivery framework.
1. Requirements
- Functional Requirements:
- Users can publish posts containing text and media URLs.
- Users can follow other users.
- Users view a personalized home timeline aggregating recent posts from all followed users in reverse chronological order.
- Scrolling supports smooth infinite pagination.
- Non-Functional Requirements:
- Low feed latency: Fetching the first 20 posts must return in under 200 ms.
- Fast fan-out: A new post must appear on regular followers' feeds within 5 seconds.
- Scale: 200 million Daily Active Users (DAU).
2. Estimates and scale
- Daily Active Users (DAU): 200 million users.
- Posting rate: Average user posts 1 time per day, resulting in 200M posts per day, roughly 2,300 posts per second average and 5,000 posts per second peak.
- Feed views: Users refresh their feed an average of 5 times per day, resulting in 1 billion feed reads per day, roughly 11,500 reads per second average and 30,000 reads per second peak.
- Read-to-write ratio: Approximately 5 to 1 up to 10 to 1 for feeds.
- Storage: Each post metadata is roughly 1 KB, totaling 200 GB of new text and metadata daily. Media is stored in object storage (S3/CDN).
- Feed cache footprint: Cache the latest 800 post IDs for active users in memory. 200M users * 800 post IDs * 8 bytes ≈ 1.28 TB of RAM across a distributed Redis cluster.
3. API design and data model
POST /v1/postsreturns{"post_id": "7182938192", "created_at": 1718000000}GET /v1/feed?limit=20&cursor=eyJwb3N0SWQiOiI3MTgyOTM4MTkyIn0=returns{"posts": [...], "next_cursor": "..."}
Data entities in PostgreSQL or document storage:
users:(user_id, username, created_at)follows:(follower_id, followee_id, created_at)with primary key(follower_id, followee_id)and secondary index onfollowee_id.posts:(post_id, user_id, content, created_at)indexed on(user_id, created_at DESC).
4. High-level design
Deep dive 1: The celebrity dilemma and hybrid fan-out
The central architectural debate in feed design is Push (Fan-out on write) vs Pull (Fan-out on read):
Fan-out on Write (Push)
- Pre-computes feeds in Redis sorted sets
- Feed reads are one fast sorted-set range query (ZREVRANGEBYSCORE)
- Write amplification: users with 50M followers lock queues for minutes to append 50M records
Pick it when: Standard users with fewer than 25,000 followers.
Fan-out on Read (Pull)
- Zero write amplification; creating a post is a single row insert
- Feed generation requires fetching post IDs from all followees on the fly, merging, and sorting in memory
Pick it when: Celebrity accounts and inactive/dormant users.
Production systems (like Twitter and Instagram) implement a Hybrid Fan-out model:
- For standard users (99.9% of users): Use fan-out on write. When they post, append their post ID to all followers' Redis timeline sorted sets.
- For celebrities (accounts with over 25,000 followers): Do not fan out on write. Instead, when a follower opens their app, the Feed Service reads the follower's Redis timeline, fetches recent posts directly from the few celebrities they follow, and merges the streams in memory.
Deep dive 2: Cursor-based pagination vs offset pagination
Never use OFFSET 20 LIMIT 20 for social feeds. Offset queries force the database to scan and discard 20,000 rows when loading page 1,000, and users see duplicate posts if new posts are created while scrolling.
Instead, implement cursor-based keyset pagination:
- A cursor encodes the
(created_at, post_id)of the last item rendered on the user's screen. - The next page request executes:
WHERE (created_at, post_id) < (:cursor_created_at, :cursor_post_id) ORDER BY created_at DESC, post_id DESC LIMIT 20 - The query jumps directly to the target B-tree index position in constant time $O(1)$, completely eliminating duplicates.
To build this design step by step, with a load test at the end, open the Instagram feed lab.
Try it
News Feed HLD
Or design it freely in the practice workspace.