Back to System Design
System Design
hard
mid

How would you design a video streaming service like Netflix?

HLS/DASH adaptive bitrate streaming via a player (Shaka/Video.js) sourced from a CDN; manifest + segmented chunks; ABR algorithm picks quality based on bandwidth + buffer; preload posters and metadata; SSR/SSG the catalog pages for SEO and fast LCP; per-row carousels; auth-gated playback; accessibility (captions, focus, keyboard).

5 min read·~30 min to think through

Netflix-style video streaming is an architecture question with two halves: the catalog/browse UX, and the player/streaming pipeline.

1. Catalog & browse

  • SSR/SSG the home/browse pages for SEO and a fast first paint (large hero, lazy-loaded rows).
  • Per-row carousels — horizontal scroll with prefetch on hover, virtualized if rows are long.
  • Edge caching at the CDN for the catalog API (with personalization handled via a thin per-user merge or client-side reordering).
  • Image-heavy — responsive posters with srcset, blurred placeholders to mask LCP.

2. Player — adaptive bitrate (HLS / DASH)

You do not stream a single MP4. Video is chunked (~2–10s segments) at multiple bitrates, described by a manifest (.m3u8 for HLS, .mpd for DASH).

  • The player downloads chunks; the ABR (adaptive bitrate) algorithm picks the next chunk's quality based on measured bandwidth and current buffer level.
  • CDN-served chunks for low latency globally.
  • DRM (Widevine, FairPlay, PlayReady) for protected content.

In the browser, use Shaka Player or Video.js / hls.js — building this yourself is a years-long project.

html
<video id="player" controls></video>
<script>
  const player = new shaka.Player(document.getElementById("player"));
  await player.load("/manifests/title-1234.mpd");
</script>

3. Buffering UX

  • Show a loading state when the buffer is low.
  • Pre-buffer ahead a few segments while playing.
  • Pause/resume preserves position; on resume, fetch the next segment.
  • "Continue watching" — persist last position per user.

4. Preloading & prefetching

  • Hover on a title card → fetch the title's metadata + start the preview clip.
  • Posters lazy-loaded below the fold.
  • Next-episode prefetch in the final seconds.

5. State & data

  • Server state via React Query: catalog, rows, search.
  • UI state (player open, captions on) local or via player API.
  • User state (profile, history) auth-scoped.

6. Personalization

  • Rows are personalized server-side; client just renders.
  • Watch progress is written back with a debounce (every 30s + on pause/end).

7. Performance

  • Code-split the player (heavy) from the catalog shell.
  • Defer DRM init until play is pressed.
  • LCP is usually the hero — preload its poster.
  • Memory: free player resources on unload to avoid leaks.

8. Accessibility

  • Captions / subtitles — toggle, language selection.
  • Keyboard: Space play/pause, ←/→ seek, F fullscreen, M mute.
  • Focus management when entering/exiting fullscreen.
  • Audio descriptions for visually impaired users.

9. Resilience

  • Network drop → buffer drains → player shows "Connecting..." and retries.
  • Quality drops automatically as bandwidth falls (ABR).
  • DRM errors surface a clean message, not a stack trace.

Interview framing

"Two halves: catalog and player. Catalog is SSR/SSG for SEO and LCP, horizontal carousels per row, posters lazy-loaded via srcset, edge-cached APIs. The player is HLS or DASH — chunked video at multiple bitrates with a manifest; the player picks quality per chunk based on bandwidth and buffer. We don't build this — Shaka or Video.js. DRM via Widevine/FairPlay/PlayReady. Continue-watching persists last position. Accessibility is a first-class concern: captions, keyboard, focus on fullscreen. Performance: code-split the player, preload the hero poster, lazy everything else."

Follow-up questions

  • What's an adaptive bitrate algorithm doing?
  • Why HLS/DASH instead of an MP4?
  • How would you implement 'continue watching'?
  • What's the LCP for a Netflix-style page and how do you optimize it?

Common mistakes

  • Streaming a single big MP4 with no adaptation.
  • Loading the heavy player on the catalog page upfront.
  • Not lazy-loading posters.
  • Skipping captions / keyboard.

Performance considerations

  • LCP is the hero poster; ABR handles bandwidth adaptation; code-split the player; preload upcoming segments while playing; free player resources on unload.

Edge cases

  • Bandwidth crashes mid-playback — ABR drops quality.
  • DRM license expiry mid-session.
  • Resume after long pause — re-auth and resync position.
  • Multiple tabs playing the same title.

Real-world examples

  • Netflix, YouTube, Disney+, Hulu — all use HLS/DASH + ABR + CDN.

Senior engineer discussion

Seniors don't reinvent the streaming wheel — they pick Shaka/hls.js, separate catalog and player concerns, optimize the LCP poster, and treat DRM and accessibility as core, not 'later'.

Related questions