A plain-English walkthrough
How the X “For You” algorithm works
Ever wonder why X showed you that post? There’s a chain of 8 steps deciding it. Here it is — in 5 minutes, in plain English.
5-min read · 8 steps
The flow at a glance
- Request
- Find candidates
- Filter
- Score with ML
- Your feed
The 30-second version
- A request comes in.
- The system gathers everything it knows about you.
- It collects candidate posts — from people you follow, and from strangers.
- It enriches each post with more data.
- It throws out the obvious junk.
- It uses an ML model to predict how you’ll react to each post.
- It sorts by score and keeps the top ones.
- A final safety pass, and the feed is sent back.
Step by step
The request arrives
You open the app and pull to refresh. That sends a request to a service called Home Mixer, which is the conductor for everything that happens next.
Think of Home Mixer as the pass in a restaurant kitchen: every dish goes through it, and it decides what reaches your table and in what order.
Under the hood: Home Mixer exposes a gRPC endpoint that returns ranked posts for a user. home-mixer/scored_posts_server.rs
Who are you, right now?
Before fetching any posts, the system builds a fresh picture of you: who you follow, what you’ve liked, replied to, shared, the topics you follow, even what was shown to you recently.
Like a barista who, before making your coffee, glances at what you usually order, what mood you seem to be in, and what you tried last week.
Under the hood: These are query hydrators — small pieces of code that each fetch one slice of context in parallel. home-mixer/query_hydrators/
Two sources of posts
Now it gathers candidates from two places, at the same time. Posts from accounts you follow (“in-network”, served by Thunder), and posts from people you don’t follow but might enjoy (“out-of-network”, found by Phoenix).
Two scouts go out: one combs through your inner circle’s posts, the other searches the entire world for things that smell like you.
Under the hood: Thunder is a Rust in-memory store fed by Kafka, with sub-millisecond lookups. Phoenix retrieval uses a two-tower model: one tower turns you into a vector, another turns posts into vectors, and the closest matches win. thunder/ · phoenix/recsys_retrieval_model.py
Filling in the blanks
For every candidate post, more data is pulled in: the actual text, media, author info, engagement counts, language, brand-safety signals, whether it’s a quote post, and so on.
Each candidate dish comes back with its full label: ingredients, allergens, where it came from.
Under the hood: These are hydrators — they enrich candidates in parallel, before any ranking decision. home-mixer/candidate_hydrators/
Cheap filters first
Before paying the cost of running ML, the obvious removals happen: duplicates, posts that are too old, your own posts, posts from people you’ve blocked or muted, posts containing your muted keywords, and posts you’ve already seen.
The bouncer’s first sweep — anyone clearly not getting in tonight is turned away before the headliner shows up.
Under the hood: Each filter is one small piece of code with one job. Composable and cheap to run. home-mixer/filters/
The scoring (the actual algorithm)
Each remaining post is scored by an ML model called Phoenix, a transformer based on Grok. For every post it predicts the probability you will: like, reply, repost, quote, click, expand the photo, watch the video, dwell, share, follow the author... and also the negative ones: not interested, block, mute, report.
Imagine a friend looking at a post and quietly betting: “There’s a 14% chance you’ll like this, a 2% chance you’ll reply, a 0.3% chance you’ll block them.” Then they sum it up with weights.
Under the hood: Each predicted probability has a weight. Positive actions add to the score; negative actions subtract. Then two correction passes run: author diversity attenuates posts when one author dominates the feed, and an OON scorer rebalances out-of-network content. phoenix/recsys_model.py · home-mixer/scorers/
Pick the best
Sort by final score. Take the top K. Those are the posts that will be served to you, in that order.
All the dishes have a final star rating. The pass sends out the top ones, best to worst.
Under the hood: A selector just sorts and slices. home-mixer/selectors/
One last safety pass
Right before sending the feed back to you, one more check: anything that was deleted, marked spam, violent, gore, etc. is dropped. Conversation threads get collapsed so you don’t see five branches of the same argument.
The expediter takes one last look at the plate before it leaves the kitchen.
Under the hood: Post-selection filters — same idea as step 5, but applied after ranking, on the small final set. home-mixer/filters/
The big idea behind the scoring
The model doesn’t output one “relevance” number. It predicts a probability for many different reactions you might have — like, reply, repost, share, click, dwell, block, mute, report. Each action has a weight: good reactions add to the score, bad ones subtract. The feed is sorted by the weighted sum.
That’s why “engagement” isn’t the whole story. Posts that make people block or report aren’t rewarded — those signals push scores down.
Know someone who’d find this useful?
If this helped you understand the feed a little better, send it to one person who’d enjoy it.