Ephemeral messaging and stories platform inspired by Snapchat: media in object storage behind a CDN, expiry that deletes content once opened, realtime chat with push for offline users, friend-graph privacy checks and 24-hour stories. A teaching design, not Snap's internal architecture.
See why storing media in database rows and deleting nothing fails both scale and the ephemeral promise.
What was missing: Nothing yet: one server receives, stores and serves every snap.
Why that's risky: Megabytes of media per row make the database the bottleneck at once, and nothing deletes opened or expired snaps, so 'ephemeral' is only a promise.
What gets added: An app server and a database that keeps each snap's photo or video in the same row as its details.
Trade-offs: Easy to build, but it mixes small hot records with huge cold files.
Keep media in object storage behind a CDN, and make expiry an actual deletion, not a flag.
What was missing: Media lived in database rows and nothing ever deleted opened or expired snaps.
Why that's risky: Chats and new-snap alerts arrive only when the app polls, and offline users never hear about them.
What gets added: The app uploads media straight to object storage with a signed URL; recipients fetch it through a CDN with short-lived signed URLs. The message store keeps only small records with a TTL, and expiry jobs delete both the file and the record once every recipient has opened it or it times out.
Trade-offs: Deleting on open means a missed delete leaves content around; a TTL on the record and lifecycle rules on storage act as a second safety net.
Deliver chats and snap alerts instantly to online users and by push notification to offline ones.
What was missing: No persistent connection, so chats and alerts depended on polling, and offline users got nothing.
Why that's risky: Nothing checks friendships or blocks before delivering, so anyone could message anyone.
What gets added: Chat gateways holding persistent connections, a chat service that stores messages in order per conversation, a per-user delivery channel to the gateway holding the recipient's connection, and a push worker that notifies offline users through APNs and FCM.
Trade-offs: Delivery is at-least-once: clients deduplicate by message id rather than the server trying to deliver exactly once.
Only friends can reach each other, and blocks take effect everywhere.
What was missing: No friend relationships or blocks: delivery didn't check who was allowed to reach whom.
Why that's risky: There is no way yet to share a story with all friends at once.
What gets added: A friend graph service for requests, accepting, removing and blocking; a friend graph database; and a cache of friend and block lists that every snap and chat checks before delivery.
Trade-offs: The friend cache can be a moment stale; blocking also invalidates it immediately so a block is never delayed.
Post once to all friends for 24 hours, and show each user a tray of friends' active stories.
What was missing: Content could only go to chosen recipients one at a time; there was no broadcast to all friends.
Why that's risky: Uploads aren't checked for abuse before delivery, and there is no visibility into engagement or the system's health.
What gets added: A story service that posts a story once with a 24-hour TTL and records viewers, plus a story tray cache per user built from friends' active stories.
Trade-offs: Trays are built when friends post (fan-out on write) because opening the app and reading the tray is far more frequent than posting.
Check uploads before delivery, collect engagement without slowing the app, and watch the system's health.
What was missing: Uploads went out unchecked and unprocessed, and nothing measured engagement or system health.
Why that's risky: Remaining risk is regional: a full region outage needs failover to another region's copies of the data.
What gets added: An upload queue and media processing workers (transcoding, thumbnails, safety classifiers) that run before recipients can open content; an engagement stream feeding a warehouse in micro-batches for streaks, ranking and creator insights; and monitoring.
Trade-offs: Safety checks add a short delay before a recipient can open a video, in exchange for blocking abuse before it spreads.
Use storage lifecycle policies + periodic sweepers; enforce access tokens with expiry; delete metadata+blobs on schedule.
Keep a server-side snap lifecycle policy that evaluates message type, chat settings, open state, replay counters, and save permissions. Clients render based on policy decisions, but the source of truth stays on the server.
Memories have different retention, backup, access, and privacy guarantees than ephemeral delivery blobs. Separating them prevents accidental deletion coupling and makes encryption, restore, and legal retention behavior easier to reason about.
Model them as different edge types or stateful relationships in the social graph. This lets the same graph support request flows, access control for private stories, and delivery eligibility checks without duplicating logic across services.
Keep both a unique-view model and a repeat-view event stream. Use the viewer store to track first-seen order and creator-visible viewer lists, while analytics pipelines separately aggregate repeat views, completion rate, and dwell time.
Chat needs low-latency person-to-person delivery and receipt tracking, while stories are high-read fanout content with different caching, ranking, and expiry patterns. Splitting them keeps data models and scaling strategies clean.
Use conversation-scoped sequence numbers, idempotent client acknowledgements, and durable inbox state so reconnecting clients can request only the delta since their last confirmed cursor.
Keep chat delivery separate from streak accounting. Emit lightweight message-open or send events, update streak counters asynchronously in a fast counter store, and periodically reconcile counters into durable storage.
Discover behaves more like a publisher feed with ranking, editorial controls, regional availability, and content shelves. Stories are mostly friend-graph fanout with expiry. They share some infrastructure, but not the same product logic.
Friendship edges, blocks, best-friend signals, private story audiences, and recommendation inputs evolve independently from chat delivery. A dedicated friend graph service/store keeps access checks, social features, and privacy logic reusable across chat, stories, and Discover.
Use hybrid fanout. Precompute trays for normal users, but for high-fanout publishers use fanout-on-read or partial materialization with cached shelf fragments. Track publisher fanout cost and dynamically choose the strategy.
Use feature flags with cohort targeting, region-scoped rollouts, kill switches, and observability tied to both technical SLOs and product metrics. New lenses, ranking models, or UI changes should be gradual and reversible.
Partition the event backbone by domain and key: chat by conversation, stories by publisher/user, engagement by content/user, and abuse signals by account/device. This keeps replay targeted, consumers isolated, and hot partitions manageable.
You're in the middle of an interview session. Leaving now will end your current attempt.
Explore concept overviews, real-system examples, key tradeoffs, and interview talking points for each roadmap section.
You've conquered this phase. These are the skills you now own: