Design a Photo Album App
Company: Robinhood
Role: Frontend Engineer
Category: System Design
Difficulty: medium
Interview Round: Onsite
Design a web-based **photo album application** for the browser (a frontend-focused system design).
The product has three main screens, each provided to you as a design mockup:
1. **Album list page** — a grid/list of albums. Each album shows a cover photo and the album name.
2. **Album detail page** — the photos inside a selected album. From here a user can upload new photos and rename the album.
3. **Photo info page** — details for a single selected photo (e.g. location, capture time).
The core functional requirements are:
- View all albums.
- Open an album and view its photos.
- Rename an album.
- Upload one or more photos into an album.
- Changes propagate across the user's devices in **near real time** (e.g. renaming an album on a laptop updates an open tab on a phone within seconds).
- Photos (and albums) can be **sorted by city**.
- Sorting supports **descending** order.
This is a **Frontend Engineer** interview, so depth is expected on the client: component/data architecture, state management, normalization, the real-time sync mechanism, and the upload flow. Design the server and API only as far as needed to support the client. Be concrete about the client data model and where each piece of state lives — the interviewer will push on trade-offs at every step, and (per the original loop) will push *especially* hard on not keeping two sources of truth.
```hint Where to start
Map each of the three screens to the data it needs, then ask the load-bearing question: is any album or photo object represented in **more than one place** (e.g. once in the album-list view and again inside the album-detail view)? That duplication question is the heart of the "state management and data normalization" discussion the interviewer cares most about.
```
```hint Normalization — push on duplication
If the same album or photo can be edited from two screens, ask yourself: when a sorted-by-city list is shown, is that ordering something you *store* and have to keep in sync, or something you *recompute*? Notice the original loop's tell — the interviewer nudged toward `useMemo` and away from `useEffect`. What does that distinction imply about where a derived view should live?
```
```hint Real-time + upload
For sync, don't jump to a transport — first characterize the traffic: which way do most messages flow, and what must happen when a device was disconnected while something changed? Let those two questions select among the options and decide whether you need a recovery path. For upload, trace where a multi-MB binary actually travels relative to your API tier, and enumerate the states a tile passes through between "user picked a file" and "fully processed."
```
```hint Sorting & rendering
Where should the sort be authoritative once an album is large enough to paginate — and what breaks if you sort only the page you've loaded? City sort has an awkward case: some photos have no city yet. Decide how those should behave. Separately, a single album may hold thousands of tiles; ask what that does to the DOM and the browser.
```
### Constraints & Assumptions
State your assumptions; reasonable ones include:
- **Single-user, multi-device** is the primary scenario (a user with a laptop and a phone sees only their own albums). Design so shared/collaborative albums could be added later, but they're an extension, not the core.
- Album lists are modest (tens to low hundreds); a single album may hold a handful to **thousands** of photos, so the photo list must paginate.
- Photos are large binaries (single-digit MB each); metadata is small. Reads dominate writes.
- "Near real time" means seconds, not strict global ordering — **eventual consistency** with conflict resolution is acceptable.
- `city` is derived server-side from a photo's EXIF GPS via reverse geocoding at ingest, or `null` when unavailable — not free-typed by the user.
- The client is a modern SPA (e.g. React); binary photos live in object storage (S3 / GCS / equivalent) fronted by a CDN. Treat the API/transport as designable (REST, GraphQL, WebSocket, SSE) and justify the choice.
### Clarifying Questions to Ask
- Is this single-user-multi-device, or do albums get **shared/collaborated** on? Does that change the sync and permission model?
- How large can an album get — do photo lists need pagination and virtualization?
- Does "sort by city" sort the photos *within* an album, the album list, or both? Must it sort the entire paginated set or only loaded photos?
- Where does `city` come from — server-side EXIF/geocoding or client-supplied?
- How strict is "real time" — sub-second or a few seconds? Is offline / queued-while-offline editing in scope?
- Are photos private (signed URLs / auth-gated) or can the CDN serve them publicly? Is there an existing data-fetching library to use, or is that a free choice?
### What a Strong Answer Covers
- A clean component-to-data mapping for all three screens and an explicit statement of the **single source of truth**.
- **Normalized** client state (entities by id + relationship id-lists) with derived views via **memoized selectors** — and an explanation of *why* (it eliminates the two-sources-of-truth consistency bugs the interviewer probes).
- A coherent API surface backing the screens: list albums, a **paginated + sortable** photo list (`sortBy`/`order`/cursor), rename with a concurrency guard, and a **two-step upload** (create-session → complete).
- A scalable upload flow: optimistic UI placeholder, **pre-signed direct-to-storage** upload, async processing (thumbnail / EXIF / city), and a "ready" event back to the client.
- A justified real-time mechanism (WebSocket vs SSE vs polling) **plus** a missed-event recovery story (reconnect + reconcile).
- A clear decision on **where sorting happens**, how it composes with pagination, and how `null` cities are ordered.
- How a grid of thousands of photo tiles actually renders (windowing/virtualization) without choking the browser.
- Concurrency/conflict handling on rename (version field / optimistic concurrency vs last-write-wins) and the trade-off chosen.
- Loading / empty / error / upload-in-flight UI states and a caching/invalidation strategy.
- **Explicit trade-offs** at each decision point with a defended recommendation — not just a list of options.
### Follow-up Questions
- A user uploads 200 photos at once on a slow connection. How do you keep the UI responsive, bound concurrency, show per-file progress, and recover from partial failure?
- Two devices rename the same album within the same second. Walk through exactly what each device sees and what's persisted under your conflict strategy.
- Some photos have no city yet (still processing). How should they appear in a city-sorted, paginated list, and how does the order stabilize once processing completes?
- Your WebSocket drops for 30 seconds and a rename plus three uploads happen during the outage. How does the client end up consistent again without a full reload?
Quick Answer: This question evaluates proficiency in frontend system design, specifically client-side component and data architecture, state management and normalization, real-time synchronization, and large-media upload flows; it sits in the System Design domain with a Frontend Engineer focus and tests practical application of these competencies.