~learn.

Side quest 21Feed and stories

Instagram Feed and Stories.

Three Billion People, Thousands of New Posts Each, and Twenty Worth Seeing First

HARD33 MIN READ6 PHASES · 45 MIN INTERVIEW+120 XP

Mission brief

Design the photo and video feed and stories of a social app

Quest log0/7 stages · 0/7 checkpoints · 0/7 quick checks
monthly active users (Meta, September 2025)
3 billion
of their feed people used to miss before ranking (Instagram, 2016)
70%
people using Stories every day (Facebook, January 2019)
500 million
less compute for basic video encodings (Meta engineering, 2022)
94%

Company names and logos are trademarks of their owners, shown only to identify each company. This course isn't affiliated with or endorsed by any of them.

Optional · timed run · 6 phases · 45 min

Want it harder? Hide the model answer and work each phase in a scratchpad against its time budget, then reveal and score yourself. Or simply read on: the checkpoints below work either way.

Intro · 3 min

How Do You Show Three Billion People the Few Posts They Care About Most, in the Half-Second After They Open the App?

It is 9 pm in Bengaluru. Meera opens Instagram on the bus home. She follows 600 accounts: friends from college, her cousin who just had a baby, a dozen chefs, two football clubs and a few hundred more. Since she last looked, this afternoon, they have posted 340 photos, videos and reels, and 90 of them have stories up.

She will scroll for maybe two minutes. In that time she'll see perhaps twenty posts. Her cousin's baby photo, posted three hours ago, has to be one of the first. The stories tray at the top has to show the people she actually watches, with the first story already loaded when she taps. And every one of those photos has to arrive sharp, on a bus, on a phone signal that comes and goes.

Instagram, from published numbers
  • 3 billion — monthly active users, announced by Mark Zuckerberg on 24 September 2025
  • 70% — of their feed that people missed on average, the reason Instagram began ranking it (Instagram, March 2016)
  • 500 million — people using Instagram Stories every day, the last daily figure Meta published (Q4 2018 earnings call, January 2019)
  • 4 stages — retrieval, first-stage ranking, second-stage ranking and final reranking in Explore, visited by "hundreds of millions" of people a day (Meta engineering, August 2023)
  • 5 — interactions Feed ranking predicts for each post: time spent, commenting, liking, sharing, tapping the profile photo (Adam Mosseri, May 2023)
  • 94% — less compute for basic video encodings after repackaging instead of re-encoding (Meta engineering, November 2022)

For context:
In December 2011, with 14 million users and three engineers, Instagram wrote that Redis powered its main feed and that it did "feed fan-out" in a task queue
Meta doesn't publish daily post volumes, so the estimates below state their own assumptions
Everything not in the list above is an illustrative design target, not Instagram's internal architecture

This is the system we'll design today.

ExplainerThe whole quest: Instagram

Instagram has 3 billion monthly users; before ranking, people missed about 70% of their feeds. On stated assumptions the feed peaks near 700,000 pages a second, ~300 reads per post, and ~5.7 PB of new media a day. Media is stored once and resized, post IDs sort by time and fan out to inboxes (huge accounts are pulled at read time), feeds are ranked in stages on five predicted interactions, and the stories tray reads an index that expires at 24 hours. The deep dives cover media and video encodings, IDs and fan-out, the ranking funnel and stories, then huge accounts, deletes and New Year's Eve.

The Interview Begins#

You're interviewing at a company building a photo-sharing app. The interviewer hands you a phone with the app open. She scrolls: a friend's photo, a reel, a carousel from a restaurant, a suggested post. At the top, a row of circles with coloured rings.

Interviewer

"Two things on this screen are the whole product: that feed and that row of stories. Design what fills them."

Design the feed and stories of a photo and video app

People post photos, carousels and videos to their followers, and stories that leave the tray after 24 hours. When anyone opens the app they see the stories of the accounts they follow at the top and a ranked feed of recent posts below, with media that loads quickly on any network.

Requirements
  • Upload photos and videos and make them viewable at every size the app shows
  • Build each person's feed from the accounts they follow, ranked so the most relevant come first
  • Stories: a tray of accounts with unseen stories, ranked, expiring after 24 hours
  • Feed and tray on screen in well under a second, media loading as you scroll
  • Survive an account with hundreds of millions of followers posting
Interviewer

"Leave out Direct messages, Reels recommendations, search and ads. Feed, stories, media."

Phase 1 · Stage 1 of 7 · 5 min budget · 2 min read

Phase 1: Requirements Clarification (5 minutes)#

You

"The feed is a read problem and the media is a storage problem; I want numbers for both."

Your Questions#

Transcript
You

"How many people?"

Interviewer

"Instagram reached 3 billion monthly active users in September 2025. Meta doesn't publish a daily figure for Instagram; assume half of them open it on a given day."

You

"Chronological, or ranked?"

Interviewer

"Ranked. Instagram started ranking the feed in 2016 because people missed 'on average 70 percent of their feeds'. Adam Mosseri wrote in 2023 that each part of the app, 'Feed, Stories, Explore, Reels, Search and more', uses 'its own algorithm tailored to how people use it'."

You

"What does Feed ranking look at?"

Interviewer

"By his account, in rough order of importance: your activity, information about the post, information about the person who posted, and your history with them. It predicts five things for each post: how long you'll spend on it, and how likely you are to comment, like, share or tap the profile photo."

You

"And stories?"

Interviewer

"They stay in the tray for 24 hours, and since 2017 they 'automatically save to your archive when they expire'. The tray is ranked on viewing history, engagement history and closeness. Meta last published a daily Stories figure in January 2019: 500 million people a day."

You

"Biggest accounts?"

Interviewer

"Hundreds of millions of followers. Assume one with 400 million posts during a final."

Functional Requirements#

Functional requirements
  • Post: photos, carousels and videos, with caption, location and tags; visible to followers (approved followers for private accounts)
  • Media: every photo and video stored once and served at the sizes and encodings each screen needs
  • Feed: posts from followed accounts, ranked; suggested posts mixed in once the new ones run out; "seen" posts not shown again at the top
  • Stories: post a story to everyone or to a close-friends list; a ranked tray of accounts with unseen stories; viewers list for the author; expiry from the tray after 24 hours; archive
  • Interactions: like, comment, share, save; each one feeds ranking
  • Delete and privacy: a deleted post or a blocked viewer disappears from feeds within seconds

Non-Functional Requirements#

Non-functional requirements
Latency (design targets)
  • Feed first page: p99 < 500 ms server time; stories tray p99 < 300 ms
  • First visible photo: < 1 s on a typical mobile connection
  • New post visible to followers who open the app: within a few seconds, minutes for the largest accounts
Scale
  • 1.5 billion daily users (assumed half of 3 billion monthly)
  • An account with 400 million followers posting at a peak moment
Durability
  • No posted photo or video ever lost; expired stories leave the tray but stay in the author's archive

Phase 2 · Stage 2 of 7 · 5 min budget · 3 min read

Phase 2: Back of the Envelope Estimation (5 minutes)#

You

"Meta doesn't publish posting volumes, so every rate below is an assumption I'll say out loud. The shape is what matters: reads dwarf writes, and media bytes dwarf everything."

Your turn · checkpoint 1not answered

Before the numbers: how many feed requests at the evening peak?

1.5 billion daily users each open the feed 10 times a day, and each open loads 2 pages. The evening peak is 2× the average. How many feed pages a second at the peak? If ranking scores 500 candidates per page, how many model scores a second is that?

Think it through, or jot it down. Then compare.

One strong answer

  • Pages a day: 1.5 billion × 10 × 2 = 30 billion.
  • Average: 30 billion ÷ 86,400 s ≈ 350,000 pages a second; at a 2× peak, ~700,000. Design for 800,000.
  • Scores: 700,000 × 500 = 350 million model scores a second, which is why a cheap first stage cuts candidates before the expensive model runs.

Traffic Estimation#

WorksheetFeed and stories traffic (every rate is an assumption: Meta doesn't publish them)
Value
Daily users (half of 3 billion monthly) 1.5 billion
Feed pages a day (10 opens × 2 pages) 30 billion
Feed pages a second, average / peak ~350,000 / ~700,000
Stories tray loads a second, peak ~700,000 (one per feed open)
  • Posts: assume 100 million feed posts a day: ~1,160 a second, ~2,300 at peak
  • Story frames: assume 1 billion a day: ~11,600 a second, ~23,000 at peak
  • Story views: 500 million daily Stories users × 30 frames watched = 15 billion seen-marks a day, ~174,000 writes a second, ~350,000 at peak

Conclusion: the feed is read about 300 times for every post written (30 billion pages, 100 million posts). Do the expensive work once per post, and keep each read cheap.

Fan-Out Estimation#

WorksheetDelivering post IDs to followers (follower counts are assumptions)
  • Average post: delivered to ~200 followers' inboxes: 100 million × 200 = 20 billion inbox writes a day, ~230,000 a second
  • The 400-million-follower account: one post = 400 million inbox writes; at 1 million writes a second that is ~7 minutes, and 10 posts during a final would queue for over an hour
  • At read time instead: each of Meera's feed opens fetches recent posts from the few huge accounts she follows and merges them: a few extra lookups per read, served from a cache

Conclusion: push post IDs to normal accounts' followers when they post; pull from huge accounts when the reader opens the feed. The Twitter quest (Bonus Problem 10) works through this hybrid in depth.

Media Estimation#

WorksheetMedia storage and delivery (sizes and mixes are assumptions)
  • Photos: 60 million a day × (a ~3 MB original + ~1 MB of resized copies) = 240 TB a day
  • Feed videos: 40 million a day × (~30 MB original + ~30 MB of encodings) = 2.4 PB a day
  • Story frames: 700 million photos × 1 MB + 300 million videos × 8 MB = 3.1 PB a day
  • Total new media: ~5.7 PB a day; ~2.1 EB a year before replication
  • Reads: 700,000 pages a second × ~10 items each = 7 million media fetches a second at peak; at ~150 KB average, ~8 Tbps, almost all of it from CDN caches

Conclusion: media is where the bytes and the money are. Feed data (IDs, scores, captions) is tiny beside it, and expired stories still cost storage because they move to the archive rather than being deleted.

Key Metrics Summary#

Metric Value
Daily users (assumed) 1.5 billion
Feed pages at peak ~700,000/s
Ranking scores at peak ~350 million/s
Inbox writes ~20 billion a day
Story seen-marks ~15 billion a day
New media ~5.7 PB a day
Media egress at peak ~7 million fetches/s, ~8 Tbps
Quick check3 questions · +5 XP each

Requirements and estimation

  1. Q1/3 30 billion feed pages a day and 100 million posts a day. Roughly how many times is the feed read per post written?

    Answer & explanation

    Answer: C

    30 billion ÷ 100 million = 300. Work done once per post is cheap; work done per read is multiplied.

  2. Q2/3 An account with 400 million followers posts. Why not write the post ID to every follower's inbox?

    Answer & explanation

    Answer: B

    At a million writes a second it's ~7 minutes per post. Pulling those posts at read time costs a few cached lookups.

  3. Q3/3 Stories leave the tray after 24 hours. Why does their storage not simply disappear?

    Answer & explanation

    Answer: C

    Since 2017 stories automatically save to the archive when they expire, so they move to cheaper storage rather than being deleted.

Phase 3 · Stage 3 of 7 · 10 min budget · 4 min read

Phase 3: High-Level Design (10 minutes)#

Your turn · checkpoint 2not answered

Sketch it first: Meera's cousin posts a baby photo

Trace the photo from her cousin's phone to Meera's screen three hours later. What happens to the image bytes, and where do they live? How does the post's ID reach Meera? What runs when Meera opens the app, and in what order do the feed and the tray load?

Think it through, or jot it down. Then compare.

One strong answer

  • Bytes first: the app uploads the photo to a media service, which stores the original and makes resized copies; each copy gets a key and is served through the CDN.
  • Then the post: a small record (author, media keys, caption, time) is written with a time-sortable ID to the author's shard.
  • Then fan-out: a background job adds the post ID to the inboxes of the cousin's followers, Meera included. The upload doesn't wait for it.
  • Meera opens the app: the tray service returns accounts with unseen stories, ranked; the feed service reads her inbox (plus recent posts from any huge accounts she follows), drops what she has seen, ranks the rest in stages, and returns the first page of IDs.
  • Hydrate and fetch: the page is filled in with captions, counts and media URLs; the app fetches the right-sized image for her screen from the nearest CDN edge, and prefetches the next few.
You

"Three planes: media (bytes), posts and inboxes (IDs), and ranking (decisions). Each scales in a different way."

System Architecture#

DiagramFeed and stories architecture
%% title: Feed and stories architecture
flowchart TD
  subgraph L1["Layer 1 · Clients"]
    direction LR
    AND@{ icon: "tech:android", label: "Android app" }
    IOS@{ icon: "tech:apple", label: "iOS app" }
    WEBC@{ icon: "sd:browser", label: "Web" }
  end
  subgraph L2["Layer 2 · Edge"]
    direction LR
    CDN@{ icon: "sd:cdn", label: "CDN<br/>images and video segments" }
    API@{ icon: "sd:api", label: "API gateway<br/>feed, tray, post" }
  end
  subgraph L3["Layer 3 · Media"]
    direction LR
    MED["Media service<br/>resize, encode, store once"]:::service
    BLOB@{ icon: "sd:object-storage", label: "Blob storage<br/>hot, then warm tiers" }
  end
  subgraph L4["Layer 4 · Posts and inboxes"]
    direction LR
    POST@{ icon: "tech:postgresql", label: "PostgreSQL · posts<br/>sharded by author" }
    FAN@{ icon: "sd:worker", label: "Fan-out workers<br/>post IDs to followers" }
    INB@{ icon: "tech:cassandra", label: "Cassandra · inboxes<br/>recent post IDs per viewer" }
  end
  subgraph L5["Layer 5 · Ranking"]
    direction LR
    FEED["Feed service<br/>candidates, stages, rules"]:::hot
    TRAY["Stories tray<br/>unseen, ranked, 24 h"]:::core
    FS@{ icon: "sd:ml", label: "Models and features<br/>embeddings, counters" }
  end
  AND ==> API
  IOS ==> API
  WEBC ==> API
  AND ==>|"media"| CDN
  API -->|"upload"| MED
  MED --> BLOB
  CDN --> BLOB
  API -->|"create post"| POST
  POST -.->|"new post"| FAN
  FAN ==> INB
  API ==>|"open feed"| FEED
  API --> TRAY
  FEED --> INB
  FEED --> FS
  TRAY --> FS
  class AND,IOS,WEBC client
  class CDN,API edge
  class BLOB,POST,INB store
  class FAN service
  class FS service

KEY INSIGHTS:

  • Bytes and IDs travel separately. The media service and CDN move megabytes; the post, inbox and ranking systems move 64-bit IDs and a few fields
  • Write once per post, read cheaply per open: resizing, encoding and fan-out happen once; the feed read is an inbox lookup, a ranking pass and a cache-friendly hydrate
  • Ranking is its own tier with its own models and features, because it is where most of the compute per read goes

Data Flow: One Photo, From Posting to the Top of Meera's Feed#

DiagramA baby photo, from the cousin's phone to Meera's feed
%% title: A baby photo, from the cousin's phone to Meera's feed
sequenceDiagram
  autonumber
  participant C as Cousin's app
  participant M as Media service
  participant P as Posts shard
  participant F as Fan-out workers
  participant I as Meera's inbox
  participant R as Feed service
  participant Me as Meera's app
  C->>M: Upload photo, 3.2 MB
  M->>M: Store original, make 1080, 640, 320, thumbnail
  M-->>C: Media keys
  C->>P: Create post with media keys
  P-->>C: Post ID, time-sortable
  P-)F: New post by an account with 420 followers
  F->>I: Append post ID to 420 inboxes
  Note over Me: three hours later
  Me->>R: Open feed
  R->>I: Recent post IDs, newest first
  R->>R: Drop seen, rank in stages
  R-->>Me: Page 1 · the baby photo is first
  Me->>Me: Fetch the 1080 copy from the CDN

MEANWHILE, IN PARALLEL:

  • The stories tray loads in its own request; the first two stories' media is prefetched before Meera taps
  • Every like, comment and second of viewing time goes back as events that update the counters and features ranking reads
Quick check3 questions · +5 XP each

High-level design

  1. Q1/3 The cousin taps "Share". What must finish before the app shows the post as posted?

    Answer & explanation

    Answer: C

    Fan-out runs in the background, as Instagram wrote in 2011, so posting is as fast for a big account as a small one.

  2. Q2/3 Why do post IDs carry a timestamp in their top bits?

    Answer & explanation

    Answer: A

    Instagram's 2012 design: 41 bits of time first, so an inbox of IDs is already in time order.

  3. Q3/3 Where is most of a feed read's compute spent?

    Answer & explanation

    Answer: D

    Hundreds of candidates × several model predictions each, per page, is the expensive part of a read.

Phase 4 · Stage 4 of 7 · 20 min budget · 12 min read

Phase 4: Deep Dives (20 minutes)#

Deep Dive 1: Media, Stored Once and Served at Every Size (Week 4 Concepts: Multi-Tier Caching; Week 1: Replication)#

You

"Most of the bytes we store are photos and videos, and most of the bytes we send are the same few thousand popular ones. Store each file once, derive the sizes, and let caches do the rest."

This builds on Week 4, Day 5 · Multi-Tier Caching.

Your turn · checkpoint 3not answered

One photo, six screens

The cousin's 3.2 MB photo will be shown full-width on a large phone, half-width in a grid, as a thumbnail in notifications, and on the web. What do you store, what do you make at upload versus on demand, and what does Meera's phone actually download?

Think it through, or jot it down. Then compare.

One strong answer

  • Keep the original, never shown directly, so the sizes can be remade later (a new format, a new screen size).
  • Make the common sizes at upload: e.g. 1080, 640 and 320 pixels wide plus a small thumbnail, in an efficient format. Each is a few hundred KB at most; the set is ~1 MB.
  • Rare sizes on demand, at the edge, then cached.
  • Keys are content addresses: media/<hash of the bytes>/<size>.<format>. The same file shared twice is stored once, and a cached copy never needs invalidating because a changed image gets a new key.
  • Meera's phone asks for the size that fits its screen, from the nearest CDN edge. A popular post is a cache hit; her cousin's photo, seen by 420 followers, may be one of the few misses that reach storage.

Videos cost far more than photos. Meta's 2022 post on Instagram video explains why: each upload gets "minimum functionality" encodings that every client can play (basic adaptive-bitrate ladders, plus progressive files for old app versions) and "advanced encodings" with newer compression. Instagram was spending "more than 80 percent" of its video compute on minimum-functionality encodings while advanced ones "covered only 15 percent of total watch time". Its fix was to stop encoding the basic ABR ladder separately: it repackaged the progressive encodings' frames into an ABR-capable structure, which took 0.36 seconds instead of the 86.17 CPU-seconds a 720p encode needed. That cut the cost of basic ABR encodings by 94% and let advanced-encoding watch time coverage grow by 33%.

DiagramOne upload through the media pipeline
%% title: One upload through the media pipeline
flowchart TD
  UP(["Upload<br/>photo or video"]):::client
  ORIG@{ icon: "sd:object-storage", label: "Original<br/>kept, never served" }
  subgraph PH["Photos · at upload"]
    direction LR
    SIZES["1080, 640, 320,<br/>thumbnail"]:::service
  end
  subgraph VI["Videos · by watch time"]
    direction LR
    BASIC["Minimum functionality<br/>encode once, repackage"]:::core
    ADV["Advanced encodings<br/>for videos people watch"]:::queue
  end
  HOT@{ icon: "sd:object-storage", label: "Hot tier<br/>new and popular" }
  WARM@{ icon: "sd:object-storage", label: "Warm tier<br/>older, erasure-coded" }
  EDGE@{ icon: "sd:cdn", label: "CDN edges<br/>most reads end here" }
  UP --> ORIG
  ORIG --> SIZES --> HOT
  ORIG --> BASIC --> HOT
  ORIG -.->|"once it is watched enough"| ADV -.-> HOT
  HOT -.->|"after weeks"| WARM
  HOT --> EDGE
  WARM --> EDGE
  class ORIG,HOT,WARM store
  class EDGE edge
  • Hot, then warm. A post gets most of its views in its first days. Facebook's f4 paper (2014) describes moving such "warm" blobs from triple replication to erasure-coded storage, cutting the effective replication factor from 3.6 to 2.8 or 2.1. The same idea applies here: after a few weeks, a photo's bytes move to cheaper storage; its key doesn't change.
  • Expired stories go warm immediately, since only their author's archive will open them.
  • Deletes are tombstones first: the post is hidden at once; the media keys are deleted from storage and purged from the CDN afterwards.
pythonmedia_key · 26 lines
# media/keys.py

"""
Content-addressed media keys (illustrative). The same bytes always get the
same key, so a re-share is stored once and a cached copy never goes stale:
an edited image is new bytes, so a new key.
"""

import hashlib

PHOTO_SIZES = (1080, 640, 320, 150)


def media_key(original: bytes) -> str:
    return hashlib.sha256(original).hexdigest()[:32]


def derived_url(key: str, width: int, fmt: str = "webp") -> str:
    return f"https://cdn.example.com/m/{key}/{width}.{fmt}"


def pick_width(screen_px: int, fraction_of_screen: float) -> int:
    """The smallest stored size that still looks sharp in its slot."""
    need = screen_px * fraction_of_screen
    fits = [w for w in sorted(PHOTO_SIZES) if w >= need]
    return fits[0] if fits else max(PHOTO_SIZES)

Deep Dive 2: Posts, IDs and Inboxes (Week 1 Concepts: Partitioning, Hot Keys; Week 4: Feed Caching)#

You

"The feed starts as a list of candidate post IDs per viewer. Getting them there cheaply is the fan-out problem; making them sortable without a lookup is the ID problem."

This builds on Week 4, Day 4 · Feed Caching and Week 1, Day 1 · Partitioning Deep-Dive.

Your turn · checkpoint 4not answered

IDs that sort themselves

Posts are written to thousands of database shards at the same moment. You want 64-bit IDs that are unique across all of them, sortable by time, and that tell you which shard holds the post, without a central ID service. How would you lay out the 64 bits?

Think it through, or jot it down. Then compare.

One strong answer

  • Instagram's 2012 answer: 41 bits of milliseconds since a custom epoch, 13 bits of logical shard ID, 10 bits of a per-shard sequence modulo 1,024.
  • Time first: sorting IDs sorts posts by creation time, so an inbox of IDs is already in feed order with no lookups.
  • Shard inside: 13 bits address 8,192 logical shards; the logical shard maps to a physical database in configuration, so shards can move between machines "without having to re-bucket any of our data".
  • Sequence: 1,024 IDs per shard per millisecond, generated in the database itself with no new service to run.
  • Runway: 41 bits of milliseconds is about 69 years from the epoch (Instagram's post says 41 years).
pythonmake_id · 30 lines
# posts/ids.py

"""
Time-sortable 64-bit IDs in the layout Instagram published in 2012
(illustrative Python; Instagram generated them in PL/pgSQL inside each
logical shard): 41 bits of milliseconds since an epoch, 13 bits of logical
shard, 10 bits of a per-shard sequence.
"""

import time

EPOCH_MS = 1314220021721          # the epoch in Instagram's published example (August 2011)
SHARD_BITS, SEQ_BITS = 13, 10


def make_id(shard_id: int, seq: int, now_ms: int = None) -> int:
    now_ms = int(time.time() * 1000) if now_ms is None else now_ms
    return ((now_ms - EPOCH_MS) << (SHARD_BITS + SEQ_BITS)) | (shard_id << SEQ_BITS) | (seq % 1024)


def shard_of(post_id: int) -> int:
    return (post_id >> SEQ_BITS) & ((1 << SHARD_BITS) - 1)


def time_of_ms(post_id: int) -> int:
    return (post_id >> (SHARD_BITS + SEQ_BITS)) + EPOCH_MS


def logical_shard(user_id: int, logical_shards: int = 2000) -> int:
    return user_id % logical_shards     # the post's example: user 31341 -> shard 1341

Fan-out: push for most, pull for the few#

DiagramHybrid fan-out for a new post
%% title: Hybrid fan-out for a new post
flowchart TD
  NEW(["New post<br/>post ID, author"]):::client
  CHK{"Author's followers<br/>above 1 million?"}:::core
  PUSH@{ icon: "sd:worker", label: "Fan-out workers<br/>batches of 1,000 inboxes" }
  INBOX@{ icon: "tech:cassandra", label: "Cassandra · inboxes<br/>newest 1,500 IDs per viewer" }
  CEL@{ icon: "tech:redis", label: "Redis · recent posts<br/>of huge accounts" }
  READ["Feed read<br/>inbox ∪ huge accounts followed"]:::hot
  NEW --> CHK
  CHK -->|"no"| PUSH --> INBOX
  CHK -->|"yes: skip fan-out"| CEL
  INBOX --> READ
  CEL --> READ
  class INBOX store
  class CEL cache
  class PUSH service
  • The inbox is bounded: the newest ~1,500 post IDs per viewer, trimmed on write. Nobody scrolls further back than that through new posts; older ones come from elsewhere if needed.
  • Fan-out jobs are idempotent: appending the same post ID to an inbox twice is a no-op, so a retried batch is harmless.
  • Inactive followers are skipped: fan-out writes only to viewers active in the last month; a returning viewer's inbox is rebuilt by pulling from the accounts they follow.
  • Unfollow and block are filters at read time first (drop IDs from that author), and inbox cleanup later.
pythonon_new_post · 27 lines
# feed/fanout.py

"""
Hybrid fan-out (illustrative). Normal accounts push the new post's ID into
followers' inboxes in batches; huge accounts skip fan-out and are pulled at
read time. Every write is idempotent.
"""

HUGE = 1_000_000
BATCH = 1_000
INBOX_MAX = 1_500


def on_new_post(post: dict, graph, inboxes, celeb_cache, active) -> None:
    if graph.follower_count(post["author"]) >= HUGE:
        celeb_cache.add_recent(post["author"], post["id"])        # readers pull it
        return
    for batch in graph.followers(post["author"], batch=BATCH):
        viewers = [v for v in batch if active.recently(v)]
        inboxes.add_many(viewers, post["id"], keep=INBOX_MAX)     # set semantics: retry-safe


def candidates(viewer: int, graph, inboxes, celeb_cache, limit: int = 1_500) -> list:
    ids = set(inboxes.recent(viewer, limit))
    for author in graph.huge_accounts_followed(viewer):           # usually a handful
        ids.update(celeb_cache.recent(author, limit=50))
    return sorted(ids, reverse=True)                               # IDs sort by time
Quick check4 questions · +5 XP each

Media and inboxes

  1. Q1/4 Why name a stored photo by a hash of its bytes?

    Answer & explanation

    Answer: B

    A changed image is new bytes and so a new key; nothing cached ever needs invalidating.

  2. Q2/4 In Instagram's 2012 ID layout, what do the 13 middle bits hold?

    Answer & explanation

    Answer: C

    41 bits of time, then 13 of logical shard, then 10 of sequence: the ID says where the row is.

  3. Q3/4 Instagram's basic video encodings once took over 80% of its video compute. What cut their cost by 94%?

    Answer & explanation

    Answer: C

    Repackaging took 0.36 s where a 720p encode took 86 CPU-seconds, freeing compute for advanced encodings.

  4. Q4/4 A post's author has 3 million followers. In the hybrid design, how does the post reach their followers' feeds?

    Answer & explanation

    Answer: B

    Above the threshold, fan-out is skipped; a few cached lookups at read time replace millions of writes.

Deep Dive 3: Ranking the Feed in Stages (Week 7 Concepts: Relevance; Week 8: Event Ingestion)#

You

"Once we have 1,500 candidate IDs, ranking decides which twenty Meera sees. It is the most expensive part of a read, so it runs as a funnel."

This builds on Week 7, Day 3 · Query Processing and Relevance.

Your turn · checkpoint 5not answered

1,500 candidates, 300 ms

Meera has 1,500 candidate posts. Your best model scores one post in 0.5 ms of accelerator time and predicts the five things Mosseri listed; a light model scores one in 0.01 ms. You have about 300 ms. How do you split the work, how do you turn five predictions into one order, and what rules run after the model?

Think it through, or jot it down. Then compare.

One strong answer

  • Filter first, free: drop posts she has seen, posts from blocked or muted accounts, deleted posts. Say 1,500 become 1,200.
  • First stage: the light model scores all 1,200: 12 ms. Keep the best 300.
  • Second stage: the heavy model scores 300 × 0.5 ms = 150 ms of accelerator time, batched in parallel to ~40 ms of wall time. It predicts time spent and the probabilities of comment, like, share and profile tap.
  • One number: a weighted sum of the predictions, the "value" of showing that post, with weights chosen by experiments.
  • Final rules: no more than two posts in a row from one account, alternate photos and videos, keep integrity filters, and lift very fresh posts from close friends. Her cousin's baby photo, with a high predicted comment probability and a close relationship, comes first.

What Instagram and Meta have published: Instagram began ranking the feed in March 2016, ordering it by "the likelihood you'll be interested in the content, your relationship with the person posting and the timeliness of the post". In 2023 Mosseri described the signals, in rough order of importance (your activity, information about the post, information about the person who posted, your history of interacting with them), and the five predictions: time spent, comment, like, share and profile tap. Meta's 2023 post on Explore describes the funnel shape: retrieval narrows billions of items, a lightweight first stage scores thousands, a heavy multi-task second stage ranks about a hundred, and a final reranking applies integrity filters and diversity rules. It uses "Two Towers" models whose user embeddings can be cached and whose item embeddings sit in approximate-nearest-neighbour indexes.

DiagramThe feed ranking funnel for one page
%% title: The feed ranking funnel for one page
flowchart TD
  C(["1,500 candidates<br/>inbox ∪ huge accounts"]):::client
  F1["Filters<br/>seen, blocked, muted, deleted"]:::service
  S1@{ icon: "sd:ml", label: "First stage · light model<br/>1,200 → 300" }
  S2@{ icon: "sd:ml", label: "Second stage · multi-task<br/>time, comment, like, share, profile tap" }
  V["Value = weighted sum<br/>of the five predictions"]:::core
  RR["Final rerank<br/>diversity, integrity, freshness"]:::core
  OUT(["Page of 10<br/>logged as impressions"]):::ok
  FE@{ icon: "tech:redis", label: "Redis · features<br/>counts, closeness, recent activity" }
  C --> F1 --> S1 --> S2 --> V --> RR --> OUT
  FE -.-> S1
  FE -.-> S2
  class S1,S2 service
  class FE cache
pythonvalue · 46 lines
# feed/rank.py

"""
Staged feed ranking (illustrative). Cheap filters, a light model over all
candidates, a heavy multi-task model over the survivors, one value per post
from the five predictions, then rules. Weights come from experiments.
"""

from typing import Dict, List

WEIGHTS = {"time_spent_s": 0.02, "p_comment": 4.0, "p_like": 1.0, "p_share": 3.0, "p_profile_tap": 1.5}
FIRST_KEEP = 300
PAGE = 10
MAX_RUN_PER_AUTHOR = 2


def value(pred: Dict[str, float]) -> float:
    return sum(WEIGHTS[k] * pred[k] for k in WEIGHTS)


def rank_page(viewer: dict, candidate_ids: List[int], deps) -> List[int]:
    posts = deps.posts.get_many(candidate_ids)                       # cached hydrate
    posts = [p for p in posts
             if p["id"] not in viewer["seen"]
             and p["author"] not in viewer["blocked_or_muted"]
             and not p["deleted"]]

    light = deps.light_model.score(viewer, posts)                    # ~0.01 ms each
    survivors = [p for _, p in sorted(zip(light, posts), key=lambda x: x[0], reverse=True)][:FIRST_KEEP]

    preds = deps.heavy_model.predict(viewer, survivors)              # batched multi-task
    scored = sorted(((value(pr), p) for pr, p in zip(preds, survivors)), key=lambda x: x[0], reverse=True)

    page, run_author, run_len = [], None, 0
    for _, p in scored:
        if deps.integrity.blocked(p):
            continue
        if p["author"] == run_author and run_len >= MAX_RUN_PER_AUTHOR:
            continue                                                 # diversity: no long runs
        run_len = run_len + 1 if p["author"] == run_author else 1
        run_author = p["author"]
        page.append(p["id"])
        if len(page) == PAGE:
            break
    deps.impressions.log(viewer["id"], page)                         # labels come back as events
    return page

Features, freshness and the seen state#

  • Features are mostly counters: how often Meera comments on her cousin's posts, how many likes a post has gathered in its first hour, how long she watches videos from this account. They are updated from interaction events (Week 8's event ingestion) and read from a cache at ranking time.
  • The seen state is a per-viewer set of post IDs that reached the screen, with a short expiry. Without it, the same top post greets her on every open.
  • Freshness is a feature, not a sort: a post's age is an input to the model, so a three-hour-old baby photo can outrank a ten-minute-old ad-like post from a brand.
  • Fallbacks: if the heavy model is slow or down, serve the first-stage order; if features are missing, serve newest-first from close accounts. A worse feed beats an empty one.

Deep Dive 4: Stories, the Tray and 24 Hours (Week 9 Concepts: Right to Deletion; Week 4: Caching)#

You

"Stories look like a feed with a timer, but the read is different: the tray asks 'which of the accounts I follow have something I haven't seen?', and the answer changes every few seconds."

Your turn · checkpoint 6not answered

The tray in 300 ms

Meera follows 600 accounts; 90 of them have stories up from the last 24 hours, and she has watched all of 30. When she opens the app, how do you find the 60 with unseen stories, order them, and have the first story ready when she taps? What happens at hour 24?

Think it through, or jot it down. Then compare.

One strong answer

  • Active-story index: for each account, the IDs of its stories from the last 24 hours, kept in a cache with a 24-hour expiry per entry. The tray service intersects Meera's followings (600) with accounts that have an entry: 90.
  • Seen state: for each viewer, the story IDs she has watched, with a ~48-hour expiry. An account is unseen if its newest story isn't in her set: 60.
  • Order: rank the 60 by predicted interest (Mosseri lists viewing history, engagement history and closeness, and predictions such as tapping in, replying or moving on), then put fully-seen accounts at the end.
  • Ready on tap: prefetch the first frame of the top two or three accounts while the tray renders.
  • At hour 24: the index entry expires, so the story leaves every tray at once without anyone deleting anything from feeds. Its media moves to the author's archive in warm storage, and its viewers list stays available to the author.
DiagramBuilding one stories tray
%% title: Building one stories tray
sequenceDiagram
  autonumber
  participant Me as Meera's app
  participant T as Tray service
  participant G as Follow graph
  participant A as Active-story index
  participant S as Seen state
  Me->>T: Open app
  T->>G: Accounts Meera follows · 600
  T->>A: Which have stories in the last 24 h? · 90
  T->>S: Which of those has Meera seen? · 30 fully
  T->>T: Rank the 60 unseen, seen ones last
  T-->>Me: Tray of 90, first 60 ringed
  Me->>Me: Prefetch first frames of the top 3
  Me->>S: Seen story 8812 (on view)
  • Audience is part of the index: a close-friends story is indexed with its audience list, so the tray only shows it to people on the list. The check runs at read time, so removing someone from the list hides future reads at once.
  • Seen-marks are the write load: ~350,000 a second at peak. They go to a key-value store as small set additions, batched on the phone and sent every few seconds; losing one means a story looks unseen once more, not a correctness failure.
  • The viewers list is the same events seen from the author's side: append (story, viewer, time); count and page through it on demand.
pythonpost_story · 32 lines
# stories/tray.py

"""
The stories tray (illustrative). An active-story index with a 24-hour expiry
per story, a per-viewer seen set with a 48-hour expiry, and a ranked tray
with unseen accounts first. Nothing is deleted at hour 24: entries expire.
"""

import time

STORY_TTL_S = 24 * 3600
SEEN_TTL_S = 48 * 3600


def post_story(author: int, story_id: int, audience, index, archive) -> None:
    index.add(author, story_id, audience=audience, expires_at=time.time() + STORY_TTL_S)
    archive.add(author, story_id)                     # the author's archive keeps it after expiry


def tray(viewer: int, graph, index, seen, ranker) -> list:
    now = time.time()
    active = index.active_for(graph.following(viewer), now)          # {author: [story ids]}
    visible = {a: [s for s in ids if index.audience_allows(s, viewer)] for a, ids in active.items()}
    visible = {a: ids for a, ids in visible.items() if ids}
    seen_ids = seen.get(viewer)
    unseen = [a for a, ids in visible.items() if ids[-1] not in seen_ids]
    watched = [a for a in visible if a not in unseen]
    return ranker.order(viewer, unseen) + ranker.order(viewer, watched)


def mark_seen(viewer: int, story_ids: list, seen) -> None:
    seen.add_many(viewer, story_ids, ttl=SEEN_TTL_S)  # idempotent set adds, batched by the app
Quick check4 questions · +5 XP each

Ranking and stories

  1. Q1/4 Why rank the feed with a light model first and a heavy model second?

    Answer & explanation

    Answer: B

    Hundreds of heavy predictions per page fit the budget; a thousand-plus would not. The funnel spends compute where it changes the order.

  2. Q2/4 Feed ranking predicts five interactions per post. How do they become one order?

    Answer & explanation

    Answer: C

    One value per post makes the predictions comparable; experiments decide how much a comment is worth against a like.

  3. Q3/4 What makes a story leave every tray at hour 24?

    Answer & explanation

    Answer: B

    The tray is built from the index on every open, so an expiring entry removes the story everywhere at once.

  4. Q4/4 A seen-mark write is lost. What is the consequence?

    Answer & explanation

    Answer: A

    Seen state is a convenience that can be slightly wrong; it is written cheaply, in batches, with an expiry.

Phase 5 · Stage 5 of 7 · 5 min budget · 2 min read

Phase 5: Scaling and Edge Cases (5 minutes)#

Interviewer

"Walk me through one post's life, and then the cases that bite."

A Post's First Day#

The baby photo, from 6 pm (the 24-hour story rule and the archive from Instagram; the rest illustrative)
  • 6:00 PM · Posted
    • Original and four sizes stored; post written to the cousin's shard; fan-out to 420 inboxes finishes in under a second
  • 6:00 PM · Story too
    • The same photo as a story: an index entry with a 24-hour expiry; it rings the cousin's circle in 420 trays
  • 9:00 PM · Meera opens the app
    • The post is in her inbox; ranking puts it first; the 1,080-pixel copy comes from the nearest CDN edge
  • Next day, 6:00 PM · Story expires
    • The index entry lapses; the story leaves every tray and stays in the cousin's archive
  • Three weeks later · Warm storage
    • The photo's bytes move to erasure-coded storage; the key and URL don't change

Edge Cases#

Edge cases
Edge case 1: The 400-million-follower account posts during a final

Problem: The account posts a goal celebration and 50 million followers open the app in ten minutes.

Solution: No fan-out: its post is in the huge-accounts cache, merged into each reader's candidates. The media is one key, so the CDN serves it from cache after the first few requests per edge; pre-warm the edges as soon as the post is created.

Edge case 2: Delete a post that's already in millions of inboxes

Problem: The author deletes a post seconds after it fanned out.

Solution: Mark it deleted on its shard; every feed read hydrates posts and drops deleted ones, so it vanishes at once. Inbox entries are cleaned up lazily; media is purged from storage and the CDN afterwards.

Edge case 3: A new user follows nobody

Problem: Their inbox is empty.

Solution: Fill the feed with suggested posts from retrieval over popular and nearby content (the Explore-style funnel), and suggest accounts to follow; switch to the inbox as follows accumulate.

Edge case 4: A private account removes a follower

Problem: A removed follower must stop seeing posts and stories immediately.

Solution: The follow edge is deleted, and every read checks the viewer against the author's audience at hydrate time, so nothing already in an inbox or the story index leaks.

Quick check3 questions · +5 XP each

Scaling and edge cases

  1. Q1/3 A post is deleted seconds after fanning out to a million inboxes. How does it disappear from feeds at once?

    Answer & explanation

    Answer: B

    Read-time filtering makes deletes instant; inbox and media cleanup can follow lazily.

  2. Q2/3 Why pre-warm CDN edges when a huge account creates a post?

    Answer & explanation

    Answer: C

    Warming turns the first wave of requests into cache hits instead of a stampede on storage (Week 4's thundering herd).

  3. Q3/3 A private account removes a follower. What stops that follower seeing posts already in their inbox?

    Answer & explanation

    Answer: D

    Permission is checked on every read, so a stale inbox can never show a post to someone no longer allowed.

Phase 6 · Stage 6 of 7 · 1 min

Phase 6: Monitoring and Operations#

You

"The dashboard asks: are feeds fast and full, is ranking healthy, are uploads getting in, and is media arriving quickly?"

Monitoring Dashboard#

Feed and stories dashboard — 21:05 IST (illustrative)
Feed
Metric Now
Feed pages 684,000/s
Feed latency p50 / p99 142 / 431 ms
Pages served from first-stage fallback 0.3%
Pages with fewer than 10 posts 0.4%
Ranking
  • Heavy model p99: 58 ms per batch; feature cache hit rate 99.2%
  • Fan-out queue: 1.4 s oldest job; huge-accounts cache: 2,900 accounts
Stories
  • Tray latency p99: 246 ms; seen-marks 331,000/s
  • Story index entries expiring per second: 11,800
Media
  • Uploads completed: 2,140 posts/s, 21,900 story frames/s; upload failure rate 0.4%
  • CDN hit rate 96.8%; first image p75 on mobile 610 ms

SLOs for the Platform#

SLOs for feed, stories and media
SLO 1: Fast, full feeds
  • Target: first page p99 < 500 ms; 99.5% of pages with a full set of posts
SLO 2: Posts reach followers
  • Target: 99% of posts from normal accounts in followers' inboxes within 10 s
SLO 3: Stories that behave
  • Target: tray p99 < 300 ms; no story visible in a tray after its 24 hours
SLO 4: Media never lost, quick to load
  • Target: 0 posted originals lost; first image under 1 s at p75 on mobile
Quick check3 questions · +5 XP each

Reading the dashboard

  1. Q1/3 0.3% of pages are served from the first-stage fallback. When should that worry you?

    Answer & explanation

    Answer: C

    The fallback keeps feeds full when the heavy stage is unhealthy; a rising share is an early alarm on ranking.

  2. Q2/3 The fan-out queue's oldest job grows from 1.4 s to 9 minutes. What do users notice?

    Answer & explanation

    Answer: A

    Reads still work from existing inboxes; only delivery of new posts is delayed.

  3. Q3/3 The CDN hit rate drops from 96.8% to 80%. What happens to storage?

    Answer & explanation

    Answer: C

    Misses go from 3.2% to 20% of 7 million fetches a second: about six times more load on storage.

Final challenge · Stage 7 of 7 · 2 min

Final Challenge: Midnight on New Year's Eve#

The interviewer glances at the calendar. "One more."

Interviewer

"Midnight on New Year's Eve in India. In the ten minutes after twelve, story posting jumps to ten times normal in one time zone, everyone opens the app to watch their friends' fireworks, and a cricketer with 270 million followers (assume that) posts a story too. Keep it working and tell me what you'd degrade."

The midnight brief
  • Normal story frames in India's evening: 4,000 a second (assumption); at midnight 10×
  • Story frames: 70% photos at 1 MB, 30% videos at 8 MB
  • Tray opens: 400,000 a second for ten minutes (assumption)
  • Each tray open triggers 3 prefetched first frames (~200 KB each)
  • Seen-marks: 8 per tray open in that window
Your turn · checkpoint 7not answered

What do you protect, and what do you degrade?

Work out the upload rate and bytes, the tray load, the seen-mark writes and the prefetch egress. Then decide what to slow down.

Think it through, or jot it down. Then compare.

One strong answer

  • Uploads: 4,000 × 10 = 40,000 story frames a second; bytes: 40,000 × (0.7 × 1 MB + 0.3 × 8 MB) = 40,000 × 3.1 MB = 124 GB/s (~1 Tbps) of ingest into the media service near India.
  • Video encoding: 12,000 videos a second; produce only the minimum-functionality versions now (Meta's repackaging trick keeps that cheap), and queue advanced encodings for after 1 am.
  • Tray: 400,000 trays a second, each an index-and-seen-set intersection; serve them from caches with the 24-hour index already in memory.
  • Seen-marks: 400,000 × 8 = 3.2 million writes a second, ten times the normal peak. The app already batches them; stretch the batch window from a few seconds to 30 s. A missed mark only makes a story look unseen again.
  • Prefetch: 400,000 × 3 × 200 KB = 240 GB/s (~1.9 Tbps) from CDN edges, mostly cache hits since friends watch each other's stories.
  • The cricketer's story: one entry in the index with 270 million potential viewers. No fan-out happens for stories, so it's just one hot key on the index and one hot media key on the CDN: replicate the index entry across cache nodes and pre-warm the edges.
  • Degrade in this order: advanced video encodings, then the heavy ranking stage for trays (use first-stage order), then the seen-mark batch window, then prefetch depth (3 frames down to 1). Never acknowledged uploads, the 24-hour expiry, or audience checks on close-friends stories.
Quick check4 questions · +5 XP each

Final challenge · Quest check

  1. Q1/4 40,000 story frames a second, 70% at 1 MB and 30% at 8 MB. Roughly what ingest bandwidth is that?

    Answer & explanation

    Answer: B

    The average frame is 0.7 × 1 + 0.3 × 8 = 3.1 MB; × 40,000 = 124 GB/s, about a terabit a second.

  2. Q2/4 Seen-marks jump to 3.2 million writes a second. What is the safe way to absorb them?

    Answer & explanation

    Answer: B

    Seen state tolerates a little loss and delay, so batching harder is cheap and safe.

  3. Q3/4 The cricketer with 270 million followers posts a story. What load does that create?

    Answer & explanation

    Answer: C

    Stories aren't fanned out; the tray reads the index. Replicate the hot entry and pre-warm the CDN.

  4. Q4/4 Which should be degraded first at midnight?

    Answer & explanation

    Answer: A

    Advanced encodings improve quality later; privacy, expiry and durability are never shed.

Wrap-up · 4 min

Interview Conclusion#

Transcript
Interviewer

"Why not just show the feed in time order?"

You

"Because people don't see most of it. Instagram said in 2016 that people missed about 70% of their feeds. Ranking spends a few hundred milliseconds of compute to put the posts someone cares about inside the twenty they'll actually see."

Interviewer

"Why keep fan-out at all, if ranking reorders everything anyway?"

You

"Fan-out builds the candidate list once per post instead of once per read. With reads 300 times more frequent than posts, that's the cheap side to do the work, except for the handful of huge accounts, where pulling at read time is cheaper."

Interviewer

"The single most important decision?"

You

"Separate the bytes from the IDs. Media is stored once, by content, and served from caches; posts, inboxes and stories move small IDs that sort by time. That lets each part scale its own way, and keeps ranking, the expensive part, working on IDs and features rather than megabytes."

Summary: Concepts Applied from 10-Week Course#

Concepts from the 10-week course in feed and stories
  • Week 1: Data at scale
    • Partitioning — logical shards inside 64-bit IDs; posts sharded by author
    • Hot keys — huge accounts pulled at read time; hot story entries replicated
  • Week 3: Messaging and async processing
    • Fan-out as background jobs, idempotent and batched
  • Week 4: Caching
    • Feed caching — bounded inboxes; multi-tier caching — CDN, hot and warm storage, content-addressed keys
  • Week 7: Search systems
    • Retrieval then staged ranking; a value model over several predictions
  • Week 8: Analytics pipelines
    • Interaction events feeding ranking features and seen state
  • Week 9: Multi-tenancy and security
    • Audience checks at read time; deletes as tombstones, then purges

Why Instagram Matters#

Why this is a different kind of hard problem
The feed is a decision, not a query
  • A chronological feed is a database read; a ranked one is a prediction for every post, for every person, every time they open the app
Bytes and IDs scale differently
  • Petabytes of media a day need caches, tiers and formats; billions of feed reads need small, sortable IDs and cheap merges
Ephemeral is a read-side idea
  • Stories disappear from the tray by letting an index entry expire, while the media lives on in an archive: "gone" is about what the reader is shown

"Store each photo once, move only IDs, and spend the compute on choosing which twenty to show."

Self-Assessment Checklist#

After studying this case study, you should be able to:

Media:

Media0/3 ready
  • Store media once by content address and serve derived sizes through a CDN
  • Explain hot and warm tiers and why expired stories still cost storage
  • Describe minimum-functionality versus advanced video encodings and where compute goes

Feed:

Feed0/3 ready
  • Lay out time-sortable 64-bit IDs with a shard inside them
  • Design hybrid fan-out with bounded, idempotent inboxes
  • Build a staged ranking funnel and turn several predictions into one value

Stories and operations:

Stories and operations0/2 ready
  • Build a tray from an expiring index and a seen set, with audience checks
  • Estimate a midnight spike and choose what to degrade

Sources#

Instagram and Meta:

Engineering posts:

Research:

Design note: the architecture, code, thresholds, rates, sizes and dashboards here are an illustrative design for the problem, not a description of Instagram's internal systems beyond what Instagram and Meta have published.

Further Reading#

Related systems to study:

  • Read

    Twitter / X (Bonus Problem 10 in this series): fan-out on write versus read, and the celebrity merge, in depth

  • Talk

    YouTube (Bonus Problem 19): an upload pipeline, view counting and two-stage recommendations

  • Talk

    Netflix (Bonus Problem 6): delivering video from caches near viewers

  • Read

    WhatsApp (Bonus Problem 2): media sharing and delivery at messaging scale

Foundational concepts (this course):

  • Read

    Week 4, Day 4 — Feed Caching

  • Read

    Week 4, Day 5 — Multi-Tier Caching

  • Read

    Week 1, Day 1 — Partitioning Deep-Dive

  • Read

    Week 1, Day 4 — Hot Keys and Skew

  • Read

    Week 7, Day 3 — Query Processing and Relevance

  • Read

    Week 9, Day 4 — Right to Deletion

"Every photo is stored once; every feed is a choice of twenty out of thousands, made again each time you open the app."

End of Side quest 21 · Instagram Feed and Stories
Clear the questFinish Instagram.Stages clear as you read them. Marking complete unlocks after ~6 more min of reading.
  • 0/7stages cleared
design sheet · 6 diagrams · pdfRevise the whole design in ten minutesThe numbers, architecture, sequence, class and state diagrams, decisions and failure modes of this quest on a few printable pages.open the sheet →

New lessons by email

New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.

Discussion · …

Public: everyone reading this lesson sees comments. Be kind and keep it on the material.

Sign in to ask a question or answer one. Reading stays open to everyone.
loading the discussion…