Design a Played-By Summary System
Company: Roblox
Role: Software Engineer
Category: System Design
Difficulty: hard
Interview Round: Technical Screen
Design the backend system that powers a single line of social proof on a game's detail page, such as:
> **Alice, Bob, and 12,345 people have played this game.**
This line is **personalized per viewer**. For a given (viewer, game) pair, the system must:
- Show up to a small number ($N$, e.g. 2) of the **viewer's friends** who have played the game (rendered by name).
- Show the **total number of unique players** who have ever played the game.
- Treat the numeric count as a **unique player count** — each user contributes at most 1, no matter how many sessions they have logged. It is **not** a raw session or play-event count.
Design the end-to-end system: how play events are recorded, how unique players are counted without double-counting, how the viewer's friends-who-played are resolved at read time, and how the whole thing is served fast on a high-traffic game page. Cover functional and non-functional requirements, the data model, APIs, the read and write paths, consistency, scalability, and edge cases.
```hint Decompose the line
The line is two independent sub-problems glued together: (1) a **per-game global aggregate** (total unique players) and (2) a **per-(viewer, game) personalized intersection** (which of *my* friends played this). They have different access patterns — solve them separately.
```
```hint Deduplicating the count
One user typically emits many play events for the same game across multiple sessions. What property of a storage write could make the "first ever play" of a (user, game) pair detectable exactly once, even when events can be replayed or retried? Think about what data structure or constraint would guarantee that a repeat event has no effect on the counter.
```
```hint Hot counters and the friends-who-played join
For a viral game, many events converge on a single game's aggregate at the same time. What techniques exist to spread that write pressure — and what happens to exactness when you apply them? For the friends intersection, consider the relative sizes of the two sets you are joining and which direction you would iterate.
```
### Constraints & Assumptions
State your assumptions explicitly; reasonable numbers for a Roblox-scale platform:
- **Catalog & users:** tens of millions of games, hundreds of millions of registered users.
- **Read traffic:** game detail pages are read-heavy; assume this summary endpoint must serve on the order of $10^5$ QPS at peak, with **p95 < ~100 ms**.
- **Write traffic:** play sessions are frequent; assume up to $\sim10^5$–$10^6$ play-start events per second globally at peak.
- **Friend list size:** typically small (median tens, capped in the low thousands).
- **Freshness:** the total count and the friend list may be **eventually consistent** — lagging by seconds to a few minutes is acceptable. Exactness should hold after the pipeline catches up.
- **Delivery semantics:** the event pipeline is **at-least-once**, so events can be duplicated, retried, and arrive out of order.
- **Privacy:** friend visibility, blocks, deleted/suspended accounts, and per-user privacy settings must be honored when rendering friend names.
### Clarifying Questions to Ask
- Does the numeric count mean **all** unique players (including the named friends), or only the *remaining* players after the friends are named? (This changes whether the number is `total` or `total − visible_friends`.)
- What exactly counts as "played" — opening the game, a session of minimum length, or a completed match? Does this definition ever change retroactively?
- Must the count be **exact**, or is a small relative error (e.g. ±1–2% from an approximate cardinality sketch) acceptable for very large games?
- How many friend names do we show ($N$), and how are they ordered when more than $N$ friends qualify (closeness, recency of play, alphabetical)?
- For a logged-out viewer, do we show just the total, or suppress the module entirely?
- What are the privacy rules — can I see that a friend played, and can I render their name, given their visibility settings and our friendship status?
### What a Strong Answer Covers
```premium-lock What a Strong Answer Covers
```
### Follow-up Questions
- Your counter can drift from the true unique count over time. How do you detect drift and reconcile it back to the source of truth without taking the count offline?
- A celebrity with millions of friends/followers loads the page. The friends-intersection step now iterates a huge set — how do you keep this read fast (and bounded) without scanning everyone?
- The product wants to extend the line to "your friends who played **this week**" (a sliding window). How does your data model and counting strategy change, and what does it cost?
- A bug double-published an hour of play events. Walk through exactly why your design either absorbs this with no overcount, or how you'd repair the count if it didn't.
Quick Answer: This system design question evaluates a candidate's ability to decompose a personalized social-proof feature into distinct distributed systems problems: maintaining an idempotent unique-player count at high write throughput and efficiently computing a per-viewer friend intersection at read time. It tests mastery of event-driven architectures, deduplication strategies, hotspot mitigation, caching hierarchies, and consistency trade-offs commonly assessed in senior software engineering interviews.