Design photo publishing, processing, and a personalized home feed.
Interview scope and guarantees
Upload photos, publish posts, follow accounts, read a feed and like posts. Media can process asynchronously; a visible post must reference a ready media version. Private-account access must be checked independently of cached feed membership.
Capacity worksheet
Assume 20 million uploads/day at 3 MB: 60 TB/day input. Four derivative sizes totaling another 3 MB add 60 TB/day. At 200 million feed opens/day and 20 images/open, there are 4 billion image fetches/day; byte delivery belongs at CDN edges.
Concrete API contract
POST /media/uploads -> {mediaId,uploadUrl}
POST /posts {mediaIds,caption,visibility} -> {postId}
PUT /posts/{id}/likes/me -> 204
GET /feed?cursor=...Data model and access paths
media(id PK,owner_id,state,source_key,derivative_manifest)
posts(id PK,author_id,visibility,created_at,version)
likes(post_id,user_id); UNIQUE(post_id,user_id)
feed_items(user_id,sort_key,post_id)Evolve a solution and explain each change
Figure — Three architecture decisions for Instagram, including the pressure each introduces.
Step 1: Publish media
Store an immutable media object and commit an authorized post record. A completed upload alone does not make a post visible.
Step 2: Project feed candidates
Fan out ordinary accounts and merge high-fanout accounts at read time. Fanout delays and duplicate events complicate feed completeness.
Step 3: Deliver media privately
Use renditions and CDN delivery while enforcing current post visibility. Cached thumbnails can leak after permission changes.
Responsibility overview
Figure — Connected responsibilities for Instagram. Trace the authoritative and derived paths separately.
Worked end-to-end scenario
A creator uploads photo object o31, then publishes post p31 referring to it. If the publish transaction fails, o31 remains an unlisted upload eligible for safe later cleanup. After publication, the outbox updates follower candidates. A follower receives the candidate twice; the post ID makes projection insertion idempotent. Opening the feed rechecks current audience membership before issuing media grants. If the creator becomes private or removes the follower, an old feed candidate and cached thumbnail must not bypass the new policy. Likes and comments are separate records with their own uniqueness and moderation semantics.
Why these access paths matter
Posts are indexed by author and publication order; media keys include immutable rendition identity. Feed candidates are indexed by recipient. A unique user-plus-post like prevents duplicate likes, but a displayed count can be an asynchronous aggregate. Store comment pagination keys explicitly; do not use the current like count as an ordering identity.
Build the baseline first
- Upload → object storage → derivative workers.
- Ready media → post transaction → publish event.
- Feed candidates → authorization → media grants.
Evolve the design under load
- Hybrid feed fanout → inbox and author timelines.
- Immutable derivatives → CDN caches.
- Like events → counter projection → display.
Defend the hardest decision
Do not publish a post just because an upload URL was issued. Verify the object, scan and transform it, then move media state to ready. Publishing checks ownership and readiness. Like is a set membership operation; using PUT makes repeated requests naturally idempotent. A displayed like count may lag while the user’s own like state comes from the authoritative relation.
Failure and recovery analysis
A user changes an account to private after its posts were fanned out widely. Inbox cleanup alone is too slow to enforce the boundary; filter access before returning metadata and media grants. Cached public media URLs complicate revocation, so the privacy promise and CDN authorization design must agree.
Security and privacy boundary
Validate file types and size, strip sensitive image metadata where appropriate, and prevent unauthorized media reuse.
Interview follow-ups with reasoning
Question: How do you delete a post?
Show answer and explanation
Answer: Tombstone source state, suppress serving, remove projections, then collect media after retention.
Question: How do you handle a viral image?
Show answer and explanation
Answer: Edge cache it while protecting metadata and counter paths.
Question: What happens when derivative generation fails?
Show answer and explanation
Answer: Show processing failure and allow a retry without duplicate posts.
Operate and verify the design
Media processing age, failed derivatives, feed freshness and private-content suppression.
Switch a public account to private during cache hits and verify that new access grants are denied.
A second scenario to test transfer
An ordinary author with 800 followers writes a post; precompute candidates. A celebrity with 30 million followers posts at once; defer that author’s fanout and merge recent posts for active readers. In both cases, the feed candidate is re-authorized before content is shown. One policy handles different fanout economics without weakening privacy.
A feed worker retries an old post event after the author has gone private. What prevents it from reintroducing visible content?
Show answer and explanation
Answer: Write candidates idempotently with the source version and treat them as references only. At read time, check current audience state before returning content or URLs. Propagate removals and reject stale versions to reduce work, but preserve the current authorization check as the visibility boundary.
Compare alternatives
| Choice | Benefit | Cost |
|---|---|---|
| Direct multipart media upload | API does not stream huge bytes | Session verification and cleanup |
| Hybrid fanout | Handles author skew | Read merge and versioning |
| Immutable media renditions | CDN reuse | Lifecycle retention |
| Visibility recheck | Protects stale candidate lists | Authorization read on feed path |
A design-changing exercise
The database denies a former follower but the CDN has a cached thumbnail. Is privacy enforced?
Show answer and explanation
Answer: Only if delivery checks authorization or a valid scoped grant. Cache residency does not grant access; define existing grant expiration separately.
Design workshop: publish media separately from feed candidates
Core scope is media posts, follow feed, likes/comments and private visibility. Stories, live video and recommendation training are extensions. Assume feed p95 under 200 ms and uploaded images playable within ten seconds under admitted processing load. Bytes uploaded is not post published; feed candidates are not permission to see media.
Add media(media_id,owner_id,source_key,generation,state), post_media(post_id,media_id,ordinal), follows(follower,author) with reverse author index, comments(id,post_id,author_id,request_key,state), and like_state(post_id,user_id,version,liked). Comments and likes carry stable request identities; current post visibility governs admission and reads. Post creation atomically attaches verified ready media and emits an outbox event. It never exposes a raw unverified upload as the final thumbnail.
Figure — Media readiness before post fanout.
Transforms validate type and dimensions, strip unsafe metadata under the chosen privacy policy and generate pinned sizes/codecs. Task identity is media/source_version/profile/generation; conditional publication rejects stale workers. Retained renditions use immutable paths. Cleanup needs live post/media references and a deleting state, not only old timestamps.
A hybrid feed pushes ordinary authors and pulls high-fanout authors during read. With one million followers and four posts/day, push produces four million candidate writes/day for one account. If only 5% read five times/day, pull creates 250,000 author merges/day, with different per-operation cost. Use measured thresholds and workload limits. The ranking service merges pushed IDs, pulled timelines and explicitly scoped recommendation candidates, then applies current ACL before hydration.
Figure — Like/unlike transitions and private delivery.
Counter events contain old/new state and source version. A projector tracks the last applied version per like key, or recomputes on gaps; blindly incrementing for every queue delivery inflates counts. Comments use a durable edit/delete version with current authorization before snippets.
Private media delivery uses an edge/origin authorization boundary. A post going private invalidates controlled grants and caches, but already issued bearer URLs remain usable under their defined lifetime unless the delivery layer checks current permissions. Separate public and private cache policy; changing the application feed alone does not protect a directly accessible object URL. A CDN miss coalesces protected origin reads; the app API does not stream all image bytes.
Exercise: A like version 3 is delayed until after unlike version 4. The counter worker receives 4 then 3. What should it do?
Show answer and explanation
Answer: Resolve the state using source versions; ignore 3 after 4. If a gap prevents a valid delta, recompute that like's current source state and reconcile the aggregate. Final liked state is false, not the last arriving event.