SDSystem design
Start learning free

LESSON 51 OF 140 · DESIGN: MEDIA & FILES

SYSTEM DESIGN · INTERVIEW WALKTHROUGH

Design Spotify: System Design Interview, Step by Step

What the interviewer is asking

"Design Spotify" is a media question, like Design YouTube, but music changes the problem. The files are small: a 4-minute song at 160 kbps is about 5 MB. The hard parts are a catalog of 100 million tracks where a few get almost all the plays, playback that must start the moment someone presses play, playlists people edit and share, downloads that must stop when the license ends, and play counts that decide what the artists are paid.

Recall the video design in a sentence, then spend the interview on what's different about music, following the 8 steps of the interview framework: scope, data, API, the busy path, numbers, services, scale and deep dives.

1. Scope: what version 1 must do

  • Search for songs, artists and albums.
  • Play a song at once, at home or on a train.
  • Make playlists and share them.
  • Download songs to play offline (premium).
  • Count every play over 30 seconds. Artists are paid per play; Spotify for Artists counts a stream once it passes 30 seconds.

Park the rest: recommendations, podcasts, a friends' feed and lyrics.

2. Data: four kinds of records

The audio bytes live in object storage, under keys like t9/160; the records keep only those keys.

record           fields
track            id, title, artist, album, length, file keys (96, 160, 320 kbps)
playlist         id, owner, name
playlist entry   playlist id, position, track id
play             play id, user, track, time, ms played

A track sits in many playlists and a playlist holds many tracks, so the entries get a table of their own. A play is one row per listen; its milliseconds played decide whether it counts.

3. The API: four calls

GET  /search?q=night
  → 200 OK   tracks, artists, albums
GET  /tracks/t9/stream
  → 200 OK   {"url": "<signed, 5 min>"}
GET  /playlists/p7
  → 200 OK   name, owner, entries in order
POST /playlists/p7/tracks   {"track": "t5", "after": "t9"}
POST /plays   [{"id": "a1", "track": "t4", "ms": 180000}, …]

The call to get right is the stream. The API checks the listener's account and answers with a signed URL for one file at the CDN, good for a few minutes: the bytes never pass through your app servers, and a copied link soon stops working. A permanent public URL would let anyone play the song without an account, and labels license music to the service's listeners. The app sends its plays in batches, each with an id it made itself.

4. The busy path: pressing play

  1. The app sends GET /tracks/t9/stream to the API.
  2. The API checks the account and reads t9's row: the key of its 160 kbps file.
  3. It answers with a signed URL, good for a few minutes.
  4. The app asks the nearest CDN edge for the start of the file.
  5. The first seconds play while the rest streams in.

One small read and a signature, then bytes from an edge nearby. Listeners judge the app by how soon the first note plays.

5. The numbers

From the interviewer: 100 million tracks of about 4 minutes in 3 qualities (96, 160 and 320 kbps), and 50 million people listening at the busiest hour, about 10 million at an average moment. A day has 86,400 seconds; round it to 100,000 for quick math.

what                working                         result
one song at 160     160 kbps × 240 s ÷ 8            4.8 MB, about 5
one track, all 3    96 + 160 + 320 = 576 kbps       about 17 MB (3 + 5 + 10 ≈ 18)
whole catalog       100 million × 18 MB             about 2 PB (5 PB in 3 copies)
audio out, peak     50 million × 160 kbps           8 Tbps
plays, average      10 million ÷ 240 s              about 40,000 a second
plays a day         40,000 × 100,000 s              about 4 billion (3.6 with 86,400)
plays, peak         50 million ÷ 240 s              about 200,000 a second

Two slips on the catalog: counting only the 160 kbps copy gives 500 TB, and skipping the ÷ 8 from bits to bytes gives 14 PB. The real answer is small: the YouTube lesson's numbers add 800 TB of video every day, so the whole music catalog is about two days of it.

Bandwidth is not small. 8 Tbps is about 1,000 servers' networks (one sends about 8 Gbps), though each listener needs about 30 times less than a 5 Mbps video: music is many small streams. The data that keeps growing is the plays: at say 100 bytes a play, 400 GB a day, about 150 TB a year.

6. Services: catalog, playlists, plays

  • Catalog: track rows in a catalog database, and a search index built from it: each word of every title, artist and album lists the tracks it appears in, prefixes included, so results show up as the listener types. LIKE '%night%' can't use an ordinary index and would read all 100 million rows.
  • Playlists: playlists and their entries in a playlists database.
  • Plays: POST /plays puts each batch on a queue and answers at once. Counting jobs store every play for the royalty reports and add up each track's plays over 30 seconds in batches. The same stream feeds the top charts.

Adding 1 to a count on the track's row for every play would be about 200,000 writes a second at the busiest hour, 40 times what one database server handles, with a hit song's row taking a big share. Totals sent by the app can't be checked, and royalties are money.

7. Scale: what each edge keeps

Plays follow a long tail: say the top 1% of tracks get 80% of plays, the next 9% get 15%, and the other 90% only 5%. Say an edge has room for a tenth of the tracks, under 200 TB. "Songs are small, keep them all" fails: 100 million of them are about 2 PB, ten times the room, and the last 90% of tracks would take 9 of every 10 bytes for 5% of the plays. Keeping only new releases fails too: most new tracks are rarely played, and many hits are years old.

So each edge keeps what's played most near it: a track arrives on its first miss, and the least played are dropped. Track rows get the same treatment: 200,000 play starts a second would be 40 times one database server, so a cache in memory, about 100,000 lookups a second per server, answers the hits.

8. Deep dive: the long tail

With the top tenth kept, an edge answers 80% + 15% = 95% of plays. Only 5% of the 8 Tbps comes from storage: 400 Gbps, about 50 servers' networks instead of 1,000. Each miss waits one extra trip to storage before it starts: fine for a song heard once a week.

9. Deep dive: instant start

On a weak 2 Mbps phone link, the whole 4.8 MB file takes about 19 seconds to arrive; even a 96 kbps file fetched whole takes about 12. Instead, fetch the first 10 seconds first: 200 KB, there in 0.8 s from an edge nearby, played while the rest follows. On a very weak link, fetch those seconds at 96 kbps and step up after.

For no gap between songs: 20 seconds before a track ends, start fetching the next one, its first 10 seconds first. Ana's playlist at 2 Mbps, time 0 when A starts:

track   fetch starts   first 10 s ready   all on the phone   plays
A       before 0 s     0 s                13.6 s             0 → 180 s
B       160 s          160.8 s            172 s              180 → 330 s
C       310 s          310.8 s            329.2 s            330 → 570 s

Waiting for a track to end would leave 0.8 s of silence between songs; fetching much earlier wastes mobile data whenever the listener skips. A skip to a track nobody fetched waits under a second.

10. Deep dive: playlists

Entries are ordered by a position. When Ana drags t5 in between songs 2 and 3 of a 200-song playlist, adding 1 to every later position is 198 updates for one drag. Give t5 a position between its neighbors instead, like 2.5: one new row, and sorting still lists the songs in order. A plain number runs out of room after about 50 inserts in the same gap, so a common design uses strings that always leave room, or renumbers a list now and then in the background. A linked order (each entry points to the next) also avoids renumbering, but reading songs 101 to 150 walks 100 links first.

A shared playlist can be edited by two people at once. Apply each edit as a small change to one entry: Ana adds t8 while Ben removes t2, and both survive. When two people change the same entry, the later edit wins. If each app saved its whole copy of the list, the last save would silently undo the other edit; a lock would make Ben wait while Ana has it open. Live co-editing, letter by letter, is a harder problem: see Design Google Docs.

11. Deep dive: offline and licensing

Downloaded songs are kept encrypted on the phone, so they can't be copied off and played elsewhere, and the key that plays them must be renewed online every 30 days. That's how the license ends: if the listener stops paying, or a song leaves the catalog, the downloads stop at most 30 days after the last check. Spotify's help pages ask premium listeners to go online at least once every 30 days to keep their downloads.

Plays made offline are sent on landing. If the reply to POST /plays is lost, the app sends the batch again; each play carries an id made on the phone, so the server keeps one play per id and the retry changes nothing:

play   track   played   paid?
a1     t4      3:00     yes
a2     t9      0:12     no, under 30 s
a3     t2      3:20     yes

Two paid plays, not four. Not retrying is no fix: when the request itself is lost, the artists aren't paid.

What breaks first

  • A big album release: millions press play in the same minute on tracks no edge has yet. Push them to the edges ahead of time.
  • The counting jobs fall behind: the queue holds the plays; royalties are monthly, so minutes of delay are fine, though the top charts lag.

Summary: the order to follow

  1. Scope: search, play at once, playlists, offline, count plays.
  2. Data: tracks with file keys, playlists with ordered entries, plays.
  3. API: GET /tracks/{id}/stream → a short-lived signed URL; POST /plays in batches.
  4. Busy path: one row, a signature, the first seconds from an edge.
  5. Numbers: about 2 PB of audio, 8 Tbps out at the busiest hour, 4 billion plays a day.
  6. Services: catalog with a search index, playlists, plays on a queue.
  7. Scale: each edge keeps the most played tracks.
  8. Long tail: 95% hits, 400 Gbps from storage.
  9. Instant start: the first 10 seconds first; the next track 20 seconds early.
  10. Playlists: positions between neighbors; the later edit wins per entry.
  11. Offline: keys renewed every 30 days; plays sent with ids.

The same steps work for any "design X" question: scope, data, API, the busy path and its numbers, services, scale, then the deep dives the interviewer cares about.

Keep going with system designInteractive lessons from the building blocks to Twitter, Uber and payments, the way interviews ask. Free to start.
Start learning free