Problem Statement & Requirements
Why News Feed Design Is a Top Interview Question
The Challenge: Generating a personalized, ranked feed for hundreds of millions of users in real-time involves nearly every major system design concept: caching, fan-out, ranking, storage, and CDN delivery.
Real Scale: Facebook's News Feed serves 2 billion users. Each feed request aggregates posts from hundreds of friends and pages, ranks them, and returns results in under 200ms.
Real-World Analogy
Think of a news feed like a personalized newspaper editor:
- Posts = News stories submitted by reporters (your friends)
- Fan-out = The printing press distributing papers to subscribers
- Ranking = The editor deciding what goes on the front page
- Cache = Pre-printed papers ready for immediate delivery
- CDN = Regional distribution centers closer to readers
Functional Requirements
Publish Posts
Users can create posts with text, images, and videos. Posts appear in followers' feeds.
View Feed
Users see a ranked, personalized feed composed of posts from friends, pages, and groups they follow.
Interactions
Users can like, comment, and share posts. These interactions influence feed ranking.
Infinite Scroll
Feed supports paginated loading. As users scroll, older posts are fetched dynamically.
Feed Generation vs Fan-Out
Push vs Pull Model
The Hybrid Approach (Industry Standard)
Facebook, Twitter, and Instagram all use a hybrid model:
- Regular users (under 10K followers): Push model. Fan out posts to all followers' feed caches on write.
- Celebrities/pages (10K+ followers): Pull model. Their posts are fetched and merged at read time to avoid fan-out storms.
- This hybrid approach keeps write latency under 1 second for 99.9% of posts while handling celebrity accounts gracefully.
Feed Ranking Algorithm
import math
from datetime import datetime, timedelta
from dataclasses import dataclass
@dataclass
class Post:
post_id: str
author_id: str
created_at: datetime
likes: int
comments: int
shares: int
post_type: str # 'text', 'image', 'video'
class FeedRanker:
"""
Multi-signal feed ranking algorithm.
Score = affinity * edge_weight * time_decay
Affinity: How close is the viewer to the author?
Edge Weight: How engaging is this type of content?
Time Decay: How fresh is the post?
"""
# Content type weights (based on engagement data)
TYPE_WEIGHTS = {
"video": 1.5,
"image": 1.2,
"text": 1.0,
"link": 0.8,
}
def calculate_score(self, post: Post, viewer_id: str) -> float:
"""Calculate ranking score for a post relative to a viewer."""
affinity = self._get_affinity(viewer_id, post.author_id)
engagement = self._engagement_score(post)
freshness = self._time_decay(post.created_at)
type_boost = self.TYPE_WEIGHTS.get(post.post_type, 1.0)
return affinity * engagement * freshness * type_boost
def _get_affinity(self, viewer_id: str, author_id: str) -> float:
"""
Relationship strength between viewer and author.
Based on: messages sent, profile views, likes, comments.
Range: 0.0 to 1.0
"""
# In production: ML model trained on interaction data
interactions = get_interaction_count(viewer_id, author_id)
return min(1.0, interactions / 100)
def _engagement_score(self, post: Post) -> float:
"""Weighted sum of engagement signals."""
return (
post.likes * 1.0 +
post.comments * 2.0 + # comments are more valuable
post.shares * 3.0 # shares indicate high quality
)
def _time_decay(self, created_at: datetime) -> float:
"""
Exponential time decay.
Half-life of ~6 hours: a post loses half its
freshness score every 6 hours.
"""
hours_old = (datetime.utcnow() - created_at).total_seconds() / 3600
half_life = 6.0
return math.pow(0.5, hours_old / half_life)
def rank_feed(self, posts: list, viewer_id: str, limit: int = 50) -> list:
"""Rank and return top posts for the viewer's feed."""
scored = [
(self.calculate_score(post, viewer_id), post)
for post in posts
]
scored.sort(key=lambda x: x[0], reverse=True)
return [post for _, post in scored[:limit]]
Media Storage & CDN
Media Pipeline
- Upload: Client uploads to a media service via pre-signed S3 URLs
- Processing: Resize images (thumbnail, medium, full), transcode videos
- Storage: S3 for originals, CloudFront/CDN for optimized versions
- Delivery: CDN serves media from the edge node closest to the user
- Lazy loading: Feed initially loads text + thumbnails; full media loads on scroll
Cache Strategy for Feed
import redis
import json
class FeedCache:
"""
Multi-tier caching strategy for the news feed.
Tier 1: Pre-computed feed (Redis sorted set per user)
Tier 2: Post content cache (Redis hash)
Tier 3: Social graph cache (who follows whom)
"""
def __init__(self):
self.redis = redis.Redis(host='redis-cluster')
self.FEED_SIZE = 500 # Cache last 500 post IDs per user
self.FEED_TTL = 86400 # 24 hours
def add_to_feed(self, user_id: str, post_id: str, score: float):
"""Push a new post into a user's feed cache (fan-out on write)."""
key = f"feed:{user_id}"
# Sorted set: score determines feed ordering
self.redis.zadd(key, {post_id: score})
# Trim to keep only the most recent N posts
self.redis.zremrangebyrank(key, 0, -(self.FEED_SIZE + 1))
self.redis.expire(key, self.FEED_TTL)
def get_feed(self, user_id: str, page: int = 0,
size: int = 20) -> list:
"""Fetch a page of feed items for the user."""
key = f"feed:{user_id}"
start = page * size
end = start + size - 1
# Get post IDs from sorted set (highest score first)
post_ids = self.redis.zrevrange(key, start, end)
if not post_ids:
# Cache miss: regenerate from database
return self._rebuild_feed(user_id, page, size)
# Fetch full post content (Tier 2 cache)
return self._hydrate_posts(post_ids)
def _hydrate_posts(self, post_ids: list) -> list:
"""Fetch full post data using pipeline for efficiency."""
pipe = self.redis.pipeline()
for pid in post_ids:
pipe.hgetall(f"post:{pid}")
return pipe.execute()
Scaling the Feed System
Database Sharding
Shard post data by user_id. Feed cache sharded by viewer's user_id. Use consistent hashing for even distribution across shards.
Async Fan-Out Workers
Fan-out is done by a pool of workers consuming from Kafka. Scale workers independently based on queue depth.
Multi-Region Deployment
Deploy feed services in multiple regions. Use eventual consistency for the social graph. CDN caches media globally.
Rate Limiting Publishes
Limit post frequency to prevent spam and reduce fan-out load. Typical: 10 posts/hour per user.
Practice Problems
Medium Celebrity Problem
A celebrity with 100 million followers posts an update. How do you handle the fan-out without overwhelming the system?
- Calculate the cost of fan-out-on-write for this user
- Design a hybrid approach that handles celebrities differently
- How do you determine the threshold for "celebrity" treatment?
Fan-out-on-write for 100M followers would require 100M cache writes per post. Instead, store the celebrity's posts separately and merge them at read time. Consider a follower count threshold (e.g., 10K) to switch strategies.
# Hybrid Fan-Out Strategy
# Threshold: users with > 10,000 followers use pull model
def publish_post(author_id, post):
follower_count = get_follower_count(author_id)
if follower_count < 10000:
# Push model: fan out to all followers
for follower in get_followers(author_id):
feed_cache.add_to_feed(follower, post.id, post.score)
else:
# Pull model: just store in celebrity posts table
celebrity_posts.store(author_id, post)
def get_feed(user_id):
# Get pre-computed feed (from push model)
feed = feed_cache.get_feed(user_id)
# Merge celebrity posts (pull model)
celeb_followings = get_celebrity_followings(user_id)
for celeb_id in celeb_followings:
recent = celebrity_posts.get_recent(celeb_id)
feed.extend(recent)
# Re-rank and return
return ranker.rank_feed(feed, user_id)
Hard Content Moderation
Design a content moderation pipeline that detects and filters harmful content before it appears in feeds. Consider both automated and human review.
- Where in the pipeline do you perform moderation?
- How do you handle false positives without delaying legitimate posts?
- How do you scale human review for edge cases?
Use a multi-stage pipeline: fast ML classifiers for obvious cases, then human review for borderline content. Consider a "shadow ban" approach where flagged content is hidden from feeds but not deleted immediately.
# Content Moderation Pipeline
# Stage 1: Pre-publish ML filter (sync, < 100ms)
# - Text: NLP classifier for hate speech, spam
# - Images: Computer vision for nudity, violence
# - Confidence > 0.95: Auto-reject
# - Confidence < 0.3: Auto-approve
# - Between: Queue for human review
# Stage 2: Post-publish async review
# - Content published immediately (good UX)
# - Async worker runs deeper analysis
# - If flagged: remove from feeds, notify author
# Stage 3: Human review queue
# - Priority queue sorted by reach (followers * views)
# - SLA: 4 hours for high-reach, 24h for low-reach
# - Reviewer decisions train the ML model (feedback loop)
Hard Trending Topics
Add a "Trending" feature that surfaces popular topics in real-time. How do you detect trends across billions of posts?
- How do you define what is "trending"?
- What data structures enable real-time trend detection?
- How do you prevent manipulation of trending topics?
A topic is "trending" when its mention velocity exceeds its historical baseline. Use Count-Min Sketch for approximate counting and sliding time windows. Compare current counts against exponential moving averages.
# Trending Detection Pipeline
# 1. Extract entities/hashtags from posts (NLP)
# 2. Stream to Kafka topic "entity-mentions"
# 3. Flink job: count mentions per 5-min window
# 4. Compare to 7-day exponential moving average
# 5. Trending if: current_rate > 3 * avg_rate
# Data structures:
# - Count-Min Sketch: space-efficient approximate counting
# - Min-Heap: top-K trending topics
# - Sliding window: HyperLogLog per entity per time bucket
# Anti-manipulation:
# - Weight by unique users, not total mentions
# - Discount accounts < 30 days old
# - Geographic diversity requirement
Quick Reference
News Feed Design Summary
| Component | Technology | Rationale |
|---|---|---|
| Feed Cache | Redis Sorted Sets | Pre-computed feed, O(log N) insert |
| Post Storage | MySQL + Vitess | ACID for posts, sharded by user_id |
| Fan-Out Queue | Kafka | Decouple write from delivery |
| Ranking | ML Model (TensorFlow) | Multi-signal personalized ranking |
| Media | S3 + CloudFront CDN | Scalable storage, global delivery |
| Social Graph | TAO (Graph DB) / Neo4j | Efficient friend/follower queries |
Key Takeaways
Interview Tips
- Clarify scope: Is it Twitter (public, follower-based) or Facebook (friends, bi-directional)?
- Always discuss the push vs pull tradeoff -- and propose the hybrid approach
- Feed ranking is a major differentiator: show you understand affinity, engagement, and time decay
- Caching is critical: pre-computed feeds in Redis dramatically reduce read latency
- Address the celebrity problem explicitly -- interviewers love this discussion point
- Consider media delivery separately from text content (CDN, lazy loading)