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.
The feed is a recommendation problem, not a fan-out problem. Follows are a signal to the model, not a filter on the query.
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.
| Quantity | Estimate | How you got there |
|---|---|---|
| Feed requests/s | ~600k/s | 1B DAU × ~50 feed refreshes ÷ 86,400. Every scroll is one call. |
| Uploads/s | ~1,700/s | 150M/day ÷ 86,400. Peak 3-5×. |
| Storage/day | ~10 PB | 150M × ~30 MB across 6 ABR rungs including previews. |
| Candidate pool per user | ~500 | prewarmed daily by offline recall; online reranker picks top 20 to serve next. |
| Model inference | ~10 ms per 20 items | batched GBM/DNN on GPU serving 1M QPS. |
| CDN egress | ~300 Tbps peak | 50B views × ~5 MB avg per playback ÷ seconds in a day. |
The catalogue is huge and the feed is dense — CDN dominates cost, and the ranking pipeline dominates complexity.
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.
- 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.
Data model
Three big data stores: video metadata (small), feed candidate pools (per-user, precomputed), user events (huge stream).
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.
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 opens app → GET /feed → feed service reads the user's candidate list from Redis (~500 videoIds).
- Reranker service scores top ~50 candidates against fresh features (recent watch, session context) and picks 20.
- Feed service fetches video metadata for those 20, signs manifest URLs, returns to client.
- Client prewarms the next 5 manifests + segments in background so the swipe is instant.
- 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.
Deep dives — where the interview is won
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.
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.
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.
Follow-ups you should expect
Why not chronological feed like Instagram used to be?
How do you handle a video with 100M views/day?
Cold-start for a new user?
Moderation for user-generated video at 1.7k/s?
What breaks first as you scale from 100M to 1B DAU?
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.