Design problems
Media & streamingMediumSDE-2 30 min

Design Spotify

Asked at
AmazonAppleGoogleJioHotstar

The interviewer asks

“Design a music streaming service like Spotify: users search a catalogue of 100M+ tracks, play songs instantly, build and share playlists, download for offline listening, and get personalised recommendations. Artists must be paid correctly for every stream.”

What a strong answer looks like

Audio is small compared with video (a 3.5-minute song at 160 kbps is ~4 MB), so the hard parts move from bandwidth to latency, metadata and correctness. A strong answer covers: audio files encoded at a few bitrates, encrypted, and served by CDN, with the client making playback feel instant through a small first chunk, prefetching the next track and a large on-device cache; a metadata and playlist layer that serves very hot objects (a playlist followed by 15M people) from cache; search over tracks, artists and podcasts in many languages; and a play-event pipeline that counts streams (30-second rule) once and only once in effect, because royalties depend on it. Recommendations are mostly precomputed in batch (weekly and daily mixes) plus session-level signals.

Concepts to mention

Audio files encoded at several bitrates, encrypted, served from CDNOn-device cache and prefetch of the next trackSmall first range request for fast startOffline downloads with expiring DRM licencesPlaylist snapshots and change operationsImmutable snapshots make hot playlists cacheableClient event ids and deduplication30-second stream rule and fraud filteringBatch royalty reports recomputable from raw eventsPrecomputed recommendations with session reranking

1 Requirements

Functional

  • Search tracks, albums, artists, playlists and podcasts
  • Play a track instantly with seek, skip, shuffle and gapless playback
  • Create, edit, follow and share playlists (including collaborative playlists)
  • Download playlists for offline listening (Premium), protected by DRM
  • Personalised home, daily mixes and weekly discovery playlists
  • Count streams accurately for royalties and artist analytics
  • Free tier with ads and limited skips; Premium with higher quality

Non-functional

  • ~700M monthly users, ~250M daily; ~100M+ tracks and millions of podcast episodes
  • Playback start < 300 ms p90 for the next track (prefetched) and < 1 s for a cold track
  • Playback 99.99% available; offline listening works with no network at all
  • Stream counts must be correct and auditable: no double counting, no lost streams
  • Search results < 200 ms p95, handling typos and Hinglish/transliterated queries
  • Work on 2G/3G with low bitrate (24-96 kbps) streams

Out of scope (say it out loud)

  • Royalty calculation formulas and label contracts
  • Ad serving internals
  • Podcast hosting and video podcasts
  • Social features beyond playlist sharing

2 Estimates

Listening per day~500M hours250M DAU × ~2 hours/day
Track plays~8-9B/day ≈ 100k/s avg, ~250k/s peak500M hours × 60 ÷ 3.5 min per track ≈ 8.6B plays/day ÷ 86,400
Concurrent listeners~20M avg, ~40M peak500M hours ÷ 24 h ≈ 21M; ×2 at commute and evening peaks
Egress~3-4 Tbps avg, ~7 Tbps peak21M × ~160 kbps ≈ 3.4 Tbps; on-device cache hits reduce real CDN traffic further
Audio storage~3-5 PB100M tracks × 4 lossy bitrates (~24, 96, 160, 320 kbps) ≈ 100M × ~20 MB = 2 PB, plus lossless for part of the catalogue
Metadata reads~500k-1M req/sHome, search, playlist and track metadata calls from 40M active clients
Play events~300k-500k events/sEach play emits several events (start, 30 s, end/skip, seek); ~4 events × 100k plays/s, more at peak

3 API

EndpointWhat it does
GET/v1/search?q=arijit tum hi ho&type=track,artist&market=INTypo-tolerant multi-type search
GET/v1/tracks/{id}Track metadata: title, artists, album, duration, availability per market
POST/v1/playback/resolveReturn file ids for the track at the allowed quality, CDN URLs and a decryption-key reference
GET/v1/playlists/{id}?snapshot_id=…Playlist contents; returns 304-like 'unchanged' if the client's snapshot is current
POST/v1/playlists/{id}/changesAdd/move/remove items: [{op, track_id, position, base_snapshot_id}]
GET/v1/me/homePersonalised home shelves
POST/v1/eventsBatched play events with client-generated event ids (works after offline playback too)
POST/v1/offline/licensesGet or renew offline keys for downloaded tracks (must reconnect every ~30 days)

4 Data model

catalogue (tracks, albums, artists)Bigtable / Cassandra + in-memory cache
track_idisrctitleartist_idsalbum_iddurationmarketsexplicitfile_ids per formatpopularity

Ingested from labels and distributors. Read-mostly; heavily cached at the service and CDN layer.

audio filesObject storage (GCS) + CDN
file_id = hashencrypted Ogg Vorbis/AAC at 24/96/160/320 kbpsFLAC for lossless

Immutable, encrypted with per-file keys; safe to cache anywhere.

playlistsBigtable / Cassandra
playlist_idownernamefollowers_countsnapshot_iditems [{track_idadded_byadded_at}] stored as an ordered list or change log

Each edit creates a new snapshot id; clients sync by snapshot. Popular editorial playlists are cached aggressively.

user libraryBigtable / Cassandra
user_id (partition)liked trackssaved albumsfollowed playlists/artistsoffline downloads

Partitioned by user; sync across devices.

play_eventsPub/Sub or Kafka → data lake (BigQuery)
event_id (UUID from client)user_idtrack_idms_playedreason_start/endoffline flagdevicets

Source of truth for stream counts and royalties; deduplicated by event_id.

search indexElasticsearch / custom Lucene-based
entity idtypenamesaliasestransliterationspopularitymarkets

Edge n-grams for prefix, phonetic and transliteration fields for Indian languages; ranked by popularity and personal affinity.

precomputed recommendationsBigtable / key-value
user_id → home shelvesdaily mixesweekly discovery track list

Generated by batch pipelines; served directly.

5 Architecture

CLIENTSEDGESERVICESASYNCDATAEXTERNALmetadata, listsencrypted audiomissbatched eventssnapshotsdeduped streamsmixes, homeaudio + metadataencoded filescatalogindexApps (cache + offline)CDN (audio files)API gatewayPlayback + metadatasvcPlaylist serviceEvent collectorSearch serviceIngest + encodeworkersRecommendation batchEvent bus (Pub/Sub)Stream counting jobsObject storage(audio)Bigtable (catalog,lists)Search indexData lake (BigQuery)Labels / distributors
ClientEdge / gatewayService / workerQueue / streamCacheDatabase / storageAI / modelThird partyrequestasync (event / queue)

Hover a box to highlight its connections.

6 Request flows

Play a track

  1. 1The user taps a song. If its file is in the on-device cache (common for repeat listening), playback starts immediately from disk.
  2. 2Otherwise the client calls /playback/resolve with track id and quality setting; the service checks market availability and plan, and returns file ids, CDN URLs and a key reference.
  3. 3The client fetches the first ~100-200 KB range (a few seconds of audio) from the CDN, decrypts and starts playing within a few hundred ms, then streams the rest.
  4. 4While the current track plays, the client prefetches the beginning of the next track in the queue so the skip or transition is instant and gapless.
  5. 5Play events (start, 30-second mark, end or skip) are queued locally with unique event ids and sent in batches.

Count a stream for royalties

  1. 1The event collector validates the client and writes events to the event bus; it acknowledges only after the bus accepts them, so the client can safely retry.
  2. 2Offline listening events are uploaded when the device reconnects, with their original timestamps.
  3. 3The counting job deduplicates by event_id, applies the rule that a stream counts after 30 seconds of play, and filters fraud (bot farms looping tracks).
  4. 4Valid streams land in the data lake partitioned by day; daily and monthly reports per track, rights holder and country are produced in batch.
  5. 5Royalty reports are recomputable from raw events, so a bug can be fixed and the period re-run.

Edit a collaborative playlist

  1. 1Two friends add songs at the same time; each client sends {op: add, track_id, position, base_snapshot_id}.
  2. 2The playlist service applies changes in order of arrival; if base_snapshot_id is stale, it rebases (an add at the end still goes at the end; a move of a removed item is dropped).
  3. 3Each successful change produces a new snapshot_id; other clients see it on their next sync or via a push notification and fetch the diff.

7 Deep dives

Making playback feel instant

Users judge music apps by how fast the song starts after a tap or skip. The levers: first, the on-device cache. Clients keep a large encrypted cache (several GB) of recently played files, and because people replay the same songs a lot, a big fraction of plays never touch the network. Second, prefetching: while track N plays, download the first part of track N+1 (and for shuffle, the chosen next track), so a skip starts from memory. Third, a small first request: fetch only a few seconds of audio with an HTTP range request so decoding starts fast, even on a slow mobile network, then continue streaming. Fourth, CDN placement: audio files are immutable and encrypted, so they can sit on multiple commercial CDNs with high hit rates; the client is given more than one host and retries elsewhere on failure. Pick the bitrate by network and plan (24 kbps on a poor connection, 160 kbps normal, 320 kbps Premium). Files are encrypted with per-file keys, and keys are delivered separately; the CDN never sees a playable file. Spotify originally used peer-to-peer to offload bandwidth but dropped it in 2014 once CDN costs fell; mentioning why P2P stopped being worth it shows judgement.

Stream counting you can pay royalties on

Royalties are paid per stream, so counts must be complete and not inflated. The client creates a unique event id for each event and persists events locally until the server acknowledges them, which means at-least-once delivery from client to server. The server writes to a durable event bus (Kafka or Pub/Sub) before acknowledging. Downstream, the counting job deduplicates by event id over a window long enough to cover late arrivals; offline plays can arrive days later, so dedup and attribution use the event's own play timestamp, and daily reports are finalised after a grace period. A play counts as a stream after 30 seconds. Fraud filtering looks for unnatural patterns: the same tracks looped by many fresh accounts, plays from data-centre IPs, or perfectly regular timing. Store raw events immutably in the data lake and compute reports in batch, so any reporting bug is fixed by re-running the job. Real-time numbers (artist dashboards) come from a streaming path that is allowed to be approximate.

Playlists: snapshots, big playlists and hot reads

A playlist is an ordered list of up to ~10,000 items. Store it as a sequence of items plus a snapshot id that changes on every edit; clients cache the playlist with its snapshot id and ask 'has it changed since snapshot X?', which is cheap and saves bandwidth for large playlists. Edits are small operations (add, remove, move) applied in arrival order with a rebase against the latest snapshot, which is enough for collaborative playlists where conflicts are rare and harmless. Reads are extremely skewed: a top editorial playlist can have tens of millions of followers and be opened thousands of times per second. Serve it from a cache keyed by (playlist_id, snapshot_id); because the snapshot is immutable, cached copies never need invalidation, only the 'current snapshot' pointer does. Follower counts are counters maintained asynchronously, not computed on read. Market availability is applied at read time, so a track not licensed in India is greyed out instead of the playlist being duplicated per country.

Personalisation without real-time heavy lifting

Most recommendations are precomputed. Batch pipelines on the data lake build user and track embeddings from listening history, playlist co-occurrence (two songs often in the same playlists are similar), and audio features; they then generate per-user outputs like a weekly discovery playlist, daily mixes and home shelves, and write them to a key-value store keyed by user id. At request time, the home service reads the precomputed shelves and adjusts them with session context (time of day, recently played, device such as car or smart speaker). This keeps the online path cheap and resilient: if the pipeline is late, users see yesterday's shelves. New users and new releases are the cold-start problem: use onboarding choices, language and region (Punjabi, Tamil, Bollywood are huge separate audiences in India), and popularity within similar listeners.

8 Trade-offs

CDN vs peer-to-peer delivery

P2P cut costs when bandwidth was expensive, but hurt mobile battery and data plans and added complexity. Commercial CDNs became cheap enough that serving everything from CDN plus a big client cache is simpler and faster.

Precomputed vs real-time recommendations

Precomputed lists are cheap, stable and easy to evaluate, but react slowly. Real-time models react to the current session but cost more per request. A hybrid gives most of the benefit: precompute candidates, rerank lightly at request time.

Exact batch counts vs real-time counts

Royalty counts must be exact and auditable, so they come from deduplicated batch jobs. Live artist dashboards use a separate approximate stream pipeline. Mixing the two leads to either slow dashboards or wrong payouts.

Snapshot-based playlist sync vs real-time CRDT

Playlists change rarely and conflicts are mild, so snapshots plus rebased operations are enough. A CRDT would add complexity without a real user benefit.

9 Bottlenecks

Big release moment

A major album drop at midnight sends millions to the same tracks. Pre-warm CDNs with the new files, cache metadata and the album page, and make sure the first-chunk requests are served from edge.

Hot editorial playlists

Millions of reads on the same playlist. Immutable snapshots cached at the service and CDN layer remove the hot key problem.

Event bus volume

Hundreds of thousands of events per second with spikes after mass reconnects (morning commute, offline uploads). Batch events on the client, compress, partition the bus by user id and autoscale consumers on lag.

10 Failure handling

Network drops during playback

The client keeps playing from its buffer and cache, retries other CDN hosts, and lowers the bitrate. Offline downloads keep working with cached keys until the offline licence expires (~30 days without reconnecting).

Event collector unavailable

Clients keep events in a local queue and retry with backoff. Events have unique ids, so retries cannot double count.

Recommendation pipeline late

Serve the previous day's shelves and mixes. Users rarely notice a one-day delay.

Search cluster degraded

Serve from replicas, reduce query complexity (skip fuzzy and personalisation) and keep popularity-ranked results available.

11 What impresses

  • Notes that audio is small, so the design focus is latency and correctness rather than bandwidth
  • Explains prefetching and on-device cache as the main tools for instant playback
  • Treats stream counts as money: client event ids, dedup, late offline events, recomputable batch
  • Uses immutable playlist snapshots to solve hot-read caching cleanly
  • Handles transliterated Indian-language search (Hinglish queries)
  • Knows why P2P delivery was dropped

12 Mistakes

  • Copying the YouTube design with heavy transcoding and video-scale bandwidth maths
  • Counting streams with a database increment per play
  • Ignoring offline listening and how its events get counted later
  • Recomputing recommendations on every home page load
  • Not handling market availability and licensing
  • Forgetting encryption/DRM of cached and downloaded files

13 Follow-ups

Q1How would Spotify Connect work: controlling playback on a speaker from your phone?
Q2How would you implement a 'Wrapped' year-in-review for 700M users?
Q3How do you detect artificial streaming fraud?
Q4How would lyrics synchronised to the song work?
Q5How do you design the free tier's skip limits and ad insertion?
Q6How would you search for a song by humming?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free