system design · system-design
Design Instagram Stories (24h TTL Ephemeral Feed)
24-hour TTL, view tracking, friend recency ordering. Ephemeral state at billions scale.
Theory
Explanation
Intuition first, formal definition second. Skim the bullets if you already know this; read the prose if you don't.
Stories are short, ephemeral, friend-only. Lifetime is 24h, TTL store is the trick. Order by recency per friend. View tracking is heavy write but bounded by friend count.
Story create: media uploaded → variants → metadata row {user, story_id, ts, ttl=24h}. Per-user stories list sorted by ts. Friends fetch: for each friend in friend list, fetch top story batch. View tracking: per (viewer, story_id) row, used to mark seen + analytics. TTL sweeper deletes expired metadata; CDN media deleted via separate lifecycle policy.
When to use
Ephemeral content: Stories, BeReal, ephemeral status.
When not to
Permanent posts. Long-form video.
flowchart LR Up[Upload Story] --> Variants[Variants] Variants --> CDN[CDN] Up --> Meta[(Story Metadata · TTL 24h)] User([User]) --> Feed[Stories API] Feed --> Friends[Friend list] Friends --> Meta Feed --> Views[(View Tracker)] Sweeper[TTL Sweeper] -.cron.-> Meta
Key insights
- TTL is the single most important design feature.
- Stories are pull-on-read since per-user friend list is bounded (~150 close friends typically).
- View tracking is hot write, batch + async via stream.
- Order within a friend by post time; across friends often by viewer relevance.
- Media must be deleted after expiry, privacy commitment.