</> MAANG.io
system design · 301

Advanced

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/480 solved 0% complete

Section 11: Growing Without a Rebuild - Follow-up Feature Evolution

Executive Summary: The interviewer reads out the product team's wish list, and each feature attaches at a seam the design already has. Ads and suggested posts become extra candidate sources plus a mixer after ranking. A chronological Following tab skips ranking. Stories get their own tray and TTL store, reusing the graph, ranking and visibility filter but not the feed cache. A "new posts" pill is a tiny hint, not a streaming feed. Post edits are free because feeds hold IDs. Fan-out, the feed cache and the API stay as they are.

The interviewer turns a page: "The feed works. Now product wants ads, suggested posts from accounts Maya doesn't follow, Stories, a chronological tab and a 'new posts' button. How much do you rebuild?"

The answer the panel wants is "almost none of it." Show where each feature plugs in, what it leaves alone, and which earlier decision made it cheap.

Ads and Suggested Posts: A Mixer After Ranking

Today the feed has one candidate source: the accounts Maya follows, ~480 IDs from her feed cache plus her 20 celebrity timelines, about 500 candidates. Ads and suggested posts are more sources that don't come from the follow graph. So I turn that first step into a list of candidate sources, each with one job: return scored post IDs before a deadline.

  • Following: the feed cache plus the celebrity merge. Unchanged.
  • Suggested: posts from accounts Maya doesn't follow, precomputed per user by an offline recommendation pipeline. They get their own model, because the strongest organic feature, affinity with the author, doesn't exist for a stranger.
  • Ads: an ad server that runs its own auction and returns ranked ads for this viewer.

A new mixer runs after ranking. It takes the ranked organic list and fills about every 5th position from the other sources, so roughly 4 slots on a 20-post page. Its rules: never two non-organic posts in a row, frequency caps per advertiser, and a source that misses its deadline loses its slots to organic posts.

Below, the sources feed ranking, the mixer fills slots, and the Following tab shortcuts both.

Feed Cache and Celebrity Timelines feed the Following source, which sends ~500 candidates to the Ranking Service alongside the Suggested source (own model, no affinity); ranked organic + suggested posts go to a Mixer that slots Ad Server ads (auction, 60 ms timeout) every ~5th position, then Visibility filter + hydration produces a 20-post feed page, while the Following tab skips ranking and goes straight to visibility filtering

The mixer itself is a few lines:

def mix(organic, extras, page_size=20, every=5):
    # extras: ads and suggested posts, already ordered by their own rules
    page = []
    while len(page) < page_size and organic:
        slot_open = (len(page) + 1) % every == 0
        page.append(extras.pop(0) if slot_open and extras else organic.pop(0))
    return page

The cursor's offset now counts organic IDs consumed, 16 per page here, not page positions.

At open, the Feed Service asks the ad server for a session's worth of ads in parallel with candidate assembly (~15 ms) and ranking (50 ms), about 65 ms of the 200 ms open budget. With an assumed 60 ms timeout the call adds nothing when healthy; when it isn't, the worst case is an all-organic page. The ads are stored beside Section 7's session snapshot, which keeps the organic and suggested ranking stable across scroll pages. Scroll pages pop from that ad queue after a quick budget check, so a campaign that runs out mid-session stops showing and scrolls never wait on the ad server.

Every non-organic post goes through the same filter at hydration, with one rule swapped by source. For organic posts the check is "still follows." For suggested posts it's "public account and public post." Deletes and blocks apply unchanged, so a suggested post from a private account or a blocked user is dropped like any other.

Interviewer: "Why a separate mixer? Put ads into the ranking model and let it sort everything."

You: "They optimize different things and have different owners. Organic ranking predicts engagement; ads rank by bid times predicted click, with pacing the feed team shouldn't own. One model turns an ads outage into a feed outage. The flip is a company decision to optimize one combined value score, and even then only the mixer's rule changes."

Key Insight: The candidate-source interface is the seam. Explore- or Reels-style sources plug in the same way: return scored IDs before a deadline. Nothing after them changes.

Quick check before moving on:


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.