Skip to the solution
HardSystem Design14 min read

How would you design YouTube?

0 views

The problem

Question presented to candidate: "Design YouTube. Creators upload videos of any length, viewers all over the world watch them on phones, laptops and TVs with fast start and no buffering, and every video shows a view count. Assume hundreds of hours of video uploaded every minute."

What a strong answer should cover:

  • Capacity: upload hours per day, encoded storage for a full bitrate ladder, transcoding compute, and viewer egress in Tbps, which shows delivery bandwidth is the dominant cost
  • The upload path: resumable chunked upload to object storage, then a transcoding pipeline that splits the video into chunks and encodes them in parallel into a bitrate ladder (resolutions and bitrates), packaged as HLS/DASH segments with manifests
  • Delivery through a CDN of immutable segment files, and adaptive bitrate on the player: choosing the rendition per segment from measured throughput and buffer level, with the trade-off between quality, stalls and switching
  • Metadata, search and recommendations as separate services; view counting as an event stream aggregated asynchronously with abuse filtering
  • What breaks at 10x: storage for rarely watched videos, encode cost, CDN egress, popular-video hot spots at the edge

Clarifying questions expected:

  • Video on demand only, or live streaming too?
  • Which devices and maximum resolution? Is there a target start time and rebuffer rate?
  • How quickly must a video be watchable after upload?

Code / implementation expected: Optional โ€” the capacity arithmetic and a player simulation comparing fixed, throughput-based and buffer-based bitrate choice on one bandwidth trace; the example does both.

  • system-design
  • case-study

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read ยท 15 sections

Read the code

Video delivery: capacity math, then fixed, throughput-based and buffer-based bitrate selection on one bandwidth trace
// YouTube-style delivery: capacity math, then four bitrate policies played against one bandwidth trace.
const fmt = (n) => n.toLocaleString("en-US");
const ladder = [["240p", 400], ["360p", 800], ["480p", 1400], ["720p", 2800], ["1080p", 5000]]; // kbps
const sumKbps = ladder.reduce((a, r) => a + r[1], 0);

console.log("== 1. Capacity (assumptions: 500 hours uploaded/min, 1B watch hours/day, 2,500 kbps average stream) ==");
const hoursDay = 500 * 60 * 24;
const kBPerHour = sumKbps * 3600 / 8; // kbit/s x s / 8 = kB
console.log("uploaded " + fmt(hoursDay) + " hours/day; full ladder = " + fmt(sumKbps) + " kbps = " + fmt(kBPerHour) + " kB per video hour");
console.log("new encoded storage " + fmt(Math.floor(hoursDay * kBPerHour / 1_000_000_000)) + " TB/day before replication");
console.log("transcode at 1 core-hour per rung per video hour: " + fmt(hoursDay * ladder.length) + " core-hours/day = " + fmt(hoursDay * ladder.length / 24) + " cores busy");
const concurrent = Math.floor(1_000_000_000 / 24);
console.log("average concurrent viewers " + fmt(concurrent) + " -> egress " + fmt(Math.floor(concurrent * 2500 / 1_000_000_000)) + " Tbps (CDN and edge caches carry this)");
const chunks = 3600 / 10;
console.log("a 1-hour upload split into 10 s chunks = " + chunks + " chunks x " + ladder.length + " rungs = " + fmt(chunks * ladder.length) + " parallel encode tasks");

console.log("\n== 2. Adaptive bitrate: 4 s segments, 20 s max buffer, bandwidth trace in kbps ==");
const SEG = 4000, MAXBUF = 20000;
const trace = [].concat(Array(8).fill(6000), Array(6).fill(2500), Array(6).fill(900), Array(10).fill(4000));
console.log("trace: 8 x 6000, 6 x 2500, 6 x 900, 10 x 4000 (" + trace.length + " segments = " + trace.length * SEG / 1000 + " s of video)");
const policies = {
  "fixed 1080p": () => 4,
  "fixed 360p": () => 1,
  "throughput": (hist) => { // highest rung under 80% of the slowest of the last 3 downloads
    if (hist.length === 0) return 0;
    const est = Math.min(...hist.slice(-3)) * 8 / 10;
    let r = 0; for (let i = 0; i < ladder.length; i++) if (ladder[i][1] <= est) r = i;
    return r;
  },
  "buffer-based": (hist, buf) => { // below 5 s: lowest rung; above 15 s: highest; linear between
    if (buf < 5000) return 0;
    return Math.min(4, Math.floor((buf - 5000) * 5 / 10000));
  },
};
console.log("policy        startup ms  stalls  stall ms  avg kbps  switches  rungs used");
for (const [name, pick] of Object.entries(policies)) {
  let buf = 0, startup = 0, stalls = 0, stallMs = 0, kbpsSum = 0, switches = 0, prev = -1;
  const hist = [], used = new Set();
  for (let i = 0; i < trace.length; i++) {
    const r = pick(hist, buf);
    const dl = Math.floor(ladder[r][1] * SEG / trace[i]); // ms to download this segment
    if (i === 0) startup = dl;
    else if (dl > buf) { stalls++; stallMs += dl - buf; buf = 0; }
    else buf -= dl;
    buf = Math.min(buf + SEG, MAXBUF); // the player pauses fetching when the buffer is full
    hist.push(trace[i]); kbpsSum += ladder[r][1]; used.add(ladder[r][0]);
    if (prev >= 0 && r !== prev) switches++;
    prev = r;
  }
  console.log(name.padEnd(14) + String(startup).padStart(10) + String(stalls).padStart(8) + String(stallMs).padStart(10) +
    String(Math.floor(kbpsSum / trace.length)).padStart(10) + String(switches).padStart(10) + "  " + ladder.filter((l) => used.has(l[0])).map((l) => l[0]).join(","));
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 106 of 106 in System design

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

End of the topic

That was the last System design question.

Pick another topic