The Blueprint
Social & feeds advanced

Design TikTok

Infinite short-video scroll on a ranked feed where the model, not the graph, decides what shows next — precompute is the whole trick.

asked at TikTok · Meta · YouTube Frequently asked ~40 min walkthrough ✎ Practice on canvas →
01

Scope the requirements

Draw the seams first: video ingest is one problem, feed ranking is another, and both share a CDN. The interview trap is treating this as Instagram-with-video — the feed here isn't graph-driven.

Functional — what it must do

  • Upload short video (≤3 min); server transcodes to ABR ladder.
  • Home feed: infinite scroll of ranked videos, not chronological.
  • Like/comment/share, follow creators, per-video watch analytics.
  • Search by hashtag, sound, creator.
  • Explicitly cut: livestream, DMs, creator payouts — offer them back.

Non-functional — the numbers that shape the design

  • Feed latency p95 under 200 ms for the next batch of videos.
  • 1B DAU, ~50B video views/day, ~150M uploads/day.
  • Ranking model runs offline; online serving under 20 ms per candidate.
  • Video prewarmed — the next 5 clips are always on the client before user swipes.
  • 99.99% on feed reads; upload can degrade briefly.
Say this out loud

The feed is a recommendation problem, not a fan-out problem. Follows are a signal to the model, not a filter on the query.

02

Back-of-envelope estimates

Two big numbers drive the design: the QPS on the feed (huge) and the storage on video variants (huge). Everything else is small.

QuantityEstimateHow you got there
Feed requests/s~600k/s1B DAU × ~50 feed refreshes ÷ 86,400. Every scroll is one call.
Uploads/s~1,700/s150M/day ÷ 86,400. Peak 3-5×.
Storage/day~10 PB150M × ~30 MB across 6 ABR rungs including previews.
Candidate pool per user~500prewarmed daily by offline recall; online reranker picks top 20 to serve next.
Model inference~10 ms per 20 itemsbatched GBM/DNN on GPU serving 1M QPS.
CDN egress~300 Tbps peak50B views × ~5 MB avg per playback ÷ seconds in a day.
Say this out loud

The catalogue is huge and the feed is dense — CDN dominates cost, and the ranking pipeline dominates complexity.

03

Sketch the API

Feed pull is one endpoint that returns a manifest of the next N videos. Upload is a signed URL that keeps bytes off the app tier.

GET/api/v1/feed?after=→ [{ videoId, manifestUrl, meta }] × N. Cursor keeps stateless.
POST/api/v1/uploads/init→ { uploadId, presignedS3Url }. Chunks land in S3 directly.
POST/api/v1/uploads/{id}/completeTriggers transcode DAG + moderation.
POST/api/v1/videos/{id}/actionbody: { type: like/share/skip, watchMs }. Feeds ranking signals.
GET/manifest/{videoId}→ HLS/DASH manifest served by CDN.
  • Feed returns manifest URLs, not video bytes. Client fetches media direct from CDN using signed manifest.
  • Every action is an event on Kafka; the ranking model consumes them nightly + online counters flow into a features store.
04

Data model

Three big data stores: video metadata (small), feed candidate pools (per-user, precomputed), user events (huge stream).

videos
id PK · creator_id · duration · encodings[] · category · lang
Cassandra keyed by video_id. Read-mostly.
user_candidates
user_id → [videoId × ~500]
Redis. Written nightly by offline recall + hourly trending patch.
events
(user_id, ts) · videoId · action · watch_ms
Kafka + S3 archive. Signal source for training + online counters.
counters
video_id → { views, likes, shares, dwell }
Redis with periodic writeback to Cassandra. Feeds ranker + card badges.
The database call

Cassandra for videos and history — append-heavy, partition by id. Redis for the candidate pool because feed serving cannot afford disk seeks. Feature store (Redis or purpose-built) holds pre-computed embeddings for the reranker.

05

High-level design

Feed serving is a two-stage funnel: pull ~500 candidates from Redis, rerank top ~20 in a model, sign manifests, return. Upload is a separate DAG that produces the manifests.

client edge / cdn service data offline App CDN edge API gateway Feed svc recall + rank Upload svc Action ingest Candidates Redis Videos meta Counters S3 media Reranker GPU inference Nightly recall Transcode DAG pull 500 meta rerank 20 media presign GET feed
feed is a two-step pull: precomputed candidates + online rerank. media flows via cdn only.
  1. Client opens app → GET /feed → feed service reads the user's candidate list from Redis (~500 videoIds).
  2. Reranker service scores top ~50 candidates against fresh features (recent watch, session context) and picks 20.
  3. Feed service fetches video metadata for those 20, signs manifest URLs, returns to client.
  4. Client prewarms the next 5 manifests + segments in background so the swipe is instant.
  5. Every user action posts to /action → Kafka → counters update in Redis. Nightly recall job aggregates the day of events into fresh candidate pools for tomorrow.
06

Deep dives — where the interview is won

Phase 01

Recall + rank: the two-stage funnel that keeps latency flat

One-shot ranking over the whole catalogue is impossible — you cannot score 100M videos in 20 ms. The pattern used by every large-scale recommendation system: recall narrows to a candidate pool of ~500-1000 items using cheap heuristics (embedding ANN, trending, follow signal, geo/lang match); a heavier reranker then scores just those candidates with a slow, accurate model.

Recall runs offline (per-user) or online (given a request context). The reranker runs online but only ever sees a bounded input, so latency is deterministic. This split is why the design has two model pipelines with wildly different SLAs — the interviewer scores you on drawing that line.

user recall rerank feed Request ANN + trending 500 items DNN reranker 20 items Feed page narrow serve
recall is broad + cheap; rerank is narrow + accurate. costs scale on candidate count, not catalogue size.
Trade-offRecall bias becomes hard to fix — anything that never made it into the pool cannot be ranked. Periodic exploration slots (random inserts) keep the model from ossifying around known winners.
Phase 02

Prewarm the next five — the whole product hinges on it

TikTok's swipe feels instant because the next few clips are already on the device. The mechanic: when the client renders video N, it fetches manifests + first segments of N+1..N+5 in the background. Bandwidth is spent optimistically because a swipe is far more likely than a stop.

Server-side, the feed endpoint returns not just the current video but a manifest set for the next batch — one round trip covers multiple clips. If the user watches to completion, we already know it; if they skip in 2 seconds, that's a strong negative signal for the model.

client network cdn Player video N Prewarmer N+1..N+5 CDN edge next 5 first segs
prewarm cost is bandwidth. the return is a swipe that renders in <50 ms.
Trade-offAggressive prewarm wastes CDN bandwidth on abandoned sessions and mobile data on users. Cap at 5 items and don't prewarm the full ladder — first segment at low bitrate is enough for a smooth start.
Phase 03

Counters that keep up with a viral moment

A single video going viral hits ~10k views/s in a hot window. Naively incrementing a Postgres row is a hot-key nightmare. The design: Redis INCR per (video_id, minute) with a batched writer that folds minute buckets into daily totals in Cassandra every few minutes. The reranker reads Redis for freshness; card badges read Cassandra for durability.

The subtle move: the ranker doesn't need exact counts. Approximate counters (HyperLogLog for uniques, top-K sketches) work fine and shard trivially. Save exact counting for user-visible numbers where a mismatch triggers a support ticket.

actions hot store durable Action events Redis INCR per minute Batched writer Cassandra totals atomic flush 1 min
hot writes stay in redis. durable writes are periodic batches. never write per-event to cassandra.
Trade-offA redis outage loses the last minute of counter increments. Acceptable for a scoring signal; the durable totals still recover from the next flush window.
07

Follow-ups you should expect

Why not chronological feed like Instagram used to be?
Follows are a weak signal at 1B DAU — users don't follow enough to fill the feed. A ranked model gives every user a personalised infinite feed after zero follows, which is why TikTok won cold-start. Chronology becomes optional in a settings toggle.
How do you handle a video with 100M views/day?
CDN absorbs almost all playback. Counters go through per-minute Redis buckets. Metadata is small and cached. The ranker doesn't treat mega-hits specially; sample them at fixed rates in training to avoid over-weighting.
Cold-start for a new user?
First few sessions use non-personalised candidates — trending in region + language, exploration mix. As actions accumulate (~30 in the first session), the model has enough signal to personalise. Watch-through rate is the fastest-converging feature.
Moderation for user-generated video at 1.7k/s?
Layered: hash-match against known-bad content on upload (milliseconds), automated vision + audio models flag risky content pre-publish (seconds to minutes), human review for edge cases. Nothing hits the feed before hash-match completes; heavier checks happen while the video is soft-published to a small audience.
What breaks first as you scale from 100M to 1B DAU?
CDN egress. The bytes are the biggest line item and scale linearly with viewers × dwell time. Answer: aggressive edge caching (each region caches its trending set), tiered CDN with ISP peering, and simulcast ladder tuned per region to control bitrate.

Where candidates lose the room

  • Treating the feed as fan-out over a follow graph — you rebuild Instagram and inherit the celebrity problem for no benefit.
  • Running the reranker over the whole catalogue instead of a candidate pool — latency budget blown by 100×.
  • Hitting the database for view counts on the play path — hot-row lockups on any viral video.
  • Skipping the prewarm story — the swipe feels laggy without it, and the whole product idea evaporates.
  • Ignoring moderation — a design that publishes user content in real time without a hash-match step ships risk you cannot walk back.

Concepts this leans on

Found a bug or want more?

Tell me what's broken, missing, or wrong. Every message lands in my inbox.