Spotify
Music Streaming Platform
Design a music player and its playlists: collaborative playlists that stay consistent under concurrent edits, a queue with shuffle, repeat and previous, pluggable shuffle strategies, and event-fed listening history.
Overview
A music streaming app has a catalog of tracks, playlists that users build and share, and a player that plays a queue of tracks with shuffle and repeat. In a low level design interview the scope is the object model: playlists and who may edit them, the player's state and queue, how shuffle and repeat interact, and how listening history is recorded.
The problem tests three things. First, whether you separate the playlist (shared, edited by several people) from the player's queue (a private copy, reordered by shuffle). Second, whether the player's rules are precise: what 'next' does at the end of the queue under each repeat mode, what 'previous' does halfway through a track, and what turning shuffle off restores. Third, whether shared playlists stay consistent when two people edit at once.
A strong answer draws the playlist and the player as separate classes, states each control's rule in a table, makes shuffle a Strategy, and uses a version number so a stale reorder is refused instead of silently applied.
Scoping the problem
- Is the queue the playlist itself, or a copy? — Editing a playlist while it plays — A copy made at load time
- Who may edit a playlist? — Permissions and concurrency — The owner; collaborators on a collaborative playlist
- Which repeat modes? — Changes what next does at the end — Off, all, one
- Does repeat one also hold on a manual skip? — A frequent edge case — No: repeat one replays only when a track ends by itself (a choice to state)
- What does previous do mid-track? — Players differ — Restart the track if more than 3 seconds in, else go back (a choice to state)
- What must shuffle restore when turned off? — Keeps the user's place — The original order, still on the current track
- Search, recommendations, audio streaming, downloads? — Separate systems — Out of scope; history is recorded for them
The rules of each control
- Track ends (tick passes its length) — Repeat one: play it again. Otherwise the same as next
- next — Next index; at the end, wrap to 0 under repeat all or one, stop under repeat off
- previous — More than 3 seconds in, or on the first track: restart. Otherwise go back one
- shuffle on — Current track first, the rest ordered by the ShuffleStrategy
- shuffle off — Back to the original order, on the same entry, same position. The player keeps a permutation of positions, so a track that appears twice is never confused with its copy
- load — Copy a snapshot of the playlist; later edits do not change the queue
The demo drives the player through these rules on a four-track album; History, a listener, records each track that starts.
- 1 — load(album) — t0 — t0
- 2 — tick(180): t0 ends — t1 — t1
- 3 — tick(10), previous — t1 restarted (10 s is past 3 s) — t1
- 4 — previous at 0 s — t0 — t0
- 5 — repeat one, tick(180) — t0 again — t0
- 6 — next (manual) — t1 — t1
- 7 — shuffle on, then off — t1, original order restored — nothing
- 8 — repeat off, next three times — t2, t3, then stopped — t2, t3
Collaborative edits
Two people edit a shared playlist. Appends are safe under the playlist's lock. A move is different: 'move the track at position 0 to 1' means something only for the version the user was looking at. If someone added a track since, the positions may point at different tracks. The code asks the caller for the version it saw and refuses a stale one.
What you will be able to do
- Separate a shared, versioned playlist from the player's private queue.
- State the exact rule for next, previous, repeat and shuffle, and test each one.
- Make shuffle a Strategy and add a strategy that plays recent tracks later.
- Keep collaborative playlists consistent with a lock for appends and a version check for moves.
- Record history as a listener on player events, not as code inside the player.
Functional requirements
- Playlists: add(user, track) and move(user, src, dst, expected_version); only the owner, or collaborators on a collaborative playlist, may edit
- Player: load(playlist) copies a snapshot into the queue and starts playing
- Controls: pause, resume, next, previous, and tick(seconds) for time passing
- Repeat modes off, all and one; repeat one replays only when a track ends by itself
- Shuffle on keeps the current track first; shuffle off restores the original order on the same track
- Shuffle order comes from a pluggable strategy; one strategy plays recently heard tracks later
- Listeners receive player events; History records every track that starts
Non-functional requirements
- Concurrent edits never lose a track: the playlist lock guards every change
- A stale reorder is refused with Conflict, never applied to the wrong track
- The queue is a copy, so editing a playlist never disturbs what is playing
- A new shuffle rule is a new ShuffleStrategy; the player does not change
- Deterministic in tests: time is passed to tick and shuffles take a seeded random generator
Interview questions
- Users say shuffle is not random: they hear the same songs again. What do you do?
- Two collaborators reorder the same playlist at the same time. What happens?
- Someone edits the playlist while I am listening to it. Does my queue change?
- Where does listening history live, and how do other features use it?
Sign up free to read the full topic
More Low-Level Design topics
Start free on Cariara · Coding problems · System design studio