Imagine walking into a stadium filled with 100,000 people all talking at once. Somehow, a friend taps you on the shoulder and hands you a tiny cue card with the 5 most interesting quotes spoken in that stadium over the last minute, perfectly tailored to what you care about.
That is what happens every time you pull down to refresh your feed on a major social network like X. Behind the curtain, the system scans hundreds of millions of active posts, picks a few hundred relevant ones, ranks them by interest, and delivers them to your screen in less than half a second.
Before we go dig deeper and understand how system handles that without melting your real world system. Have you noticed?
Whenever you open X app, it takes few seconds to show latest on your feed
If you have X opened via app or web, after few 30 seconds, it automatically starts showing the options to refresh feed
You are not following someone but still their posts are shown at your feed
You liked post of someone you are not following or just spent some time reading the post and X starts showing you posts from that person
Let’s decode all this in technical terms
1. The 3 Steps to Building a Real-Time Feed
A common mistake is thinking a feed is just a simple database query like SELECT * FROM posts WHERE user_id IN (...) ORDER BY created_at DESC. At scale, that query will crash your database instantly.
Instead, building a feed requires a three-step pipeline:

Step 1: Candidate Generation (Gathering the Haystack)
What is it? The system quickly pulls roughly 1,500 potential posts from two sources: In-Network (accounts you follow) and Out-of-Network (trending topics or content liked by people with similar tastes).
Why do we need it? You cannot run heavy scoring models on millions of posts at once. You must narrow down the giant universe of content into a small, manageable pool first.
What is the catch? It relies on lightweight heuristics and fast lookup caches (like memory-based graph stores). If your candidate generator misses a post here, the user will never see it.
Step 2: Feature Hydration & Machine Learning Ranking (Sorting the Best)
What is it? The pipeline takes those 1,500 candidates and enriches them with data - such as post age, total likes, media type, and how often you interact with the author. An algorithm then scores each post using an engagement formula:
Score = Like * w_1 + Reply * w_2 + Retweet * w_3
Why do we need it? Raw chronological feeds often hide high-value posts under a mountain of low-quality noise. Scoring ensures relevant content surfaces first.
What is the catch? Machine learning models are resource-heavy. Running deep predictions across hundreds of metadata points adds delay, so candidate pools must stay strictly limited.
Step 3: Filtering & Deduplication (Cleaning the Result)
What is it? A safety and usability pass that removes posts you have already viewed, strips out content from blocked or muted accounts, and ensures you do not see five posts in a row from the same person.
Why do we need it? Nobody wants to scroll past the exact same post twice or see content from accounts they muted.
What is the catch? Checking long lists of seen posts for millions of active users can drain memory quickly if not managed using fast data structures.
2. X Feed Architecture

3. Database Storage to Serve Massive Read Scale
X is not dependent on single database but it uses multi-tiered database layer based on access patterns. Cache works as a major database storage for building user feed and 90% of the data served through cache

4. Maintaining User Recent Activity and Applying it to Feed
Every action taken on X (likes, retweets, profile visits, clicks, video watches, and dwell time - i.e., pausing on a post for 3 seconds) generates an event.
Relevancy is also identified based on your interest topics, geographical areas and distant mutuals, ex - A is followed by B and B is followed by C, so post of A can be show to C as well
All these events are pushed to cache, X uses GraphJet to keep users' recent activity trails. GraphJet is X’s open-source real-time graph engine. It holds a sliding window (typically the last 24 hours of interactions) in memory. It maintains directed edges between User -> Post or User -> Topic.
While building or refreshing user feed, based on these activities data is pulled from out of the network people and pushed it to user’s feed

5. Refreshing User Timeline
Users timeline can be refreshed via multiple ways, through passive polling in every 30-60 seconds or user do timeline refresh or app does background prefetch etc. End goal is same, fetching details from cache and building the feed to be shown to the user based on following and relevancy
When a user is active via app or web, it opens a full-duplex HTTP/2 (or HTTP/3) running over a persistent TCP or QUIC connection. It remains open when app is actively communicating and keep polling the data from X servers

6. Solving the Fan-Out Problem (Push vs. Pull)
When a user posts an update, how does it reach their followers? This is known as the Fan-Out problem. There are two primary ways to handle it, along with a hybrid approach.
flowchart LR
subgraph Push Model [Fan-Out on Write]
U1[Regular User Posts] -->|Write Once| W1[Worker Process]
W1 -->|Push to 200 Inboxes| C1[Follower 1 Cache]
W1 -->|Push| C2[Follower 2 Cache]
W1 -->|Push| C3[Follower N Cache]
end
subgraph Pull Model [Fan-Out on Read]
U2[Celebrity Posts] -->|Write Once| Outbox[(Celebrity Outbox)]
F1[Follower Opens App] -->|Query Outboxes| Outbox
F1 -->|Merge in Memory| Feed[User Feed]
end
Option A: Fan-Out on Write (Push Model)
What is it? As soon as a user posts, a background worker delivers a copy of that post ID directly into every follower’s personal inbox cache in memory. Think of it like a mail carrier dropping a physical letter into every neighborhood mailbox.
Why do we need it? Reading a feed becomes lightning fast (O(1) efficiency). When a follower opens the app, the system simply fetches their pre-assembled inbox cache.
What is the catch? If an account with 80 million followers posts, the system must perform 80 million write operations immediately. This causes massive write spikes and wastes storage for inactive followers who may not open the app for weeks.
Option B: Fan-Out on Read (Pull Model)
What is it? When a user posts, the system saves it once to that author’s personal outbox. No work is done for followers until those followers actually open the app. When they do, the app queries the outboxes of everyone they follow and merges them on the fly. Think of it like a central town bulletin board where you check notices only when you visit town.
Why do we need it? It handles celebrity posts effortlessly. Writing a post takes exactly 1 database write, regardless of follower count.
What is the catch? Opening the app requires fetching and sorting posts from hundreds of individual outboxes simultaneously, which slows down feed loading for the reader.
Option C: The Hybrid Strategy (The Real-World Winner)
To balance speed and system load, production systems split users based on their follower counts:
Users with < 10,000 followers: Use Fan-Out on Write (Push). Delivering to a few thousand caches is cheap and keeps reads instantaneous.
Users with > 10,000 followers (Celebrities): Use Fan-Out on Read (Pull). Their posts stay in a dedicated outbox and are merged into followers' feeds only when requested.
7. Comparing Delivery Architectures
Strategy | Read Speed | Write Overhead | Best Used For |
|---|---|---|---|
Fan-Out on Write (Push) | Instantaneous (O(1) lookup from memory cache) | Extremely High (11 Write * N Followers = N Writes) | Standard accounts with low-to-medium follower counts |
Fan-Out on Read (Pull) | Slower (O(M) lookup over M followed outboxes) | Low (1 Write Operation) | Mega-accounts, celebrities, and high-volume outlets |
Hybrid Fan-Out | Fast for all users | Balanced across workers | Production platforms operating at massive scale |
What Do You Think?
What’s your thought over this architecture and what would you change to handle better?
