Designing a Photo-Sharing Platform: The Instagram-Scale Challenge
This article explores the system design for a photo-sharing platform similar to Instagram, where users upload images, follow each other, and consume personalized feeds. The discussion draws on an architectural approach presented in a guest post by Ankit Sirmorya, a Machine Learning Lead at Amazon.
Functional Scope
Before diving into architecture, it's critical to define what the system handles. The core features required are:
- Photo upload and personal gallery access.
- Social graph management for following users.
- Aggregated, personalized feed generation from followed users.
- Engagement mechanics such as likes and comments.
Deliberately excluded from this design are direct messaging between users and ML-based discovery recommendations (suggesting new people, photos, videos, or stories).
Foundational Architecture
Upon receiving any client action (e.g., a new post or like), the server engages in two concurrent processes. It first persists the data in its primary store. Simultaneously, it publishes the action to a streaming data service for pub-sub consumption. Downstream micro-services—such as the User Feed Service or Media Counter Service—subscribe to this stream to perform their domain-specific tasks. This pattern yields a highly extensible architecture, allowing additional consumers (like search indexing services) to be appended without modifying core logic.
Core System Components
The design relies on a micro-service topology with specialized data layers:
- Graph Database (e.g., Neo4j): Chosen for the inherent complexity of social relationships. Data entities—users, posts, comments—are nodes, while relationships like "follows" and "likes" are the edges connecting them.
- Columnar Store (e.g., Cassandra): Utilized for handling high-write, high-volume data pertaining to user feeds and activity counters.
- Streaming Data Model (e.g., Amazon Kinesis, Azure Stream Analytics): Used for processing real-time actions (like, follow) and feeding them to asynchronous services.
Managing the Posting Pipeline
When a user publishes a photo, the operation bifurcates into immediate workflows and deferred ones.
Synchronous Process: The image is first uploaded to file storage. Following that, media metadata is written to the graph data store. Upon this, a confirmation is sent back to the user, and a trigger is generated to update their activity record.
Asynchronous Process: Actionable data is persisted in a columnar data store, which triggers the "pre-computed feed" fan-out. The fan-out mechanism here is targeted: it only calculates feeds for followers of non-celebrity accounts (those with a few thousand followers). For celebrities with over a million followers, pre-computing the feed is immensely compute- and I/O-intensive; instead, their content is fetched on-demand at view time.
This dual-path mechanism for "normal" vs. high-follower accounts is a textbook extreme scale pattern: push on write for small fan-outs, and pull on read (or lazy fetch) for large ones.
API Design for Posting
For the upload call, the client and server communicate using the multipart/form-data content type. This encoding distributes the image file payload and the textual metadata across different parts of the request body.
The Pre-Compute Feed Job
For non-celebrity uploads, the asynchronous flow runs as follows: a message placed in the User Feed Service Queue triggers the orchestration. The service calls the Followers Service to fetch the user's follower list. The new post (parent ID) is subsequently inserted into a unique feed list for each of those followers within the Cassandra cluster. This ensures that for the majority of users, the home screen has a pre-baked set of items ready to retrieve instantly.
Fetching a User Feed
On read—when a user opens the app to refresh their home screen—the User Feed Service dispatches two parallel query threads to optimize latency:
- Thread 1: Fetches feeds aggregated from non-famous users via the pre-computed fan-out cache.
- Thread 2: Simultaneously attempts to pull fresh, recent posts from any followed celebrity accounts in real-time.
The service thread then merges these two result sets (typically sorting them by recency/rank) and presents the unified feed back to the requesting client.
Feed Retrieval API Design
The view feed interface is designed to return these merged and final results to the mobile/web client for display to the end user.
Data Models and Storage Logic
Graph Data Models for Relationship Traversal
In Neo4j, all data resides as nodes. Critical lookups require primary index support. These nodes are coalesced into a single nodeIndex registry to accelerate relationship traversals. This model exceeds relational database hierarchy limits (beyond two depths) and benefits from a schema-less design to allow rapid iteration without downtime for schema changes.
Access Patterns Supported by Graph DB queries:
- Fetching all followers of a given user: Queries incoming
FOLLOWSedges to collect originating nodes. - Fetching all posts made by user: Retrieves the target nodes attached via outgoing
POSTSedges. - Filtering nuanced connections: Discovers posts by User A where User B commented by intersecting the outgoing
COMMENTSedges of User B with all incomingCOMMENTED_ONrelationships of User A's posts.
Columnar Models for Feed Storage
Cassandra is used to store the feed and activity aggregates. The model maps a User_id directly to a collection/list of post IDs (a sorted list for feeds). A TTL (time-to-live) mechanism can be applied at the row level to automatically evict stale posts after a specific duration, enforcing a bounded and performance-efficient environment.
The Streaming Data Denormalization
The streams support both data snapshot containers (Users and Posts) and discrete action event types (interactions). Crucially, these are denormalized specs to ensure consumers don't need to join multiple tables. For instance:
- Entities A & B: Represent the base denormalized snapshot state of User and Post records.
- Entity C (
LikeEvent): Represents when a user interacts with a media item. The Media Counter Service subscribes and updates the like metrics in the storage grid. - Entity D: Represents the follow action, conveying the follower and the followee IDs.
This instantly informs specific dedicated micro-services to accurately maintain auxiliary data, moving logic off the critical path latency of the client request.
Optimizations
To enhance performance and content relevance, the design layers specific caching and ranking strategies. Feeds that are rolled up through computation are stored in a distributed cache with an LRU (Least Recently Used) eviction policy. This reduces latency and prevents redundant feed re-computation for highly active sessions.
Beyond latency, the quality of content in the home screen dictates user retention. Ranking algorithms assign a representative score to content from the merge step above, estimating likely user engagement (vis-à-vis shares, likes, and clicks). These models represent each feed item using high-dimensional feature vectors that include:
- Who the user frequently engages with socially.
- Staleness and cross-network interaction patterns.
- Legacy interests from past activities (viewing or liking similar subjects).
Model complexity often employs deep neural network stacks to process massive feature vectors (100K+ density). The presentation emphasizes that historically, decisions were seeded by more classic models (decision trees, logistic regression), followed by empirically beneficial pivots to deep learning architectures as the engineering team matured.



