Design Instagram
🎯 Explain Like I'm 5...
Imagine a magical photo album where you can share your pictures with friends, and they can like them and leave comments. Plus, you can see all your friends' photos in one place! That's Instagram!
What is Instagram?
A photo and video sharing social network where users can post content, follow others, and interact through likes and comments.
Example:
You post a photo of your lunch → Your followers see it in their feed → They can like it or comment!
🔨 How It Works (Simple Version)
- 1. Take a photo or video with your phone
- 2. Upload it to Instagram with a caption and hashtags
- 3. Your followers see it in their feed
- 4. They can like, comment, or share your post!
🎯 Key Features to Design
- ✓Upload Photos/Videos: Users can share media content
- ✓News Feed: See posts from followed users
- ✓Follow System: Follow/unfollow other users
- ✓Like & Comment: Interact with posts
- ✓Stories: Short-lived 24-hour content
- ✓Direct Messages: Private messaging
- ✓Search & Explore: Discover new content
📋 Requirements
Functional Requirements:
- • Users can upload photos and videos (max 10 photos per post)
- • Users can follow/unfollow other users
- • Users can like and comment on posts
- • Users can view feed (timeline) of followed users' posts
- • Users can search for other users and hashtags
- • Support for Stories (24-hour disappearing content)
Non-Functional Requirements:
- • High availability (99.99% uptime)
- • Low latency for feed loading (<300ms)
- • Scalability (1 billion users, 500M DAU)
- • Reliable media storage and delivery
- • Efficient image compression and processing
📊 Capacity Estimation
Assumptions:
- • 1 billion total users
- • 500 million daily active users (DAU)
- • Each user uploads 2 photos per day on average
- • Each photo is ~2MB before compression, ~200KB after
- • Each user views 50 photos per day
- • Read:Write ratio = 100:1
Calculations:
- • Daily uploads: 500M users * 2 photos = 1B photos/day
- • Upload per second: 1B / 86,400 ≈ 11,600 photos/sec
- • Storage per day: 1B * 200KB = 200TB/day
- • Storage per year: 200TB * 365 = 73PB/year (73,000TB)
- • Daily reads: 500M users * 50 photos = 25B photo views/day
- • Reads per second: 25B / 86,400 ≈ 289,000 reads/sec
- • Bandwidth (egress): 289,000 * 200KB ≈ 58GB/sec
🔌 API Design
// 1. Upload PhotoPOST /api/v1/postsRequest (multipart/form-data):{ "userId": 12345, "caption": "Beautiful sunset! #nature #photography", "location": "San Francisco, CA", "images": [file1.jpg, file2.jpg] // max 10 images}Response:{ "postId": 987654321, "userId": 12345, "caption": "Beautiful sunset! #nature #photography", "imageUrls": [ "https://cdn.instagram.com/p/987654321/image_0.jpg", "https://cdn.instagram.com/p/987654321/image_1.jpg" ], "thumbnailUrls": [ "https://cdn.instagram.com/p/987654321/thumb_0.jpg", "https://cdn.instagram.com/p/987654321/thumb_1.jpg" ], "createdAt": "2024-01-15T10:30:00Z", "likes": 0, "comments": 0}// 2. Get User FeedGET /api/v1/feed?userId=12345&page=1&pageSize=20Response:{ "posts": [ { "postId": 999, "userId": 67890, "username": "alice_photos", "profilePicUrl": "https://cdn.instagram.com/u/67890/profile.jpg", "caption": "Morning coffee ☕", "imageUrls": ["https://cdn.instagram.com/p/999/image_0.jpg"], "thumbnailUrls": ["https://cdn.instagram.com/p/999/thumb_0.jpg"], "likes": 245, "comments": 12, "createdAt": "2024-01-15T09:00:00Z" } // ... more posts ], "nextPage": 2, "hasMore": true}// 3. Like PostPOST /api/v1/posts/{postId}/likeRequest Body:{ "userId": 12345}// 4. Comment on PostPOST /api/v1/posts/{postId}/commentsRequest Body:{ "userId": 12345, "text": "Amazing photo!"}// 5. Follow UserPOST /api/v1/followRequest Body:{ "followerId": 12345, "followeeId": 67890}// 6. Search Users/HashtagsGET /api/v1/search?q=nature&type=hashtag&page=1Response:{ "results": [ { "hashtag": "#nature", "postCount": 1234567, "recentPosts": [...] } ]}💾 Database Schema
-- Users Table (MySQL/PostgreSQL)CREATE TABLE users ( user_id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(100), bio TEXT, profile_pic_url VARCHAR(500), followers_count INT DEFAULT 0, following_count INT DEFAULT 0, posts_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_username (username), INDEX idx_email (email));-- Posts Table (Cassandra recommended for scale)CREATE TABLE posts ( post_id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, caption TEXT, location VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, likes_count INT DEFAULT 0, comments_count INT DEFAULT 0, FOREIGN KEY (user_id) REFERENCES users(user_id), INDEX idx_user_created (user_id, created_at DESC));-- Post Images Table (multiple images per post)CREATE TABLE post_images ( image_id BIGINT PRIMARY KEY AUTO_INCREMENT, post_id BIGINT NOT NULL, image_url VARCHAR(500) NOT NULL, thumbnail_url VARCHAR(500) NOT NULL, order_index INT DEFAULT 0, width INT, height INT, FOREIGN KEY (post_id) REFERENCES posts(post_id), INDEX idx_post (post_id));-- Followers Table (relationship graph)CREATE TABLE followers ( follower_id BIGINT NOT NULL, followee_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (follower_id, followee_id), FOREIGN KEY (follower_id) REFERENCES users(user_id), FOREIGN KEY (followee_id) REFERENCES users(user_id), INDEX idx_followee (followee_id), INDEX idx_follower (follower_id));-- Likes TableCREATE TABLE likes ( user_id BIGINT NOT NULL, post_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, post_id), FOREIGN KEY (user_id) REFERENCES users(user_id), FOREIGN KEY (post_id) REFERENCES posts(post_id), INDEX idx_post_likes (post_id, created_at DESC));-- Comments TableCREATE TABLE comments ( comment_id BIGINT PRIMARY KEY AUTO_INCREMENT, post_id BIGINT NOT NULL, user_id BIGINT NOT NULL, text TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (post_id) REFERENCES posts(post_id), FOREIGN KEY (user_id) REFERENCES users(user_id), INDEX idx_post_comments (post_id, created_at DESC));-- Feed Table (Pre-computed feeds - Cassandra)CREATE TABLE user_feed ( user_id BIGINT NOT NULL, post_id BIGINT NOT NULL, post_created_at TIMESTAMP NOT NULL, PRIMARY KEY (user_id, post_created_at, post_id)) WITH CLUSTERING ORDER BY (post_created_at DESC, post_id DESC);Image Storage & Processing
Storage Strategy:
- • Store original images in object storage (S3, Azure Blob)
- • Generate multiple sizes (thumbnail, medium, full)
- • Use CDN for fast global delivery
- • Compress images using WebP or AVIF format
Processing Pipeline:
- 1. User uploads image → API Server
- 2. Upload to temp storage
- 3. Image processing service (async):
- • Compress image (reduce size by 90%)
- • Generate thumbnails (150x150, 640x640)
- • Extract metadata (location, timestamp)
- 4. Store in object storage (S3)
- 5. Save metadata to database
- 6. Return success to user
public class ImageProcessingService { private final S3Client s3Client; private final DatabaseService dbService; private final MessageQueueService messageQueue; /** * Process uploaded image asynchronously */ public void processImage(ImageUploadEvent event) { try { // 1. Download original image from temp storage byte[] originalImage = s3Client.download(event.getTempUrl()); // 2. Compress image (reduce size by ~90%) byte[] compressedImage = compressImage(originalImage, 80); // 80% quality // 3. Generate thumbnails byte[] thumbnail150 = resizeImage(compressedImage, 150, 150); byte[] thumbnail640 = resizeImage(compressedImage, 640, 640); // 4. Upload to permanent storage String imageKey = "posts/" + event.getPostId() + "/image.webp"; String thumb150Key = "posts/" + event.getPostId() + "/thumb_150.webp"; String thumb640Key = "posts/" + event.getPostId() + "/thumb_640.webp"; s3Client.upload(imageKey, compressedImage); s3Client.upload(thumb150Key, thumbnail150); s3Client.upload(thumb640Key, thumbnail640); // 5. Get CDN URLs String imageUrl = getCdnUrl(imageKey); String thumbnail150Url = getCdnUrl(thumb150Key); String thumbnail640Url = getCdnUrl(thumb640Key); // 6. Save metadata to database PostImage postImage = new PostImage(); postImage.setPostId(event.getPostId()); postImage.setImageUrl(imageUrl); postImage.setThumbnailUrl(thumbnail640Url); postImage.setWidth(event.getWidth()); postImage.setHeight(event.getHeight()); dbService.savePostImage(postImage); // 7. Delete temp file s3Client.delete(event.getTempUrl()); System.out.println("Image processed successfully: " + imageUrl); } catch (Exception e) { System.err.println("Failed to process image: " + e.getMessage()); // Retry logic or dead letter queue } } private byte[] compressImage(byte[] image, int quality) { // Use ImageIO or external library (e.g., TwelveMonkeys, WebP Java) // Compress to WebP format with specified quality // Return compressed bytes return new byte[0]; // Placeholder } private byte[] resizeImage(byte[] image, int width, int height) { // Resize image to specified dimensions maintaining aspect ratio // Use library like Thumbnailator or imgscalr return new byte[0]; // Placeholder } private String getCdnUrl(String s3Key) { // Return CloudFront/CDN URL for the S3 key return "https://cdn.instagram.com/" + s3Key; }}Feed Generation Strategies
Strategy 1: Pull-based Feed (Fan-out on Read)
Generate feed when user requests it
Pros:
- • No storage for inactive users
- • Always up-to-date feed
Cons:
- • Slow feed loading (must query many users)
- • High read latency
Strategy 2: Push-based Feed (Fan-out on Write)
Pre-generate feed when post is created
Pros:
- • Fast feed loading (pre-computed)
- • Low read latency
Cons:
- • Expensive for users with many followers
- • Storage overhead for all users
Strategy 3: Hybrid Approach (Recommended)
Use both based on user type
- • Regular users (<10K followers): Push-based (fan-out on write)
- • Celebrities (>10K followers): Pull-based (fan-out on read)
- • Cache feed in Redis (last 100 posts)
- • Ranked feed using ML algorithm
public class FeedService { private final PostRepository postRepository; private final FollowerRepository followerRepository; private final CacheService cacheService; private final RankingService rankingService; private static final int CELEBRITY_THRESHOLD = 10_000; /** * Get ranked feed for user (hybrid approach) */ public List<Post> getFeed(long userId, int page, int pageSize) { String cacheKey = "feed:" + userId + ":" + page; // 1. Check cache List<Post> cachedFeed = cacheService.get(cacheKey); if (cachedFeed != null) { return cachedFeed; } // 2. Get users this user follows List<Long> followingIds = followerRepository.getFollowing(userId); // 3. Separate celebrities from regular users List<Long> celebrities = new ArrayList<>(); List<Long> regularUsers = new ArrayList<>(); for (Long followeeId : followingIds) { int followerCount = followerRepository.getFollowerCount(followeeId); if (followerCount > CELEBRITY_THRESHOLD) { celebrities.add(followeeId); } else { regularUsers.add(followeeId); } } List<Post> allPosts = new ArrayList<>(); // 4. For regular users: get from pre-computed feed (push-based) if (!regularUsers.isEmpty()) { allPosts.addAll(getPrecomputedFeed(userId, pageSize)); } // 5. For celebrities: fetch their recent posts (pull-based) if (!celebrities.isEmpty()) { allPosts.addAll(getRecentPostsFrom(celebrities, pageSize)); } // 6. Rank posts using ML algorithm List<Post> rankedPosts = rankingService.rankPosts(userId, allPosts); // 7. Paginate int offset = (page - 1) * pageSize; rankedPosts = rankedPosts.subList( offset, Math.min(offset + pageSize, rankedPosts.size()) ); // 8. Cache the result cacheService.set(cacheKey, rankedPosts, 300); // 5 min TTL return rankedPosts; } private List<Post> getPrecomputedFeed(long userId, int limit) { // Get posts from user_feed table (pre-computed during fanout) return postRepository.getUserFeed(userId, limit); } private List<Post> getRecentPostsFrom(List<Long> userIds, int limit) { // Pull recent posts from these users return postRepository.getRecentPosts(userIds, limit); }}/** * Ranking Service using ML algorithm */public class RankingService { /** * Rank posts based on multiple signals */ public List<Post> rankPosts(long userId, List<Post> posts) { // Calculate engagement score for each post List<ScoredPost> scoredPosts = new ArrayList<>(); for (Post post : posts) { double score = calculateScore(userId, post); scoredPosts.add(new ScoredPost(post, score)); } // Sort by score (descending) scoredPosts.sort((a, b) -> Double.compare(b.score, a.score)); // Return sorted posts return scoredPosts.stream() .map(sp -> sp.post) .collect(Collectors.toList()); } private double calculateScore(long userId, Post post) { double score = 0.0; // 1. Engagement score (40% weight) double engagementScore = post.getLikes() * 1.0 + post.getComments() * 3.0 + post.getShares() * 5.0; score += engagementScore * 0.4; // 2. Time decay (30% weight) - newer posts score higher long ageInHours = ChronoUnit.HOURS.between( post.getCreatedAt(), Instant.now() ); double timeScore = Math.exp(-ageInHours / 24.0) * 100; // Exponential decay score += timeScore * 0.3; // 3. User affinity (20% weight) - how often user interacts with this author double affinityScore = getUserAffinity(userId, post.getUserId()); score += affinityScore * 0.2; // 4. Content type (10% weight) - videos might score higher double contentScore = post.isVideo() ? 10.0 : 5.0; score += contentScore * 0.1; return score; } private double getUserAffinity(long userId, long authorId) { // Calculate based on past interactions (likes, comments, views) // Return score 0-100 return 50.0; // Placeholder } private static class ScoredPost { Post post; double score; ScoredPost(Post post, double score) { this.post = post; this.score = score; } }}🏛️ High-Level System Design
┌──────────────┐
│ Clients │
│ (Web/Mobile) │
└──────┬───────┘
│
┌──────▼────────┐
│ Load Balancer │
└──────┬────────┘
│
┌──────▼────────┐
│ API Gateway │
└──────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌───────▼──────┐ ┌──────▼──────┐
│ Upload │ │ Feed │ │ User │
│ Service │ │ Service │ │ Service │
└────┬────┘ └───────┬──────┘ └──────┬──────┘
│ │ │
│ ┌──────▼──────┐ │
│ │ Cache │ │
│ │ (Redis) │ │
│ └─────────────┘ │
│ │
┌────▼─────────┐ ┌──────▼──────┐
│ Image │ │ Follower │
│ Processing │ │ DB │
│ Service │ │ (MySQL) │
└────┬─────────┘ └─────────────┘
│
┌────▼─────────┐
│ Message │
│ Queue │
│ (Kafka) │
└────┬─────────┘
│
┌────▼─────────┐
│ Fanout │
│ Workers │
└────┬─────────┘
│
├─────────────────┬─────────────────┐
│ │ │
┌────▼─────┐ ┌──────▼──────┐ ┌─────▼──────┐
│ Object │ │ Posts DB │ │ Feed DB │
│ Storage │ │ (Cassandra) │ │(Cassandra) │
│ (S3) │ │ │ │ │
└──────────┘ └─────────────┘ └────────────┘
│
┌────▼─────┐
│ CDN │
│(CloudFront)
└──────────┘
Components:
- • Load Balancer: Distribute traffic across servers
- • API Gateway: Route requests to microservices
- • Upload Service: Handle media uploads
- • Media Processing Service: Image/video processing
- • Feed Service: Generate user feeds
- • User Service: Manage user profiles and relationships
- • Notification Service: Send notifications
- • CDN: Deliver media content globally
- • Object Storage: Store images/videos (S3)
- • Cache (Redis): Store feeds and hot data
- • Databases: User data, posts, relationships
- • Message Queue (Kafka): Async processing
Data Flow:
Upload Photo Flow:
- 1. User uploads photo → Load Balancer → API Gateway
- 2. Upload Service receives image
- 3. Store in S3, publish to Kafka
- 4. Processing Service: compress, generate thumbnails
- 5. Feed Service: fanout to followers (async)
- 6. Notification Service: notify followers
View Feed Flow:
- 1. User requests feed → API Gateway
- 2. Feed Service checks Redis cache
- 3. If cache miss → query database
- 4. Rank posts using ML algorithm
- 5. Return feed with CDN URLs for images
🔍 Deep Dive Topics
Database Sharding
Partition data by user_id. Each shard handles subset of users. Use consistent hashing for even distribution.
Caching Strategy
- • User profiles: Cache for 1 hour
- • Feeds: Cache last 100 posts per user
- • Popular posts: Cache separately with higher TTL
- • Use Redis with LRU eviction
Feed Ranking Algorithm
Machine learning model considering:
- • Engagement score (likes, comments, shares)
- • Time decay (newer posts ranked higher)
- • User affinity (interaction history)
- • Content type (photo/video/carousel)
CDN Strategy
- • Store images in multiple edge locations
- • Cache popular images with longer TTL
- • Use responsive images (different sizes)
- • Lazy loading for better performance
⚖️ Trade-offs & Decisions
Chronological vs Ranked Feed:
Chronological: Chronological: Simple, predictable, but may miss important posts
Ranked: Ranked: Better engagement, but complex ML model needed
Storage: SQL vs NoSQL:
SQL: SQL (MySQL): User data, relationships (ACID required)
NoSQL: NoSQL (Cassandra): Posts, feeds (high write throughput)
Image Format: JPEG vs WebP vs AVIF:
WebP: 30% smaller than JPEG, good browser support. AVIF: 50% smaller, but limited support.
🚀 Optimizations
- →Progressive Image Loading: Show blurred placeholder first
- →Lazy Loading: Load images as user scrolls
- →Image Compression: Use WebP with 80% quality
- →Video Streaming: Use HLS/DASH for adaptive streaming
- →Database Indexing: Index on user_id, created_at for fast queries
- →Connection Pooling: Reuse database connections
🤔 Follow-up Questions
Q: How would you implement Instagram Stories?
A: Store stories in separate table with 24hr TTL. Use Redis for active stories. Delete from S3 after 24hrs. Show in separate API endpoint.
Q: How to implement Direct Messages?
A: Use separate messaging service. Store messages in Cassandra partitioned by conversation_id. Use WebSockets for real-time delivery. Cache recent messages in Redis.
Q: How to handle viral posts?
A: Cache in CDN with high TTL. Use separate cache tier for hot content. Load balance reads across replicas. Rate limit requests per user.
Q: How to implement hashtag search?
A: Use Elasticsearch for full-text search. Index hashtags from posts. Maintain trending hashtags in Redis (count last 24hrs). Update asynchronously.
Q: How to prevent inappropriate content?
A: ML model for image classification. Human moderators for flagged content. User reporting system. Hash matching for known bad content.
🌍 Real-World Architecture
- • Instagram uses Cassandra for feed storage and PostgreSQL for user data
- • Instagram uses memcached extensively for caching (millions of cache items)
- • Images stored in Facebook's Haystack (custom object storage)
- • Instagram uses Python (Django) for backend and React for frontend