A personalized news feed is the ranked list of posts, photos, videos, and links a user sees when they open a social app like Facebook, LinkedIn, or Twitter/X. The ranking problem looks similar to video or product recommendations on the surface, but three things make it harder: the inventory is heterogeneous, the objective is multi-dimensional, and the shelf life of most posts is measured in hours. Getting one of those three wrong breaks the feed in a different way, which is why this is one of the most instructive ML system design interview problems.
The feed at Meta looks different from the home timeline at Twitter/X or the feed on LinkedIn. Before drawing any architecture, a candidate should lock down scope, inventory, and what counts as success.
Candidate: "When you say news feed, do you mean the home feed, stories, reels, or all three surfaces?"
Interviewer: "Home feed only. Stories and short-form video have their own rankers. Assume our home feed includes text posts, photos, videos, link shares, and a small amount of sponsored content interleaved."
Candidate: "Is the feed purely algorithmic, or do users see a chronological option as well?"
Interviewer: "Both exist, but we're designing the default algorithmic feed. Chronological is a user setting that bypasses ranking."
Candidate: "How big is the platform? DAU, posts created per day, and average feed loads per user?"
Interviewer: "Around 2 billion registered users, 500 million DAU. About 20 billion new pieces of content per day across all types. An active user opens the feed roughly 10 times a day and scrolls through 50 to 100 items per session."
Candidate: "What's the candidate pool per user? Am I ranking posts only from accounts they follow, or a broader set?"
Interviewer: "Broader. Followed accounts are the seed, but we also mix in posts from second-degree connections, groups the user joined, and a small exploration slice of topical content from strangers. Expect a few thousand eligible posts per user per request."
Candidate: "What's the target latency and size of a single feed response?"
Interviewer: "Return 50 ranked items per request. P99 for the ranking service is 200ms end to end."
Candidate: "How do we handle ads? Are they ranked in the same model or blended in later?"
Interviewer: "Ads come from a separate auction pipeline. The organic ranker returns a sorted list, and an ads-blending service inserts sponsored items at configured slots. You don't need to model the ad auction itself."
Candidate: "What's the north-star metric? Clicks, likes, session time, or something longer-horizon?"
Interviewer: "We've been burned by raw engagement before. Optimizing for clicks pulls the feed toward clickbait and outrage. The north star is a weekly active retention metric combined with self-reported survey scores on how valuable users found their feed. We call that bundle 'meaningful interactions' internally."
Candidate: "What's hard-filtered before ranking? Blocked accounts, integrity-flagged content, anything else?"
Interviewer: "Hard filters: blocks, mutes, reported accounts, posts flagged by integrity classifiers above a safety threshold, content the user has already seen in the last 24 hours, and content outside the user's locale when required by regional policy. Everything else is eligible for ranking."
Functional Requirements:
Non-Functional Requirements:
The feed is a pointwise multi-task ranking problem. For a given user u and candidate post p, the model predicts the probability of several engagement events, and a value model blends those predictions into a single score used for sorting.
The naive framing is a single-head CTR model: P(click | u, p). It's easy to train because every impression is labeled, but it fails in two ways. Clicks are biased toward sensational content, and a single objective cannot trade off fast engagement against longer-term signals like whether the user hides the post or marks it as not interested.
A better framing uses a small set of heads, each representing a distinct user action, and a value model that combines them:
The blended score is a weighted sum, with the negative head subtracted. Weights are learned or hand-tuned from A/B tests that optimize the retention north star rather than raw engagement. Candidate generation is a separate stage with its own architecture; the ranker assumes it receives a few hundred to a few thousand candidates that have already cleared integrity filters.
| Type | Metric | Why It Matters |
|---|---|---|
| Offline | Per-head AUC (click, like, comment, share, long_dwell, hide) | Calibration of each head on held-out next-day impressions |
| Offline | NDCG@10 on blended value score | How well the blend ranks real engagement events |
| Offline | Calibration plots per head | Predicted vs observed probability per decile; drift shows up here first |
| Online | Sessions per DAU | Do users come back more often during the experiment? |
| Online | Meaningful interactions per user-day | Composite of long_dwell, likes, comments, shares minus hides |
| Online | 7-day and 28-day retention | Whether the ranker is optimizing for habit, not just today's clicks |
| Guardrail | Integrity false-positive rate | Don't fix ranking by over-filtering borderline content |
| Guardrail | Creator-tail impression share | Fraction of impressions going to creators outside the top 1% |
| Guardrail | Source diversity (Gini on topic/creator mix) | Detect echo chambers collapsing the feed onto a narrow set |
| Guardrail | Negative-feedback rate (hide + report + not interested) | A slow rise here always precedes a retention drop |
At the top level, a feed request flows through four stages: candidate generation fans out across heterogeneous sources, a lightweight pre-ranker trims the pool, a heavy multi-task model scores the survivors, and a re-ranker enforces diversity, dedupe, and ads-blending rules before returning a ranked list.
Each stage trims the candidate set by an order of magnitude while using progressively more expensive features. This chapter focuses on what's specific to feed ranking: the heterogeneity of the candidates, the blend of retrieval sources, and the re-ranking stage.
Three loops keep the system healthy, and they run on very different cadences.
The offline loop retrains the ranker on the previous day's impressions joined with labels that landed within the attribution window. The streaming loop keeps time-sensitive features fresh: a newly created post has its early-velocity features updated every few seconds as likes and comments arrive, and a user's recent-engagement vector refreshes within seconds of each action. The online loop is a straight feature-fetch-score-rank pipeline, and its budget is set by the 200ms p99 target.
Before touching architecture details, it's worth grounding the scale with a few estimates. These numbers drive almost every design decision downstream.
| Quantity | Estimate | Calculation |
|---|---|---|
| DAU | 500M | Given |
| Feed loads per DAU per day | 10 | Given |
| Ranking requests per day | 5B | 500M x 10 |
| Peak ranking QPS | ~60K | 5B / 86,400 x 3 (peak factor) |
| Candidates scored per request (ranker) | 200 | Pre-ranker output |
| Ranker scoring ops per second at peak | 12M | 60K x 200 |
| Eligible candidates per user per request | 3,000-5,000 | From candidate gen |
| Posts created per day | 20B | Given |
| Post features retained for 30 days | 600B rows | 20B x 30 |
| Impression log volume per day | ~300B events | 5B requests x 50 items shown, deduped |
The 12M-per-second ranker scoring number is what drives the push toward model distillation, batched GPU inference, and aggressive caching of user-side features. The 600B post-feature rows push toward tiered storage with hot posts in Redis and long-tail posts in a columnar store.
The feed draws from several independent data streams, each owned by a different team in most large organizations. Getting the join keys right across these sources is half the engineering effort.
Feed features fall into four groups: user, post, viewer-creator cross features, and context. The hardest to get right are the cross features, because they describe the relationship between a specific user and a specific post, which cannot be precomputed for all pairs.
User features capture who is asking: their recent engagement behavior, topic-affinity embedding, active hours, device class, and session-so-far signals like how many posts they've already scrolled past without engaging.
Post features describe the item: age in minutes, creator identity and reputation, content-type flags, topic tags, language, embedding of the text and media, quality-classifier score, integrity-classifier score, and early-velocity signals like likes-per-minute and hide-rate-per-impression.
Viewer-creator cross features are where most of the signal lives: whether the viewer follows the creator, how many prior interactions the viewer has had with this creator, time-decayed engagement rate, and whether the viewer and creator share groups or pages.
Context features situate the request in time and place: hour of day, day of week, device, network quality, and whether this is a cold-start session (app just opened) or a deep scroll within an existing session.
Cross-features between user embeddings and post embeddings are learned implicitly by the deep ranker, but a few explicit crosses via DCN-style or DeepFM-style layers tend to help on sparse signals like user-creator affinity.
| Feature | Type | Source | Freshness | Description |
|---|---|---|---|---|
| user_embedding | Vector(128) | Batch pipeline | Daily | Learned from long-term engagement history |
| user_topic_affinity | Vector(64) | Batch pipeline | Daily | User's preference over topic taxonomy |
| user_recent_actions | Seq(50) | Streaming | <10s | Last 50 engagement events with metadata |
| user_session_depth | Int | Request-time | Real-time | Posts scrolled so far in current session |
| user_active_hours | Vector(24) | Batch pipeline | Weekly | Probability of being active per hour |
| post_embedding | Vector(256) | Batch at create | At create | Multimodal embedding of text + media |
| post_age_minutes | Int | Streaming | Real-time | Minutes since post creation |
| post_creator_id | Id | Post metadata | At create | Foreign key to creator features |
| post_topic_distribution | Vector(32) | Classifier | At create + 10s | Soft distribution over topic taxonomy |
| post_quality_score | Float | Classifier | At create + 30s | Quality classifier output, 0 to 1 |
| post_integrity_score | Float | Classifier | At create + 30s | Safety classifier, hard filter above threshold |
| post_likes_per_minute | Float | Streaming | <10s | Early velocity, smoothed |
| post_hide_rate | Float | Streaming | <60s | Hides per 1000 impressions, smoothed |
| viewer_follows_creator | Bool | Graph service | <60s | Direct follow edge |
| viewer_creator_interactions_30d | Int | Batch pipeline | Daily | Count of engagements with this creator |
| viewer_creator_decayed_rate | Float | Batch pipeline | Daily | Time-decayed engagement rate |
| context_hour_of_day | Int | Request-time | Real-time | 0 to 23 in user locale |
| context_device_class | Categorical | Request-time | Real-time | Phone tier, tablet, web |
The freshness column matters more than it looks. A post's integrity score must be available before it can enter the ranker, but its velocity features update continuously after that. A viewer-creator interaction feature can lag by a day without hurting quality, because long-term affinity doesn't swing hour to hour. Knowing which features tolerate lag and which cannot is what lets the feature store keep the budget under control.
The first version of every feed ranker in history has been logistic regression on hand-crafted features with a single click label. It's not glamorous, but it's honest: it sets a floor the next model has to clear, and it exposes what the feature pipeline is actually producing.
A reasonable baseline looks like this:
This caps out quickly for two reasons. A click-only objective pushes the ranker toward clickbait and sensational content, because those items have the highest short-term click probability and the worst long-term effect on retention. And logistic regression cannot learn the dense interaction between a user embedding and a post embedding without extensive manual cross-featuring, so it leaves a lot of signal on the floor.
The production ranker is a multi-task deep model with a shared bottom and several task-specific heads. Variants in the literature include MMoE (Multi-gate Mixture of Experts) and PLE (Progressive Layered Extraction); the high-level architecture is similar across them.
The shared bottom compresses raw features into a dense representation. Experts are small MLPs that specialize in subsets of tasks. Task-specific heads take expert outputs (optionally gated) and predict the per-head probability. The hide head is trained with the same architecture but its prediction gets subtracted from the blended score at serving time, not added.
Why this architecture works well for feeds:
For embedding inputs, user and post embeddings come from two-tower retrieval models, or from dedicated content-understanding models for richer text and media representations. The ranker uses these embeddings as features rather than training them from scratch.
Training data comes from joining the impression log with labels that landed within the attribution window.
The value-model blend at serving time is a weighted sum:
Weights are set by offline grid search on a Pareto frontier of engagement vs negative-feedback, then confirmed by A/B testing against retention. Common practice is to set the comment and share weights noticeably higher than click and like, because those actions correlate more strongly with long-term retention. The hide weight is set aggressively: a single predicted hide probability above 0.05 should usually demote a post below anything without that signal.
A feed request starts when the user opens the app or scrolls deep enough to refresh. Every request goes through the same pipeline, though different surfaces (app launch vs pull-to-refresh vs deep scroll) parameterize it differently.
User state includes the user's embedding, recent-action sequence, and surface-level settings. It's cached at the edge for active users and refreshed every few seconds or on explicit invalidation. Candidate generation fans out in parallel to three sources, described in detail in the deep dives section. The integrity filter sits between candidate generation and the ranker to guarantee that no flagged content reaches a scoring model, regardless of what candidate generation produced.
Every component in the pipeline lands somewhere on the spectrum from fully precomputed to computed per request. Getting the split right is how feed systems hit 200ms p99 with billion-user catalogs.
| Component | Computed | Why |
|---|---|---|
| User long-term embedding | Daily batch | Changes slowly; retraining daily is enough |
| Post embedding | Once at post create | Content doesn't change after posting |
| User recent-action sequence | Streaming, <10s lag | Changes every scroll; request-time would miss the most recent action |
| Post early-velocity features | Streaming, <10s lag | Changes as engagement accrues; critical for new posts |
| Viewer-creator interaction rate | Daily batch | Long-term affinity is stable; real-time precision not worth the cost |
| Candidate generation | Request-time | Social graph and trending both change; cached per-user for seconds at most |
| Pre-ranker scores | Request-time | Depends on all three sources and user context |
| Ranker scores | Request-time | Must combine freshest user and post state |
| Diversity re-rank | Request-time | Depends on the specific 200 candidates and their topic mix |
The sharpest split is between daily-batch features and streaming features. A batch feature is cheap to compute once and serve many times; a streaming feature requires a continuously running job consuming from Kafka and writing to the online store. Feeds use more streaming features than most recommendation systems because the inventory itself is fresh: a post is created, accrues engagement, and decays in hours, so its features cannot be precomputed once and frozen.
The 200ms p99 target has to be allocated across the pipeline, with enough slack to absorb tail latency on any single component.
| Component | Budget | Notes |
|---|---|---|
| Network ingress + egress | 20ms | App to edge to ranking service |
| User state fetch | 15ms | Edge cache hit; miss triggers reshape |
| Candidate generation (parallel) | 30ms | Max of graph, trending, and ANN lookups |
| Integrity + hard filter | 10ms | In-memory bloom filters plus per-candidate check |
| Feature fetch for pre-ranker | 15ms | Online store reads, batched |
| Pre-ranker inference | 15ms | Small MLP or GBDT, CPU |
| Feature fetch for ranker | 20ms | Heavier feature set; cached where possible |
| Ranker inference | 40ms | Batched across 200 candidates, GPU |
| Diversity re-rank | 10ms | Single-threaded, deterministic |
| Ads blending | 5ms | Insert at fixed slots |
| Buffer | 20ms | Tail-latency headroom |
| Total | 200ms | P99 target |
The ranker inference budget drives the most decisions. Forty milliseconds for 200 candidates on GPU means the model has to be small enough to fit several requests per batch, or it has to be distilled from a larger teacher model. A common pattern is to train a big two-stage teacher and distill into a smaller student that fits the latency budget.
Every other system in this section has a cleaner objective. Video recommendations optimize watch time. Product recommendations optimize purchases. PYMK optimizes accepted connections. A news feed doesn't get to pick one. If the ranker chases clicks, users end up seeing outrage content and session length crashes a week later. If it chases long dwell, it under-weights quick reactions like likes that are genuine positive signal. If it chases pure "meaningful interactions," it can under-show time-sensitive content like breaking news that a user just wants to scan.
The value model is the hinge. Its job is to combine per-head predictions into a single scalar score where weights encode a business judgment about what the feed should feel like. Setting those weights is more of a policy decision than a modeling decision, and it's tuned against long-horizon A/B results, not same-day clicks.
Three strategies dominate in practice, each with distinct trade-offs.
| Strategy | How It Works | Pros | Cons |
|---|---|---|---|
| Hand-tuned weights | Weights chosen by PM and ML leads, validated by A/B | Transparent, easy to reason about | Slow to adapt; prone to drift as inventory changes |
| Learned weights | Weights optimized to maximize a chosen online metric via offline bandit or contextual RL | Adapts to shifting inventory; captures non-linear interactions | Hard to debug; can overfit to short-term signals if not regularized |
| Pareto + A/B confirm | Offline grid search generates a Pareto frontier of engagement vs negative feedback; candidates on the frontier go to A/B tests against retention | Explicit trade-off visualization; anchors product decisions | Expensive; needs a lot of A/B bandwidth |
Counterfactual reweighting is an important refinement in all three. Users see only the top-ranked items, so the log is biased toward the ranker's past beliefs. Inverse propensity scoring at training time corrects for this, and randomized exploration slots in production generate unbiased data for evaluating new value-model weights offline.
Surveys earn their keep here. Meta has publicly discussed using in-product surveys asking users to rate individual posts as a direct signal for quality. The survey signal is low volume compared to clicks, but it anchors the value model against something closer to genuine user preference than passive engagement metrics. Without a survey signal, any value-model tuning loop slowly overfits to whatever is measurable, and the feed drifts toward whatever form of engagement is easiest to provoke.
A user's social graph alone isn't enough to populate 50 quality slots. Follow graphs are sparse in the tail and dominated by inactive creators in the head; pure friend-and-follow feeds run dry within seconds of scrolling. Feed candidate generation pulls from three parallel sources, unions the results, and deduplicates before handing the pool to the pre-ranker.
The graph source walks direct follows, groups, and second-degree connections, returning recent posts sorted by a cheap recency-weighted engagement score. The trending source retrieves posts with high velocity restricted to topics or creator clusters the user has engaged with before. The topical ANN source performs an approximate nearest-neighbor lookup against post embeddings using the user's topic-affinity embedding as the query, pulling in content from accounts the user doesn't follow but is likely to engage with.
| Source | Strength | Weakness | Typical Quota |
|---|---|---|---|
| Social graph | Highest precision for engaged users, natural explanation ("your friend posted") | Dries up for low-connection users; biased toward head accounts | 40-60% |
| Trending-by-affinity | Brings in fresh, viral content within user's interest space | Risk of same-content-twice if dedupe is loose | 20-30% |
| Topical ANN | Exploration beyond the follow graph; critical for cold-start users | Lower precision; can surface low-quality content if not filtered | 10-30% |
The quotas are per-request targets and shift based on user state. A user with 5 follows gets almost all candidates from topical ANN. A user with 500 follows gets most from the graph. Cold-start post handling lives mainly in the trending source: a new post from a creator the user follows enters the graph source immediately, but posts from creators the user doesn't follow need to accumulate enough early velocity in the trending-by-affinity job before they become eligible for that user's feed. Giving new posts a small impression floor, even without signal, is how the system avoids an absorbing-state problem where new content can never accumulate enough data to compete.
Deduplication operates on two levels: the same post ID cannot appear twice, and near-duplicate content from different IDs (same link share, same reposted video) is collapsed to a single candidate based on content fingerprinting. The dedupe step sits before the pre-ranker so the expensive stages don't waste cycles on redundant items.
If a ranker optimizes pure predicted engagement, every user's feed collapses onto a narrow set of creators and topics. The top 200 candidates after ranking might all be from the same five creators, all on the same topic, because those are the highest-scoring items this user will see today. That's bad for users (monotony), bad for creators (winner-take-all), and bad for the platform (retention drops when the feed feels repetitive).
Re-ranking applies diversity, fairness, and dedupe constraints after the ranker has scored the candidate set. Several layers run in sequence, each enforcing a different kind of constraint.
The order matters. Near-duplicate collapse runs first so the greedy picker never compares two almost-identical items. Source and topic caps filter out candidates that would violate constraints before the picker sees them. The MMR layer does the actual diversity-aware selection. Creator-tail boost and exploration injection apply late, after the bulk ranking is done, so they don't distort the relative ordering of the main selections.
A common algorithm for the MMR layer is a greedy selection that balances relevance against redundancy.
The lambda controls how much diversity is worth in units of value-score. Setting lambda too high pushes out high-quality content in favor of variety; too low and the feed collapses. The right setting is typically small, around 0.1 to 0.3, and itself a hyperparameter subject to A/B testing.
Beyond MMR, several practical constraints layer on top.
| Technique | What It Does | When It Kicks In |
|---|---|---|
| Source caps | Limit N posts from any single creator per feed window | Always; typical cap is 2 or 3 per session |
| Topic caps | Limit the fraction of slots dedicated to any single topic | When topic mix across top 50 exceeds a threshold |
| Creator-tail boost | Small additive bonus to candidates from creators outside the top 1% by impressions | Always; magnitude tuned against fairness metrics |
| Exploration injection | Random slot insertion of candidates from under-explored topics | At configured frequency, typically 1 in 20 slots |
| Echo-chamber audit | Offline scan for users whose feed exposure has collapsed below a diversity threshold | Batch, daily; triggers reset of exploration injection rate |
The creator-tail boost deserves special attention. Without it, the top 0.1% of creators by follower count win almost every scoring comparison because they have more engagement data, better velocity features, and higher-quality content on average. Giving tail creators a small score bonus keeps the feed's creator distribution from collapsing onto the head, which matters for the platform's long-term content supply, not just fairness.
The median post has a useful life of a few hours. Some posts, like breaking news, have a useful life of minutes. Unlike video recommendations or product recommendations, where the catalog turns over slowly and content stays relevant for weeks, feed ranking is constantly racing against the clock.
Three design decisions follow from this.
First, post-create to ranker-eligible must be fast. The pipeline from post creation through integrity classification, indexing, and feature population runs on a streaming backbone with a 30-second budget. Anything slower and trending content misses its window. This is why quality and integrity classifiers have to be fast; they cannot afford minute-scale batch cadences.
Second, early-velocity features dominate early-life ranking. A post that's two minutes old has no viewer-creator interaction history to pull from, no long-term velocity, and no established engagement rate. What it has is likes-per-minute and hide-rate-per-impression over its first few hundred impressions, and those are the features that tell the ranker whether this specific post is resonating. Early-velocity features update every few seconds on a streaming job keyed by post ID.
Third, the scoring function applies an explicit recency decay. The ranker learns to predict the engagement probability from features, but those features include post age. In practice, a small multiplicative decay on the final score based on post age is still applied to ensure that two posts with similar predicted engagement don't crowd out freshness. A typical decay halves a post's effective score every 6 to 12 hours.
Spiky posts create a specific headache. A post that goes viral in its first hour accumulates velocity features so strong that the ranker predicts very high engagement for every viewer, which is mostly correct but has two failure modes. One, the post dominates the feed for hours until the velocity signal decays, pushing out everything else. Two, the velocity feature is easy to manipulate by coordinated early engagement, so a small cluster of accounts can inflate a post's early-life score. The mitigations are source caps in re-ranking (a single post can only appear once per user per day) and trust-weighted velocity (engagements from historically reliable accounts count more than engagements from brand-new accounts).
Model artifacts themselves have a freshness question. The ranker is retrained daily, but the world can shift faster than that during events like elections or natural disasters. Some platforms keep a small set of fast-retraining shadow models that can be swapped in if the live model's calibration degrades beyond a threshold. This is the difference between a ranker that's fresh and a ranker that's robust: the features can be seconds old, but if the model's weights are stale against a new distribution, the feature freshness doesn't help.
Before any new model or value-model weight touches production, it gets scored on held-out impressions from the previous day. Per-head AUC measures whether each head is well-calibrated; NDCG@10 on the blended score measures whether the new configuration would rank real engagement events higher than the live model would.
Two specific offline checks catch the most common failure modes. Calibration plots per head reveal when a head is systematically over- or under-predicting at certain deciles, which is usually the first sign of a feature-pipeline bug or distribution shift. And replay scoring, where the new ranker scores the same candidate pools the live ranker saw, computes the correlation between the two rankings. A correlation below 0.7 against the live model usually signals something more surgical than an experiment should change.
Every candidate model goes through a staged rollout: shadow mode where it scores but doesn't serve, a 1% A/B where it serves a small slice, then progressively larger slices if metrics hold.
The interesting thing about feed A/B tests is that short-horizon metrics can lie. A new ranker that cranks up the click weight will show better clicks in a one-week A/B and worse retention in a four-week holdout. The standard response is to maintain long holdouts: a 1% slice of users kept on the previous model for a quarter, with retention and survey scores measured against the treatment group. Compounding retention effects only show up in weeks, not days, so long holdouts are not optional.
Guardrail metrics run alongside the primary metrics on every test. The integrity false-positive rate cannot rise. Creator-tail impression share cannot collapse. Negative-feedback rate (hides, reports, not-interested clicks) cannot increase. If any guardrail moves beyond its threshold, the test is paused regardless of what the primary metric says.
Once a model is live, monitoring falls into four categories: feature drift, model calibration drift, guardrail metrics, and segment-level health.
Serving logs feed every monitor in parallel. Each monitor has its own threshold and its own response: a feature PSI spike pages the data team, a per-head calibration drift pages the ML team, a guardrail breach auto-pauses any active experiments on the affected surface.
| Monitor | What It Catches | Alert Threshold |
|---|---|---|
| Feature distribution (per feature) | Upstream pipeline failures, shifts during news events | 5% PSI change day-over-day |
| Head calibration (predicted vs observed) | Model degradation under distribution shift | Miscalibration >10% in any decile |
| Creator-tail impression share | Re-ranking weight drift | >5 percentage point drop week-over-week |
| Integrity false-positive rate | Over-filtering breaking feed quality | >0.5% FP on sampled audits |
| Per-segment NDCG (new users, low-activity users, power users) | Ranker regressions that hide in the average | >3% drop for any segment week-over-week |
| Negative-feedback rate | Slow creep toward outrage content | >10% week-over-week rise |
The trickiest monitor is per-segment health. An overall engagement metric can look flat while new users are getting a progressively worse experience, because power users generate most of the engagement volume and drown out new-user signal. Splitting by segment and alerting on segment-level regressions is how long-term retention gets protected against average-case drift.
Feature drift alerts are the most common false positive. Topic distributions shift during real-world events (elections, sports seasons, natural disasters), and the ranker has to absorb that shift without triggering a rollback. The standard pattern is to alert on drift but gate rollback on calibration or guardrail metrics, so a natural distribution shift doesn't unnecessarily revert a healthy model.
Q1: Why is a multi-task ranker the right choice for a feed, and what would go wrong with a single-head CTR model?
A single-head CTR model optimizes for one signal that correlates with short-term clicks but not long-term value. Clickbait has high CTR, so the feed drifts toward outrage and sensational content within weeks. Users who find their feed progressively worse churn quietly, which shows up as a retention decline months later, long after the ranker change that caused it. A multi-task model predicts click, like, comment, share, long-dwell, and hide probabilities jointly, and a value model blends them with weights tuned against retention. This lets the system make explicit trade-offs like "a predicted hide outweighs a predicted click" rather than pretending a single number captures everything.
Q2: How would you decide the weights in the value model that blends per-head predictions?
Three approaches work in practice and each has trade-offs. Hand-tuned weights are transparent and stable but slow to adapt as the content mix shifts. Learned weights, optimized via contextual bandits or RL, adapt faster but are hard to debug and can overfit to easy-to-measure signals. The most common approach in large teams is to generate a Pareto frontier offline over engagement vs negative-feedback, pick a few promising points on the frontier, and A/B test them against retention and survey-based quality metrics. Whichever approach you pick, counterfactual reweighting on the training logs and a small randomized exploration slot in production are essential, because the ranker's own past behavior biases every metric the new weights are scored on.
Q3: Why does feed ranking need streaming features more than most recommendation systems?
The useful life of a feed post is measured in hours. A post created five minutes ago has no viewer-creator interaction history, no established engagement rate, and no long-term velocity signal. What it has is a few minutes of early engagement data, and the ranker's ability to score it well depends on having that data available in under 10 seconds. In contrast, a video or product in a catalog has a shelf life of weeks or months, so daily-batch features capture most of the signal. Feeds can't afford that cadence because the window between a post being created and the ranker deciding whether to show it is often shorter than a batch job's schedule.
Q4: How do you prevent a feed ranker from collapsing onto a small set of creators, and what are the trade-offs of your approach?
The core issue is that pure engagement optimization is a winner-take-all process. Top creators have more data, better velocity signals, and higher average engagement, so they win almost every scoring comparison. Diversity re-ranking with a greedy MMR-style algorithm penalizes candidates too similar to what's already selected in the feed, and source caps limit how many posts any single creator can appear in per session. A creator-tail boost adds a small score bonus to candidates from creators outside the top 1% by impressions. The trade-off is that diversity costs short-term engagement: an MMR lambda of 0.3 might drop same-day engagement by 1-2% while increasing retention measurably over weeks. The balance is set by A/B tests on long-horizon metrics, not same-day clicks.
Q5: A new post goes viral in its first hour. Its velocity features predict extremely high engagement for every viewer. What can go wrong, and how do you mitigate it?
Two failure modes show up. The post dominates feeds for hours because its predicted score beats almost everything else, pushing out content variety and creating a monotonous experience. And the velocity features are easy to manipulate: a small cluster of accounts engaging immediately after post creation can inflate the early-life score, which is the basis of many coordinated-inauthentic-behavior campaigns. Mitigations include per-user-per-day source caps so a single post cannot appear more than once in a user's feed per session, trust-weighted velocity where engagements from accounts with a long history count more than engagements from brand-new accounts, and recency decay that halves effective score every 6-12 hours regardless of predicted engagement. These don't eliminate virality, they bound its reach.
The next chapter tackles ad click prediction, which shares many patterns with feed ranking but has its own specifics: extreme sparsity, tight calibration requirements, and an auction on the other side that turns predicted CTR into a bid.