Skip to content
SeeItFlow
System Design · 13 chapters · ~25 min read

Designing a Social Media Platform

Instagram's defining engineering problem isn't storage or media delivery — it's the fanout problem: feed generation has to work completely differently for an account with 50 followers versus one with 50 million. This walkthrough goes from a blank whiteboard to a production-grade distributed system built around that constraint.

Mangesh MandlikLast updated:
Chapter 01

Problem Definition

We are designing a social media platform where users post photos and videos, follow each other, and expect a personalized feed of recent content from everyone they follow — fast and reliably.

The surface looks simple: post a photo, see a feed. Underneath is one of the most nuanced system-design problems in software engineering. The challenge isn't storing posts or delivering media — it's how to efficiently determine what each of hundreds of millions of users should see when they open the app, given a constantly changing social graph and a follower distribution that varies from zero to tens of millions.

Photos and videos dominate storage, processing, and network bandwidth, so media ingestion and delivery form a separate scaling problem from feed generation — this design treats both as first-class concerns rather than letting one overshadow the other.

The follower distribution is the whole story

Most users have a handful of followers. Some accounts have tens of millions. This power-law distribution means no single feed-generation strategy works for every account. That asymmetry is the design challenge — and understanding it is what separates a thoughtful answer from a template one.

Interactive

Visual Design Journey

Watch a single-server photo app evolve, bottleneck by bottleneck, into a hybrid-fanout feed architecture that treats a 200-follower account and a 40-million-follower account completely differently.

Chapter 02

Requirements

Functional and non-functional requirements, and the specific design freedoms that unlock large-scale optimizations.

Functional

Create photo/video posts. Delete a post. Follow other users. View a personalized home feed. Like and comment on posts. View media quickly, globally. Support private accounts and conceptual visibility rules for who can see a given post.

Non-functional

High read-to-write ratio

Users read feeds far more often than they create posts. This is the dominant workload and drives the entire architecture.

Low feed latency

Opening the app should feel instant. Latency budgets for feed generation are tight — 100–200ms is a useful illustrative design target for this walkthrough, not a documented production figure.

High availability

Feed browsing and media delivery must survive individual service failures without a full outage.

Scalable media delivery

Photos and videos represent the vast majority of bytes served. They must be served efficiently at global scale.

Eventual consistency is acceptable

A post appearing in a follower's feed with a few seconds' delay is acceptable. Like counts that lag by a few seconds are acceptable. Strong consistency is not required for most social interactions.

Durable, asynchronous media handling

Media uploads must be durable even while processing is still in progress, processing itself should happen asynchronously rather than blocking the upload response, and the system should degrade gracefully — not fail outright — when feed, ranking, or media-processing components are unavailable.

Capacity estimate (illustrative)

These are illustrative planning numbers, not measurements of any real system. Assumptions: 500 million daily active users (DAU); 10% of DAU publish one post per day (50 million posts/day); each DAU opens the feed 8 times per day; fanout-eligible authors average 150 followers (very high-follower accounts are excluded from fanout, as in the hybrid design); an average post stores about 3 MB across the original and processed variants (a mixed photo and short-video assumption); peak traffic is 3× the daily average; 86,400 seconds per day.

QuantityFormulaResult
Posts per second50M ÷ 86,400 s (×3 for peak)about 580/s average, about 1,740/s peak
Feed reads per second500M × 8 ÷ 86,400 s (×3 for peak)about 46,300/s average, about 139,000/s peak
Read-to-write ratio4B feed opens/day ÷ 50M posts/dayabout 80 : 1
Feed-store writes (fanout)50M posts × 150 followers = 7.5B/day (upper bound: treats every post as fanout-eligible); ÷ 86,400 s (×3 for peak)about 86,800/s average, about 260,000/s peak
One 50M-follower post, naive fanout1 post × 50M followers50M feed writes — about 576 s of the whole fleet's average fanout load
New media storage50M posts × 3 MBabout 150 TB/day, roughly 55 PB/year before replication or tiering
Feed-store sizeAssumed 500 post references per feed × 16 B = 8 KB; × 500M feedsabout 4 TB
Illustrative capacity estimate — formulas use the assumptions above

What this implies: the 80:1 read ratio is why feeds are precomputed; fanout writes (tens of thousands per second) dominate post writes (hundreds per second), so the fanout queue and feed store are the write bottleneck; and media bytes dwarf feed metadata (150 TB/day versus a 4 TB feed store in total), which is why media goes direct to object storage and out through the CDN rather than through the application tier.

Chapter 03

Initial Architecture

Start simple. One user, one API, one database, one file store. Introduce complexity only when a specific, demonstrated problem demands it.

Swipe to pan
Initial ArchitectureClient → API → App Service → Database · media path separateUserAPI GatewayApp ServiceUpload SvcDatabasemetadataObj StoragemediaCDN
The simplest architecture — API, app service, database, plus a separate media path

The API gateway accepts all requests. An application service handles business logic. A relational database stores users, posts, relationships, comments. Media lives entirely separately.

Why media must be separated from metadata

Media files are large, binary, immutable after processing, and highly cacheable — exactly what object storage and CDN are designed for. Metadata is small, mutable, and relational. Storing media in a relational database wastes database resources on binary blobs that do not benefit from SQL at all. The separation is one of the most important early decisions.

This is a starting architecture, not the final one. Later chapters evolve the upload path specifically to avoid unnecessarily proxying large media through general application servers, and evolve the feed path to avoid the read-time cost of naive fanout.

Chapter 04

Media Upload Flow

What happens from the moment a user presses 'Share' to the moment their followers can view the photo or video.

Swipe to pan
Media Upload FlowUser uploads → async processing → object storage → CDNUserUpload SvcObj Storageraw filesProcessorasyncCDNraw filetrigger async jobprocessed → CDN
Media upload: client → upload authorization → object storage → processing event → media processor → CDN

A production-grade upload flow looks like: Client → upload authorization → object storage → processing event → media processor → processed variants → CDN. Rather than streaming raw bytes through the application tier, the system authenticates and authorizes the upload, issues a temporary/signed upload target, and lets the client upload directly to object storage. Once the transfer completes, the client or object storage notifies the Post Service, which creates or updates the post's metadata and triggers asynchronous processing.

Why asynchronous processing?

Resizing images and transcoding video takes real time. Blocking the upload response on that work would make the app feel slow. Asynchronous processing decouples upload acknowledgement from processing time: the user gets confirmation immediately, and processing completes in the background.

Lifecycle and variants

A post's media moves through a simple lifecycle: UPLOADING → PROCESSING → READY, with a failure path of PROCESSING → FAILED. Processing produces multiple variants — thumbnails and several sizes for photos; multiple resolutions/bitrates plus a poster/thumbnail for video — the mechanics of which belong to Media Processing Pipeline.

Chapter 05

Social Graph

The social graph — who follows whom — is the foundation of feed generation. How it is stored and accessed shapes everything downstream.

Swipe to pan
Social GraphDirected follow relationships — the foundation of feed generationUser AUser BUser CSocial Graphfollower storeFanout WkrFeed Svcfollowsfollows
Social graph: directed follow relationships, read in both directions for feed composition and fanout

Follow relationships form a directed graph: User A follows User B, and B may or may not follow back. This graph is read in two distinct directions, and both must be fast.

Following lookup

user → accounts user follows. Used for feed composition, pull-source lookups when reading a feed, and relationship signals for ranking.

Follower lookup

author → users following author. Used for fanout-on-write — the write side of feed generation.

High-degree nodes

Follower counts follow a power law. Most users have a few hundred followers. Some accounts have tens of millions. This asymmetry is the source of the hot-user problem — the central architectural challenge in this story.

Chapter 06

Feed Generation

The central system-design problem: a user opens the app. How does the system determine what posts they should see?

Naive approach: fanout-on-read

The simplest implementation: on every feed request, query the social graph for the user's followed accounts, fetch recent posts from each, merge and rank, return. This works at small scale. As the number of followed accounts grows into the hundreds or thousands, read-time composition must retrieve recent candidates across many followed accounts on every request — causing the work per feed open to grow with follow count, and aggregate feed traffic to grow with total active users.

Chapter 07

Fanout-on-Write

Pre-build each user's feed at write time, so reads are trivially fast.

Swipe to pan
Fanout-on-WritePost created → queue → workers → write to each follower's feed storeCreatorPost SvcQueueWorkersfanoutFeed Storeper-userO(1) readO(followers) write
Fanout-on-write: post → queue → workers → write to each follower's feed store

When a post is created, the Post Service puts an event on a queue. Fanout workers consume the event, look up the follower list from the social graph, and write a reference to the post into each follower's pre-built feed store. When a user opens the app, they just read from their feed store — one query, instant response.

Benefit

Reading the precomputed recent feed becomes a bounded lookup independent of the number of accounts the reader follows. Latency is low and predictable.

Cost

Write amplification: one post becomes N feed writes, where N is the poster's follower count. For normal users, this is manageable. For accounts with millions of followers, this explodes.

Chapter 08

The Hot User Problem

A celebrity account creates a post. The fanout-on-write strategy produces write operations proportional to that account's follower count — for an account with tens of millions of followers (an illustrative example, not a specific documented figure), this can be enough to overwhelm the worker fleet.

Swipe to pan
The Hot User ProblemOne celebrity post → 40M fanout writes → queue explodes⭐ Celeb40M followersPost SvcQueueOVERLOADEDWorkers99% load40M events queuedhours of backlogNormal users' feed updates are starved behind this backlog
A single hot-user post floods the fanout queue with millions of writes

With fanout-on-write, one post from a very high-follower account requires a proportionally huge number of writes to the feed store. At any realistic posting frequency, the aggregate write load from a handful of such accounts can consume worker capacity, increase queue lag, overload feed-store writes, and delay ordinary users' posts from propagating on time.

The queue depth grows to millions. Workers run at 100% load processing a single user's post. Other users' fanout is delayed. The architecture that works beautifully for normal users becomes catastrophic at the extreme end of the follower distribution.

Chapter 09

Hybrid Fanout

Neither fanout-on-write nor fanout-on-read is universally optimal. The production answer is a hybrid that uses different strategies for different account types.

Swipe to pan
Hybrid FanoutNormal users: fanout-on-write · Hot users: fanout-on-read · Feed Service merges bothNormaluser⭐ Celebhot userPost SvcQueueDatabasepost storeWorkersFeed StoreFeed Servicecomposes feedfanout-on-writeskip fanoutmerge + rank → final feed
Hybrid fanout: normal users use fanout-on-write; hot users are fetched at read time

Normal users (low follower count): fanout-on-write. Pre-build feeds. Fast reads. The write amplification is small and manageable.

Celebrity accounts (high follower count, above a threshold): skip fanout-on-write entirely. When any follower opens their feed, the Feed Service identifies which followed accounts are 'hot', fetches their recent posts directly from the database, and merges them with the pre-computed portion of the feed.

Feed composition

Pre-computed feed entries (from normal accounts) + live fetch of hot-user recent posts + ranking = final feed.

Hybrid Fanout Strategy covers strategy transitions and adaptive classification beyond a single follower-count threshold in depth.

Chapter 10

Feed Storage & Caching

Feed data is stored and cached across distinct layers, each with a different lifecycle and consistency property.

Three layers are worth distinguishing rather than treating 'the feed cache' as one thing. The feed store holds derived/materialized candidate state — a time-ordered list of recent post IDs/references per user, built by fanout workers. The post/object cache caches post metadata during hydration, avoiding a database round trip for posts that were just fetched. The assembled/ranked feed cache caches the final, ordered result of composing and ranking a feed for a short period, so a repeated request within that window is served instantly.

The feed store's window can be bounded — for illustration, keeping something like the last few thousand entries per user — but this is an illustrative bounded-window example, not a claim about any specific platform's real implementation.

Eventual consistency

A new post appearing in a feed after a short delay is not a bug — it is an accepted trade-off in exchange for dramatically reduced read-time work.

Chapter 11

Likes & Comments

High-frequency write interactions — likes and comments — must be handled without blocking the main feed experience or creating a database hotspot.

Likes: relationship vs. count

A like has a logical relationship — this user liked this post — which can be modeled as a uniqueness key of (user_id, post_id) so a retried like request does not create multiple likes. This is separate from the aggregate count shown on the post: a viral post should not require every single interaction to synchronously update one relational counter row. Fast, sharded counters absorb the write volume, with asynchronous aggregation reconciling them into a durable total over time.

Comments

Comments are separate from the main feed path. They require their own pagination at scale, and a feed response typically carries only a preview or a count rather than the full comment thread — detailed comments are fetched separately when a reader expands a post.

Chapter 12

Post Lifecycle & Feed Correctness

Before assembling the final architecture, it's worth naming a problem that fanout alone doesn't solve: a feed entry can become invalid long after it was written.

A post can be deleted. An account can become private or be suspended. Two users can block each other. A reader can unfollow the author. The underlying media can fail or be removed. In every one of these cases, a feed entry that was already fanned out to a follower's feed store may no longer represent something that follower should actually see.

Post Lifecycle, Privacy & Visibility covers the full serving-path mechanics behind this idea.

Chapter 13

Final Architecture

The complete system — every component earns its place by solving a specific, demonstrated problem.

Swipe to pan
Final ArchitectureComplete system — every component earns its placeUserAPI GatewayPost SvcUpload SvcDatabaseObj StorageCDNSoc. GraphQueueWorkersFeed StoreFeed SvcFeed Cache
Complete architecture: media path, social graph, hybrid fanout, feed composition, caching, interactions

Media write path

Client → upload authorization → Object Storage → processing queue → Media Processor → processed variants → CDN.

Post / feed write path

Post Service → metadata store → fanout queue → workers → Social Graph → Feed Store.

Feed read path

Feed request → Feed Service → Feed Cache / Feed Store + pull sources → filtering/dedup → ranking → hydrate post/media metadata → response.

Interaction path

Likes and comments follow their own separate service and storage paths rather than being routed through the feed service — they scale differently and should not add latency to feed reads.

Key takeaways
  1. 01API Gateway routes all client traffic to appropriate services
  2. 02Upload authorization + direct-to-object-storage transfer keep large media off the application tier
  3. 03Media Processor turns raw uploads into READY variants asynchronously
  4. 04Object Storage + CDN serve media globally with minimal origin load
  5. 05Post Service writes metadata to the primary database as the durable source of truth
  6. 06Social Graph Service serves both follower and following lookups for fanout and feed composition
  7. 07Fanout Queue + Workers distribute posts to normal users' feed stores, with batching, checkpointing, and idempotent writes
  8. 08Hybrid fanout excludes high-fanout accounts from push and fetches their posts at read time instead
  9. 09Feed Service composes precomputed + pulled candidates, filters, dedups, and ranks
  10. 10Feed Cache and Feed Store hold derived/rebuildable state, distinct from durable sources of truth
  11. 11Post Lifecycle handling filters deleted/blocked/private content at serving time, with async cleanup after
  12. 12Like Service + Comment Service handle interactions with sharded counters, without blocking the feed path