InterviewLegend

Design a Video Streaming Service (Netflix)

Signature problem
Alex Chen40 min readUpdated 37 real interview reports
In this lesson
  • Start from the asymmetry: a trickle of catalog writes against a planet-scale read fan-out
  • Walk the three paths: transcode, CDN-fronted playback, and resume-position writes
  • Go deep on the parts that decide the round: adaptive bitrate streaming, chunked transcoding, and syncing watch position across devices

Understanding the problem

The prompt

Design a video streaming service like Netflix. Let a viewer browse and search a catalog of titles, stream any title at a resolution that adapts to their connection, and resume a title from wherever they stopped, even on a different device.

The first move is to separate the two halves of this system, because they could not be more different in size. New titles arrive slowly: a studio or content team adds a curated catalog, a handful of titles a day, not a firehose of user uploads. Playback is the opposite. The same title is streamed by millions of people, each pulling a fresh media chunk every few seconds, from every corner of the world at once. So the write side is small and can take its time, and the read side is enormous and has to feel instant. State that split in the first two minutes and the rest of the design falls out of it. That asymmetry is the design.

That framing matters because "Design Netflix" is often confused with "Design YouTube." They are not the same problem. YouTube ingests hundreds of hours of video a minute from anyone, so its hard part is the write path: a transcode fleet keeping up with an unbounded upload stream. Netflix's catalog is curated and comparatively tiny. A new title can spend hours in an encoding pipeline and nobody is waiting on it. The hard part here lives on the read side. It is getting a smooth, high-quality picture to a viewer on an unreliable connection, and doing it cheaply at global scale.

The HubSpot angle
HubSpot asks this one to see how you reason about a read-heavy delivery system, not because you will build Netflix there. The rubric is the same instinct that runs through their take-home and the weather-module round: right-size the design, name the read-versus-write asymmetry out loud, and justify each storage and caching choice in plain terms. Buzzword bingo backfires. Plain reasoning wins. The interviewer is described as collaborative and quick to give hints, so expect them to steer you toward one component and ask you to go deep, rather than grade breadth for its own sake. You will sketch on a whiteboard tool such as Excalidraw, so rehearse narrating the flow in simple boxes. Depth scopes with level: a Senior I loop is often one design round, and a Senior II loop often runs two rounds or a round plus a written document.

Functional requirements

Keep the scope to the three things the prompt actually names, and let the interviewer widen it. Browse and search the catalog, stream a title with the picture adapting to the connection, and resume from the last watched position across devices. Everything else is below the line until asked. Keep it tight.

In scope

  • Browse and search the catalog of titles by name, genre, and metadata
  • Stream a title with adaptive multi-resolution playback that tracks the viewer's bandwidth
  • Ingest and transcode a new title into the renditions playback needs
  • Save and resume watch position for a user, consistent across their devices

Below the line (name it, then skip)

  • Personalized recommendations and home-row ranking (a large ML system of its own)
  • Billing, subscriptions, and account management
  • Live streaming and real-time events (this is video-on-demand)
  • Social features: reviews, comments, sharing, watch parties
  • The full DRM licensing flow (name it as a read-path gate, do not design it)

Non-functional requirements

Put a target on each one, because for a streaming service the non-functional side is most of the design. Startup latency comes first. When a viewer presses play, the first frame should appear in roughly two seconds. Longer than that and people abandon. That single number pushes work toward the edge and shapes how playback begins. Plant it early.

Playback smoothness is the requirement people forget to state. Once a title is playing it must not stall to rebuffer, even as the viewer's bandwidth swings, moves onto a train, or shares a home connection. We cannot control the network, so the design has to absorb its variability rather than assume it away. That is the whole reason adaptive bitrate exists, and it is the first deep dive.

Availability leans almost entirely on reads. Browsing and playback have to work globally around the clock, so the read path is replicated and fronted by a CDN with no single origin in the request path. The write path is far more forgiving. If the transcode pipeline is down for an hour, no viewer notices, because they stream titles that were encoded days ago. Consistency is relaxed in the one place it appears, resume position, where a few seconds of staleness between devices is invisible to a human. Durability matters for the master files and the catalog. It barely matters for a cache we can refill.

  • ~2 s Time to first frame. Press play to a visible picture, or viewers leave.
  • Tens of thousands Titles in the catalog. Curated, so the write side is a trickle.
  • Tens of millions Concurrent streams at peak. Each pulling a new chunk every few seconds.
  • ~50-80 GB Stored per title. One master fanned out across the rendition ladder and codecs.

The number that dominates is the read-to-write skew. A handful of new titles land per day. Playback, meanwhile, serves tens of millions of simultaneous streams that each fetch a media chunk every few seconds, which is on the order of millions of segment requests a second globally. That is easily a billion-to-one ratio of reads to writes. Nearly every hard decision below is about making that read cheap and smooth, and letting the slow, small write path take whatever time it needs. Say that skew out loud early.

The set-up

Core entities

Name the objects the system moves before drawing anything. There are five, and the split between the last few is where the storage design is decided.

1

Title

A movie or episode as the catalog knows it: titleId, name, description, genres, cast, artwork references, and a status (draft, encoding, ready). This is small structured data, and it is what browse and search read.

2

Rendition

One encoded version of a title at a specific resolution and bitrate, in a specific codec, for example 1080p at 5 Mbps in H.264. A title has a whole ladder of these. Each rendition is a stream of small media segments.

3

Segment

A few-seconds chunk of one rendition (commonly 4 to 6 seconds), stored as an immutable file. Playback fetches these one after another. Because they never change once written, they cache forever.

4

Manifest

The small text file (HLS .m3u8 or DASH .mpd) that lists a title's renditions and, for each, the ordered URLs of its segments. The player downloads this first and uses it to decide what to fetch next.

5

WatchProgress

One row per user per title: userId, titleId, positionSeconds, and updatedAt. Read when a viewer opens a title to resume, written continuously while they watch.

Now the storage split, and it is the classic bytes-versus-metadata divide done at extreme scale. The Segment and Rendition bytes are enormous, immutable, and write-once: a single title fans out to tens of gigabytes across its ladder and codecs, and a big catalog runs to petabytes. That is exactly what object storage such as S3 is built for, and it is wrong to put it in a database. The Title catalog and the WatchProgress rows are small structured records, and they belong in databases, but not the same one, because they have opposite access patterns.

Catalog metadata is read constantly and written rarely, tens of thousands of rows keyed by titleId, so a Postgres instance with read replicas is the name everyone in the room knows and it is right-sized here. Search gets its own inverted index in Elasticsearch, fed from the catalog, because matching on name, genre, and free text is a different query than a key lookup. Watch progress is the odd one out. It is written millions of times a second and only ever read by the key (userId, titleId), so it wants a write-optimized key-value store like Cassandra or DynamoDB, partitioned by userId. Four stores, one per access pattern. The reason for each is the shape of its traffic.

API

The interface has a small twist worth calling out. The player does not stream video through our API. Our services return metadata and a manifest, and the player then pulls the actual segments straight from the CDN using the URLs inside that manifest. Keeping the heavy bytes off our request path is the entire point. That is deliberate.

The interface
RequestServer push
browse or search the catalog
GET /v1/catalog/search?q={query}&genre={genre}
{ results: [{ titleId, name, artwork, ... }] }
served by the catalog service off the search index
title detail plus where to start playing
GET /v1/titles/{titleId}
{ titleId, name, description, manifestUrl, resumePosition }
folds in the viewer's resume position in one call
the playback manifest (renditions + segment URLs)
GET /v1/titles/{titleId}/manifest.m3u8
HLS manifest text
small, cacheable; the player reads it to drive ABR
heartbeat the current position while watching
PUT /v1/progress/{titleId} { positionSeconds }
202 Accepted
fired every ~15 s by the player, fire-and-forget

Segment fetches are absent from this list on purpose. They are plain CDN GETs against the URLs the manifest hands out, never calls to our services, which is why the read path can carry millions of requests a second without our servers ever seeing them.

High-level design

Build it path by path and get a complete skeleton before polishing any one piece. The system splits into a slow ingest path that turns a master file into playable segments, and a fast read path that carries browse, playback, and resume. The diagram codes each box by role: blue for people and outside sources, violet for our services, amber for queues, green for caches and storage. The walkthrough tags each component the same way.

Loading diagram...

1) Ingest and transcode, occasionally. The Content team uploads a master file to the Transcode pipeline. The pipeline splits the video into chunks, enqueues one encode job per chunk on the Encode job queue, and workers encode each chunk at every rung of the bitrate ladder and in each codec. It packages the results into small HLS segments, writes them and the manifest to Object storage, and writes the title record to the Metadata DB, flipping its status to ready. This runs for minutes to hours and no viewer is waiting, which is why there is no urgency and no need to scale it hard. The part we are punting: turning one master into a whole ladder efficiently, on chunk boundaries that decode cleanly, is a deep dive.

2) Browse and search, constantly. The Viewer app calls the Catalog + search API, which reads the Search index for text and genre matches and the Metadata DB replicas for title details, and returns the small JSON the home and search screens render. No video is involved yet; this is ordinary read-heavy metadata serving, made cheap by replicas and caching. The part we are punting: keeping search fast and the index fresh as the catalog changes is a lighter deep dive.

3) Play, at massive fan-out. When the viewer opens a title, the Playback API returns its metadata and the URL of its manifest, folding in the resume position. The player downloads the small manifest, then starts fetching Object storage segments through the CDN edge, one every few seconds, choosing which rendition to pull based on the bandwidth it measures. Almost every one of those fetches is an edge cache hit that never reaches us, because segments are immutable and popular titles are watched by millions. The part we are punting: how the player picks a rendition and keeps playback smooth across changing bandwidth is the central deep dive.

4) Resume, in the background. While the viewer watches, the player fires a position heartbeat to the Progress API every few seconds, which writes it to the Progress store keyed by user and title. When they reopen the title anywhere, the resume position comes back with the metadata and playback picks up where they left off. The part we are punting: absorbing millions of heartbeat writes a second and reconciling position across devices is the third deep dive.

Technology choices

Each box is a category so far. Name a concrete tool for each and be ready to defend the category. The interviewer's favorite follow-ups are failure questions. The theme underneath all of them is one line. The write path is small enough to stay simple, and the read path leans on immutability and caching so no stateful tier grows with viewer traffic.

  • The Object storage is S3 (or GCS). Segments are large, immutable, write-once files, which is exactly the workload object storage is for and exactly the workload a database is not. Because a segment never changes after it is written, it is safe to cache at the edge forever, and losing one from a cache costs a single refetch. Durability is S3's job, so the master and every segment survive a machine or rack loss with no work from us.
  • The Metadata DB is Postgres with read replicas, keyed by titleId. The catalog is small and read far more than written, so replicas absorb the read load and the primary handles the rare title update. There is nothing to shard at tens of thousands of rows. If the primary fails, a replica is promoted and reads keep flowing off the other replicas throughout, since browse never needed the primary.
  • The Search index is Elasticsearch, fed from the catalog. It answers name, genre, and free-text queries that a key-value lookup cannot. It is fully rebuildable from the metadata DB, so if a node or the whole cluster is lost, we reindex from the source of truth and lose nothing but a little freshness while it catches up.
  • The Progress store is Cassandra or DynamoDB, partitioned by userId. It is chosen for write throughput: continuous heartbeats are a firehose of tiny upserts, and these stores are built to absorb high write rates and answer point reads by key. There are no cross-key queries and no joins, so their weak spot, complex querying, never applies here. A hot partition would only appear if one user streamed on absurdly many devices, which is a non-issue.
  • The CDN edge is CloudFront, Fastly, or Akamai, and at Netflix's scale, appliances placed inside ISP networks. Video-by-segment is unusually cache-friendly: segments are immutable, and a popular title's segments are requested millions of times, so an edge serves nearly all of them from a box near the viewer. When an edge fails, players resolve to another nearby edge, and because origin traffic is already a thin trickle of cache fills, there is no herd for the origin to fear.
  • The Encode job queue is SQS or Kafka holding one job per chunk. It lets the pipeline fan a title across many workers and retry a failed chunk on its own. Encode jobs are idempotent, meaning re-running one writes the same segment to the same place, so a duplicated or redelivered message is harmless. If a worker dies mid-job, the message reappears after its visibility timeout and another worker picks it up.
  • The Playback API, Catalog + search API, and Progress API are stateless servers behind a load balancer. They hold no state between requests, so any instance serves any request and a dead one just drops out of rotation. This is deliberately the boring part: all the hard state lives in the stores and the CDN, and the services are thin.
The HubSpot angle
The bullet to say with conviction is the ingest one. Because the catalog is curated, the transcode pipeline is a batch job that can take hours and needs no autoscaling heroics, and saying so is worth more than adding a streaming ingest tier the problem does not have. That is the same judgment HubSpot's take-home grades for, simplicity and clarity over cleverness. A candidate who answers "Design Netflix" by building a YouTube-style firehose ingest has pattern-matched the word "video" to "big write pipeline" without checking the traffic, and it reads exactly that way. Point at the tiny write volume, keep ingest simple, and spend your depth on the read path, where the hard problems sit.

Potential deep dives

The skeleton is the easy part. Almost all the signal is in the next few questions. The interviewer will pick one or two and push. The first is where the round is usually decided. Slow down there.

1) How does adaptive bitrate keep playback smooth as bandwidth swings?

The viewer's connection is out of our control and changes constantly: full bars on wifi, then a tunnel, then a shared line at dinnertime. Commit to one quality up front and you lose either way. Pick high and the video stalls the moment the connection dips. Pick low and you waste a good connection on a soft picture. We want the highest quality the current connection can carry, re-decided continuously, without ever running the buffer dry. That is what adaptive bitrate streaming, usually shortened to ABR, is for. The player does the deciding.

The mechanism rests on two ideas from the setup. First, every title is encoded into a bitrate ladder: a set of renditions at climbing resolution and bitrate. Picture 240p at 300 kbps at the bottom up through 1080p at 5 Mbps and 4K near 15 Mbps at the top. Second, each rendition is cut into aligned few-second segments, and the manifest lists them so that segment 12 of the 480p stream covers the same slice of the movie as segment 12 of the 1080p stream. Because the cut points line up, the player can fetch segment 12 at 480p and segment 13 at 1080p and splice them with no visible seam.

BadOne resolution, progressive download

Approach

Encode each title once at a single quality and stream it as one long file, the way a basic web video tag downloads an MP4.

Challenges

There is no way to react to the network. A viewer on a weak connection buffers endlessly because the one bitrate is too high, while a viewer on fiber is stuck with whatever single quality you chose. A mid-stream dip stalls playback outright, since the player has no lower rung to drop to. This is the approach ABR exists to replace, and being able to say why it fails sets up the tiers that follow.

GoodFixed bitrate ladder with client-driven ABR

Approach

Encode every title into a fixed ladder of renditions and cut each into aligned segments listed in the manifest. The player downloads the manifest and starts a couple of rungs up. After each segment it compares the throughput it just measured and how full its buffer is against the ladder, then requests the next segment from the highest rung it can sustain. When bandwidth drops it steps down before the buffer empties; when bandwidth recovers it steps back up. All of this logic lives in the player, so our servers only ever hand out immutable segments.

Challenges

A single ladder is a compromise across every title. A simple animation and a grain-heavy action film get the same rungs, so one wastes bits and the other looks worse than its bitrate should allow. Starting a couple of rungs up also costs a beat at the very start while the player probes the connection, which nibbles at the two-second target. It works well and is a complete answer on its own, but the ladder is not tuned to the content and startup is not yet optimized.

The good tier is genuinely strong, and for a Senior I answer it is enough. What forces the upgrade is two specific wants: squeezing better quality out of the same bitrate by tuning the ladder to each title, and shaving the startup delay so the first frame beats the two-second bar. Both are content-aware refinements on top of the same ABR machinery.

GreatPer-title encoding and a fast start

Approach

Analyze each title's visual complexity and set its ladder to match, rather than reusing one fixed ladder for everything. A low-complexity title drops the wasteful top rungs and shifts its rungs to lower bitrates that still look clean, while a high-complexity title earns richer rungs. Viewers get the same or better picture at meaningfully lower bitrate, which cuts both storage and delivery cost. For startup, begin playback at a low rung so the first segment arrives fast and the picture appears within the latency budget, keep the first few segments small, and ramp up as the buffer fills. Prefetch the manifest and the opening segments of a title the moment a viewer hovers or presses play, so the first fetch is often already warm at the edge.

Challenges

Per-title analysis adds real work to the encode pipeline, but that work is on the slow, cheap write side where nobody is waiting. That makes it a good trade: pay once at encode to save on every one of millions of streams. The honest limit is that ABR is reactive, so a sudden bandwidth cliff can still force a visible quality drop. The buffer is the shock absorber. The player keeps it healthy, which trades a slightly longer startup for resilience mid-stream. Tuning that buffer target, and how aggressively to ramp, is a judgment call the player logic owns.

The HubSpot angle
Tie this back to cost, because it is where a HubSpot-style pragmatism lands hardest. HubSpot hosts customer sites and pays for every byte those sites serve, so cost-per-delivered-byte is a real line item they think about, not an abstraction. They reward the same instinct on this problem. Delivery is nearly the entire bill, so attack it. Immutable segments cached at the edge keep origin traffic flat regardless of viewers, and per-title encoding makes every stream carry fewer bytes for the same picture. Naming that you would pay compute once at encode to save bandwidth on every stream is exactly the cost-aware reasoning the round is listening for.

The scaling reads and blob and object storage lessons go deeper on edge caching and how immutable objects make a read path cheap.

2) How do we turn one master file into the whole rendition ladder?

Start with why this is not trivial. Each title arrives as one large high-quality master. Playback needs it as a full matrix: every rung of the bitrate ladder, in each codec the various devices support, cut into aligned segments with a manifest. Encoding high-quality video is slow. It is often slower than the video's own running time on a single machine, and we owe that work several times over per title. Done naively, a single two-hour film could sit encoding for many hours end to end.

The number that reframes it: the catalog grows by only a handful of titles a day. So unlike a firehose upload service, this is an occasional batch job. The only real goal is to finish each title in a reasonable wall-clock time without a machine sitting idle for hours. That lets us trade a lot of parallel workers, which are cheap and idle most of the day, against latency per title. Nobody is waiting on the batch.

BadEncode the whole file on one machine, serially

Approach

Feed the master to a single encoder and produce each rendition one after another, top to bottom of the ladder.

Challenges

Wall-clock time per title balloons into many hours, because a serial pass at slower-than-realtime is repeated for every rung and codec. Worse, a failure near the end, a crash or a bad segment, throws away all the work and restarts the whole title. It technically produces correct output, but it wastes both time and any chance to recover from a partial failure cheaply.

GoodChunked parallel encoding on clean boundaries

Approach

Split the master into short chunks and encode them in parallel across a worker fleet, one job per chunk on the Encode job queue. The cut points must fall on closed-GOP boundaries. A GOP, or group of pictures, is a run of frames that starts with a full keyframe and can be decoded without reference to any frame outside it. Cut there and each chunk decodes on its own, so the pieces line up perfectly when stitched. Each chunk job encodes its slice at every ladder rung and codec, packages the output into aligned HLS or DASH segments, and writes them to Object storage. A coordinator tracks which chunks are done and assembles the final manifest once they all land. A whole title now finishes in the time of its slowest chunk plus assembly, not the sum of every rung.

Challenges

You need the boundaries exactly right, because a chunk cut mid-GOP would not decode cleanly and the seams would glitch. And you are coordinating many jobs per title, so the pipeline needs to know when a title is fully done versus partly encoded. Both are manageable, and the queue's retry handles a failed chunk without redoing the title.

Chunked parallel encoding is the answer that matters most here, and for many loops it is the finish line. What earns the great tier is handling the codec matrix and priorities with the same cost-awareness the rest of the design has, rather than blindly encoding everything into every codec.

GreatIdempotent chunk jobs and a popularity-tiered codec matrix

Approach

Make each chunk-encode job idempotent, keyed by title, chunk, rung, and codec, so a retry after a crash rewrites the exact same segment to the exact same path and duplicate messages are harmless. The queue can then guarantee at-least-once delivery cheaply. On the codec matrix, do not encode every title into every advanced codec up front. Always produce the universally supported baseline (H.264) so any device can play immediately. Reserve the more efficient but costlier codecs (H.265, VP9, AV1) for titles that earn them, encoding those lazily or for popular titles, where the bandwidth saved across millions of streams more than pays for the extra encode. This is per-title encoding's sibling: spend encode compute where it returns delivery savings, and skip it where it would not.

Challenges

Lazy or tiered codec generation means a device that prefers an efficient codec might, for an obscure title, fall back to the baseline until the better rendition exists, which is a fine trade. And keying jobs for idempotency adds bookkeeping, but that bookkeeping is exactly what lets the pipeline retry freely and run on cheap interruptible workers. Both costs buy a pipeline that is safe to restart and cheap to run.

The async processing and message queues lessons cover idempotent jobs and retries in more depth.

3) How do we store and sync resume position across devices?

Put a number on this one first, because the number is the whole reason it is hard. Tens of millions of streams play at once, and each player reports its position every few seconds. Take a heartbeat every fifteen seconds against, say, fifty million concurrent streams. That is over three million writes a second, all day. These writes are tiny and individually worthless, since only the latest position per user and title ever matters. But the rate is relentless. Meanwhile the read is trivial: one point lookup when a viewer opens a title. So this is a write-absorption problem with a throwaway read. The rate is the whole challenge.

BadSynchronous write to a relational DB every heartbeat

Approach

On each heartbeat, write the new position straight into a transactional row in the main database and acknowledge only once it commits.

Challenges

Three million committed writes a second to a relational primary is a firehose it was never built to take, and every write pays for durability and locking the data does not need. The database becomes the bottleneck for a value that is stale two seconds later anyway. Nobody should build this, but naming why it fails motivates the write-optimized store.

GoodThrottled writes to a write-optimized key-value store

Approach

Send heartbeats to the Progress API, which upserts one row keyed by (userId, titleId) into a write-optimized store like Progress store (Cassandra or DynamoDB) partitioned by userId. These stores are built to swallow high write rates and answer point reads by key, which is precisely this workload. Treat each heartbeat as fire-and-forget: the player does not wait on the ack, and a dropped one just means the next heartbeat a few seconds later carries a slightly newer position. Because we only keep the latest position per key, every write is a simple overwrite with no history to manage.

Challenges

Three million upserts a second is still real load, and much of it is redundant, since a viewer's position barely moves between two heartbeats fifteen seconds apart. And once the same account is watching on two devices, both write the same key, so we need a rule for which position wins. The store handles the volume, but we can cut the volume further and we still owe a cross-device answer.

The good tier holds up, and for most loops it is enough. The upgrade is about trimming that redundant write volume and settling the cross-device question honestly rather than hoping two devices never touch the same title.

GreatClient batching, a write-through cache, and last-writer-wins

Approach

Let the player be smart about when it reports. It keeps position locally and only sends a heartbeat every so often or on meaningful events (pause, seek, exit), which cuts the write rate without losing resume accuracy, since the local position is the truth while watching. Front the store with a Redis write-through layer so the hot, latest position is a fast in-memory read on title open and the durable store is updated behind it. For two devices on one account, resolve with last-writer-wins on updatedAt: the most recently reported position simply wins, which is exactly what a human expects, since the device they most recently watched on holds where they really are. This is eventual consistency by design. A second or two of cross-device skew is invisible.

Challenges

Last-writer-wins can, in a rare tie, favor a device the viewer just paused over one they are actively watching, but the next heartbeat from the active device corrects it within seconds, so the window is imperceptible. Client batching means a hard crash can lose the last few unsent seconds of position, which resumes a moment earlier than ideal, a cost no viewer notices. Choosing this weak consistency and explaining why it is safe is the answer the round wants, not a distributed lock over a value that does not deserve one.

The HubSpot angle
This dive is a good place to show the judgment HubSpot's rubric prizes: matching the guarantee to what the data actually needs. Watch position is the textbook case for relaxing consistency, and a candidate who reaches for strong consistency or a transaction here is over-building against the grain of the problem. Say the weaker guarantee out loud, "eventually consistent, last-writer-wins, and here is exactly why a second of skew is fine," and defend it. That sentence, a justified consistency choice rather than a reflexive strong one, is worth more than any amount of machinery, and it is the kind of tradeoff the collaborative interviewer will happily follow you into.

The handling contention and Cassandra and DynamoDB lessons go deeper on write-heavy key-value stores and conflict resolution.

This one is lighter, and worth reaching for when the interviewer scopes toward the front door rather than the player. Browse and search read constantly and write rarely. The catalog is tens of thousands of titles that change a few times a day, queried by a huge audience. So the shape is ordinary read-heavy metadata serving. The tools are the familiar ones rather than anything streaming-specific. Reach for the boring ones.

Serve title lookups and home rows from the Metadata DB read replicas, and cache the hot, mostly-static responses (the home screen, popular genres) so most reads never touch a database at all. Send free-text and genre search to the Search index, an inverted index that matches on words the way a key lookup cannot. Keep the index in sync by feeding it catalog changes as they happen, and lean on the fact that it is fully rebuildable from the metadata DB if it ever falls behind or a node is lost. Because the catalog barely changes, staleness of a few seconds in search results is a non-issue, so the sync can be asynchronous and simple.

Only push here if the interviewer wants breadth on the read path. Having it ready shows you treated "browse and search" as a real requirement. It also shows you know when a problem calls for boring, proven tools rather than clever ones.

What is expected at each level

HubSpot scopes this round by level, and roughly how far you carry the delivery and encoding pieces is what sorts one level from the next. What follows is what to land for the level you are interviewing for. The level names below follow HubSpot's ladder; the round count by level is reported and varies, so treat it as a guide.

SSE-1 (Senior I)

This level leans on breadth, roughly 80/20 breadth to depth, and a Senior I loop is often a single design round. Reach a working skeleton: a slow transcode pipeline that produces a bitrate ladder, with segments and a manifest in object storage. Add a CDN-fronted playback path where the player pulls segments directly, catalog and search off a metadata DB and index, and resume position in a key-value store. Above all, state the read-versus-write asymmetry early and let it keep your ingest simple. The bar: a clean, right-sized design with a coherent story for how a smooth picture reaches a viewer.

SSE-2 (Senior II)

Now depth enters, roughly 60/40, and a Senior II loop often runs two rounds or a round plus a written document. Drive the ABR deep dive yourself. Explain the aligned bitrate ladder and manifest, and how the player picks a rendition from measured bandwidth and buffer. Then explain why per-title encoding and a low-rung fast start both improve quality and cost. You should reach chunked parallel encoding on closed-GOP boundaries without being led there, and handle resume position as a write-absorption problem with a justified eventual-consistency choice. The bar: you find the delivery and encoding depth on your own and defend the tradeoffs behind them.

Staff and above

Depth leads here, roughly 40/60, and the scope widens past the happy path. A few threads open up. Reason about delivery cost at global scale and the CDN economics of caching immutable segments near viewers. Handle read-side protection the player glosses over: signed expiring segment URLs and DRM as the gate on who may fetch bytes. Talk through graceful degradation when an edge region or the origin is under strain, and how the encode ladder and codec matrix trade write-side compute for delivery savings. The platform question is fair too: how the same immutable-segment-plus-CDN pattern generalizes to any large read-heavy delivery workload. The bar: you own the delivery, encoding, and consistency argument end to end, and the interviewer redirects you rather than rescues you.

Key Takeaways

  • ✓Lead with the asymmetry: a curated catalog is a trickle of writes against a planet-scale read fan-out, so keep ingest simple and spend your depth on delivery.
  • ✓Never stream bytes through your services. The manifest sends the player straight to a CDN for immutable segments, so no stateful tier grows with viewer traffic.
  • ✓Adaptive bitrate is the core: an aligned bitrate ladder plus segments lets the player pick quality from measured bandwidth, and per-title encoding cuts bytes for the same picture.
  • ✓Transcode with chunked parallel encoding on closed-GOP boundaries and idempotent jobs, since the write side is a slow batch where finishing cleanly beats finishing fast.
  • ✓Resume position is a write-absorption problem: throttle heartbeats into a write-optimized key-value store and settle devices with last-writer-wins, a justified eventual-consistency choice.
Had your HubSpot interview?Tell us what they asked. Reviewed before it goes in, never with your name.
HubSpot interview guide

Know what HubSpot asks before you walk in.

7 coding · 3 system design · 23 behavioral questions

Get Premium$49 a year · Cancel anytime
Or start freeNo card.

Get their questions first.

InterviewLegend is independent and not affiliated with or endorsed by any company it covers.