System design deep dive · HLD
Design Netflix: full high-level design.
Requirements, back-of-envelope capacity estimation, a high-level design diagram, a deeper deployment architecture diagram, sequence diagrams for the transcoding pipeline and adaptive-bitrate playback, and an entity-relationship diagram for videos, encodings, watch history, and subscriptions - with the reasoning an interviewer expects behind every box and arrow.
1. Clarify requirements before drawing any box
Netflix is really two systems glued together: a batch/offline ingest-and-encode pipeline, and an online, latency-critical playback path. Separating those two concerns up front is the single most important framing move in this interview.
Functional requirements
Non-functional requirements
2. Back-of-the-envelope capacity estimation
These numbers decide the shape of everything downstream: how much encoding compute is needed, how aggressively the CDN must cache, and how the watch-history write path must be batched to survive peak concurrency.
| Metric | Assumption | Resulting estimate |
|---|---|---|
| Peak concurrent streams | 260M subscribers, ~15% actively streaming at prime-time peak | ~39M concurrent streams during the evening peak window |
| Peak CDN egress bandwidth | ~3.5 Mbps average bitrate per stream (mixed SD/HD/4K) | 39M × 3.5 Mbps ≈ 137 Tbps - only survivable because 99%+ is served from edge caches embedded in ISPs, not a central origin |
| New titles ingested | ~500 new titles/day across originals and licensed content | 500/day × ~15 renditions × 3 codecs (H.264/HEVC/AV1) ≈ 22,500 encode jobs/day |
| Encode storage growth | ~150 GB average across the full rendition ladder per title | 500 × 150GB ≈ 75 TB/day of new encoded assets, before origin replication |
| Watch-history writes | 39M concurrent streams, one progress heartbeat every 30s | ~1.3M heartbeat writes/sec - batched and buffered, never written synchronously per-second per stream |
3. High-level design (HLD)
The HLD splits cleanly into the ingest/encode pipeline (offline, write-heavy, cost-sensitive) and the playback path (online, read-heavy, latency-sensitive), connected only through the origin store and the catalog metadata service.
What each box owns
Ingest service & transcoding fleet
The ingest service validates the studio master, splits it into chunks so a two-hour film can be encoded in parallel rather than as one long serial job, and enqueues one encode job per (rendition, codec, chunk) tuple. The transcoding fleet is a large pool of stateless, horizontally scalable workers - this workload is embarrassingly parallel and tolerant of minutes of latency, which is exactly why it runs on elastic batch/spot compute instead of always-on capacity.
QC, packaging, and the origin store
After encoding, an automated (and sometimes manual) QC pass checks for artifacts, sync issues, and missing subtitle tracks before a title is marked publishable. Packaging attaches DRM license metadata and multiplexes subtitle/audio tracks. The origin store is the durable, versioned source of truth for every rendition - it is written to once per title and read from constantly by the CDN, never directly by end-user devices at scale.
Playback API & CDN edge (Open Connect)
The Playback API authenticates the session, checks subscription tier and regional licensing, and returns a signed manifest listing available renditions. The actual video bytes are then requested from the nearest CDN edge node - Netflix's Open Connect appliances are physically embedded inside ISP networks specifically so the vast majority of bytes never cross the public internet backbone at all.
Watch history service & event stream
The player emits lightweight progress heartbeats (not per-second, batched every ~30s) to an event stream; a dedicated watch-history service consumes that stream and updates the "continue watching" position per profile. This is deliberately asynchronous and decoupled from the playback path - a slow watch-history write must never cause a rebuffer.
4. Detailed architecture diagram
The architecture diagram takes every HLD box and answers "how is this actually deployed at Netflix's scale?" - control-plane regions, the encoding farm's elasticity, and exactly how the CDN edge is embedded in ISP networks.
| Decision | Choice | Reasoning |
|---|---|---|
| CDN strategy | Own-built edge (Open Connect) embedded in ISPs | At this egress volume, paying third-party CDN transit rates is uneconomical; owning appliances placed inside ISP networks avoids most public-internet transit entirely. |
| Encoding compute | Elastic batch/spot fleet, scales to near-zero off-peak | Transcoding is bursty (new releases) and parallelizable; paying for always-on dedicated capacity would waste money most of the day. |
| Watch history storage | Wide-column store (Cassandra), partitioned by profile_id | Access pattern is almost always "get/update this profile's recent history" - a partition-friendly key beats a relational join-heavy schema at this write volume. |
| Content pre-positioning | Push popular titles to edge caches ahead of release, not reactively | Waiting for organic cache misses on a major release would overwhelm origin bandwidth in the first hour; predictable releases are proactively warmed. |
5. Sequence diagrams for the two critical flows
One flow is entirely offline and tolerant of latency (encoding); the other is online and cannot tolerate more than milliseconds of added delay (playback). The diagrams below show exactly where that boundary sits.
5.1 Video upload & transcoding pipeline
Step 3 fans a single upload out into thousands of independent, parallelizable encode jobs - this is what turns a two-hour film into an encoding job that finishes in minutes rather than hours. Step 6 is drawn as a synchronous ack only for job-completion bookkeeping; the actual encoded bytes in step 5 go straight to the origin store, not back through the queue, so large binary payloads never round-trip through the job broker.
5.2 Playback with adaptive bitrate streaming
Steps 4-6 repeat continuously for the whole runtime of the title, entirely between the player and the CDN edge - the Playback API is consulted once per session, not once per segment, which is exactly why it can stay stateless and cheap relative to the CDN's byte volume. Step 6 is the client-side ABR heuristic (buffer occupancy and measured throughput) deciding to step down a rendition before the buffer empties; step 7 is fire-and-forget so a slow watch-history write can never cause a stall.
6. Entity-relationship (ER) diagram and schema
The data model has to answer: how many renditions does one video own, how is per-profile resume position looked up instantly, and how does a subscription gate which titles and which bitrate ceiling a profile is entitled to.
Key modeling decisions
| Storage choice | Use when | Watch out for |
|---|---|---|
| Relational (Postgres/MySQL) for videos, encodings, subscriptions | Catalog and billing data is comparatively low-volume and benefits from strong consistency and joins (a video's full rendition list, a plan's entitlements). | Not designed for the extreme write-fan-out of playback telemetry. |
| Wide-column (Cassandra/DynamoDB) for watch_history | Access pattern is always "read/write this profile's rows" at massive concurrent write volume. | Cross-profile analytics queries (e.g. "most-resumed titles") need a separate batch/OLAP pipeline, not ad hoc queries on this store. |
7. Deep dives interviewers actually probe
How does client-side adaptive bitrate (ABR) selection actually work?
The player continuously measures two signals: current buffer occupancy (how many seconds of video are already downloaded and waiting) and recent segment download throughput. A buffer-based ABR algorithm steps down a rendition well before the buffer empties (avoiding a stall), and steps up only after sustaining healthy buffer levels for several segments (avoiding oscillation). This logic lives entirely on the client - the server just exposes the rendition ladder in the manifest and never dictates bitrate.
Per-title encoding ladder vs a fixed ladder for every video - is it worth the complexity?
A fixed ladder (e.g. always encode 240p/480p/720p/1080p/4K at fixed bitrates) is simple but wastes bits on visually simple content (animation, talking-head interviews) and under-serves visually complex content (fast action, film grain) at the same bitrate. Per-title encoding analyzes each source's complexity and picks a custom bitrate/resolution ladder, typically cutting average bitrate 20-50% at equal perceived quality - worth it given the CDN egress cost at Netflix's scale, but it roughly doubles encoding pipeline complexity and compute cost per title.
How is "continue watching" kept consistent across devices without conflicts?
Each device sends periodic heartbeats with its own local playback position; the watch-history service applies a last-write-wins update keyed by (profile_id, video_id) using the heartbeat's server-received timestamp, not client clock time (client clocks drift and can't be trusted for ordering). A user rarely watches the same title simultaneously on two devices, so last-write-wins is an acceptable simplification rather than needing full conflict resolution.
How does the CDN decide what to cache before anyone has watched it yet?
Popular and newly-released titles are proactively pushed ("pre-positioned") to Open Connect appliances during off-peak hours ahead of their release window, based on regional popularity predictions - waiting for organic cache misses on a hit show's midnight release would otherwise create a massive, predictable origin traffic spike in the first hour. Long-tail catalog titles rely on standard reactive LRU caching instead, since pre-positioning every title everywhere isn't economical.
What is the single biggest bottleneck as this scales 10x?
Not playback bandwidth - CDN edge capacity scales roughly linearly by adding more appliances inside more ISPs. The real bottleneck becomes encoding pipeline throughput during a burst of same-day releases (e.g. a major franchise premiere plus several new originals landing at once): thousands of parallel encode jobs competing for the same elastic compute pool can delay QC and publishing. The fix is prioritized job scheduling (release-date-aware queues) rather than simply adding more workers, since spot capacity itself has regional limits.
Post a Comment
Add