Write path:
POST /v1/tweets
Body: {"user_id": string, "content": string (max 140 chars), "media_ids": [string]}
Response: {"tweet_id": string, "created_at": timestamp}
POST /v1/users/{user_id}/follow
Body: {"target_user_id": string}
Response: 204 No Content
Post /v1/tweets/{tweet_id}/like
Body: {"user_id": string}
Response: 204 No Content
Read path:
GET /v1/feed?user_id={id}&cursor={cursor}&limit=20
Response: {"tweets": [...], "next_cursor": string}
GET /v1/feed/trending?limit=K
Response: {"tweets": [...]}
The system splits into two core paths:
Key components:
1. Fan-out Service (Hybrid push/pull)
Problem: A celebrity with 50m followers posting a tweet cannot fan out to 50m feed caches synchronously
Solution: hybrid aproach
Data structure: Each user's feed is a Redis sorted set: feed: {user_id} -> {tweet_id: timestamp}. Capped at 800 entries.
Scaling: Fan-out workers are horizontally scaled behind a message queue (Kafka). Partitioned by target user_id for ordering guarantees.
2. Trending/Top-K Service
Problem: Compute the top K tweets globally based on a scoring function: score = likes * wl + retweets * w2 + recency_decay.
Solution:
Tradeoff: Approximate counts are acceptable - trending doesn't need exact precision.
3. Social Graph Service
Storage: Follow relationships stored in a graph-optimized store (adj list in Cassandra or dedicated graph store like TAO)
Schema (Cassandra):
both tables maintained in a dual-write pattern for O(1) lookups in either direction. The followers table is critical for the fan-out service to know who to push to.
dual write - use a transactional outbox or event sourcing to ensure durability
Key Tradeoffs summary:
Decision: Hybrid fan-out: complexity vs handling celebrity problem
Redis sorted sets for feeds: memory cost vs read latency
Approximate trending (Count-Min Sketch) precision vs throughput
Eventually consistent likes/counts: Consistency vs Availability
Dual-write follower/following tables: Storage cost vs O(1) lookups in both direcitons