Skip to main content

grokking-system-design-interview

Pattern-based system design interview preparation framework with reusable building blocks, question walkthroughs, and company-specific interview guides

Jump to install

Source facts

Repository
reason-machines/design-skills
Last source activity
July 16, 2026 at 15:33
Detected SKILL.md language
English
Stars
4
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
grokking-system-design-interview
description
Pattern-based system design interview preparation framework with reusable building blocks, question walkthroughs, and company-specific interview guides
triggers
["help me prepare for a system design interview","show me system design patterns","design a scalable system","explain distributed system concepts","practice system design questions","what are common system design building blocks","how to approach system design interviews","show me cache and load balancing patterns"]
# Grokking System Design Interview Skill > Skill by [ara.so](https://ara.so) — Design Skills collection. This skill provides expertise in using the Grokking System Design repository, a pattern-based approach to system design interviews. Learn reusable building blocks (caching, sharding, replication, consistency models) and apply them to any design question. ## What This Project Does Grokking System Design is a free, open companion to the original course that teaches: - **Pattern-based methodology**: Learn 24 reusable building blocks instead of memorizing answers - **Interview framework**: A repeatable 7-step structure for any question - **40+ question walkthroughs**: From TinyURL to ChatGPT, organized by difficulty - **Company-specific guides**: How 58 companies run their system design rounds - **Distributed systems deep dives**: Case studies of Dynamo, Cassandra, Kafka, etc. ## Installation This is a knowledge repository, not code you install. Clone it for offline reference: ```bash git clone https://github.com/design-gurus/grokking-system-design.git cd grokking-system-design ``` Or browse online at: https://github.com/design-gurus/grokking-system-design ## Repository Structure ``` grokking-system-design/ ├── patterns/ # 24 reusable building blocks ├── questions/ # 40+ system design walkthroughs ├── companies/ # 58 company-specific interview guides ├── cheat-sheets/ # Quick reference sheets ├── roadmaps/ # 1-week, 2-week, 6-week study plans ├── deep-dives/ # Distributed systems case studies └── glossary.md # System design vocabulary ``` ## The 7-Step Interview Framework Every system design question should follow this structure: ### 1. Clarify Requirements (3-5 minutes) **Functional requirements** (what the system does): ``` Example for "Design Instagram": - Users can upload photos - Users can follow other users - Users see a feed of photos from people they follow - Users can like and comment on photos ``` **Non-functional requirements** (how the system performs): ``` - Scale: 500M users, 100M daily active users - Availability: 99.99% uptime - Latency: Feed loads in < 200ms - Consistency: Eventual consistency is acceptable ``` ### 2. Estimate Scale (5 minutes) ``` Traffic estimate: - 100M DAU - Each user views 50 photos/day = 5B photo views/day - 5B / 86400 seconds ≈ 58K requests/second - Peak traffic (3x average) = 174K RPS Storage estimate: - 2M new photos/day - Average photo size: 2MB - Daily storage: 2M × 2MB = 4TB/day - 5-year storage: 4TB × 365 × 5 ≈ 7.3PB Bandwidth estimate: - Incoming: 4TB/day = 46MB/s - Outgoing (views): 5B × 2MB / 86400 = 115GB/s ``` ### 3. Define the API (5 minutes) ```python # REST API example for Instagram # Upload photo POST /api/v1/photos Content-Type: multipart/form-data Body: { photo: file, caption: string, location: string } Response: { photo_id: string, url: string } # Get user feed GET /api/v1/feed?user_id={id}&cursor={cursor}&limit=20 Response: { photos: [...], next_cursor: string } # Follow user POST /api/v1/users/{user_id}/follow Body: { follower_id: string } Response: { success: boolean } # Like photo POST /api/v1/photos/{photo_id}/like Body: { user_id: string } Response: { success: boolean, like_count: number } ``` ### 4. Design the Data Model (5 minutes) ```sql -- SQL schema example for Instagram CREATE TABLE users ( user_id BIGINT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT NOW(), INDEX idx_username (username) ); CREATE TABLE photos ( photo_id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL, photo_url VARCHAR(500) NOT NULL, caption TEXT, location VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (user_id) REFERENCES users(user_id), INDEX idx_user_created (user_id, created_at) ); CREATE TABLE follows ( follower_id BIGINT NOT NULL, followee_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT NOW(), PRIMARY KEY (follower_id, followee_id), INDEX idx_follower (follower_id), INDEX idx_followee (followee_id) ); CREATE TABLE likes ( photo_id BIGINT NOT NULL, user_id BIGINT NOT NULL, created_at TIMESTAMP DEFAULT NOW(), PRIMARY KEY (photo_id, user_id), INDEX idx_photo (photo_id) ); ``` For NoSQL (DynamoDB): ```json // Users table { "PK": "USER#12345", "SK": "METADATA", "username": "johndoe", "email": "john@example.com", "created_at": "2024-01-01T00:00:00Z" } // Photos table { "PK": "USER#12345", "SK": "PHOTO#67890", "photo_id": "67890", "photo_url": "https://cdn.example.com/photos/67890.jpg", "caption": "Beautiful sunset", "created_at": "2024-01-15T18:30:00Z" } // Follows table (adjacency list) { "PK": "USER#12345", "SK": "FOLLOWS#USER#99999", "created_at": "2024-01-10T12:00:00Z" } ``` ### 5. High-Level Architecture (10 minutes) ``` ┌─────────────┐ │ Clients │ (Web, iOS, Android) └──────┬──────┘ │ ▼ ┌─────────────┐ │ CDN │ (CloudFront, Cloudflare) └──────┬──────┘ │ ▼ ┌─────────────┐ │Load Balancer│ (ELB, HAProxy, NGINX) └──────┬──────┘ │ ▼ ┌──────────────────────────────┐ │ API Gateway / BFF │ └──────┬───────────────────────┘ │ ├─────────┬─────────┬─────────┐ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ User │ │ Photo │ │ Feed │ │ Social │ │Service │ │Service │ │Service │ │Service │ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ │ │ │ │ ├──────────┼──────────┼──────────┤ ▼ ▼ ▼ ▼ ┌───────────────────────────────────────┐ │ Message Queue (Kafka) │ └───────────────────────────────────────┘ │ │ │ │ ├──────────┼──────────┼──────────┤ ▼ ▼ ▼ ▼ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ Cache │ │ SQL DB │ │ NoSQL │ │ Object │ │(Redis) │ │(Postgres)│ │(Cassandra)││Storage│ └────────┘ └────────┘ └────────┘ │ (S3) │ └────────┘ ``` ### 6. Deep Dive (15 minutes) Pick 1-2 components and go deep. Example: **Feed generation** ```python # Feed generation service - approach 1: Fan-out on write class FeedService: def __init__(self, cache, db, queue): self.cache = cache # Redis self.db = db # PostgreSQL self.queue = queue # Kafka def publish_photo(self, user_id, photo_id): """ When user posts a photo, push to all followers' feeds (Fan-out on write) """ # Get all followers followers = self.db.query( "SELECT follower_id FROM follows WHERE followee_id = %s", (user_id,) ) # Push to each follower's feed cache for follower in followers: feed_key = f"feed:{follower['follower_id']}" self.cache.zadd( feed_key, {photo_id: time.time()}, # Score = timestamp nx=True # Only if not exists ) # Keep only latest 1000 items self.cache.zremrangebyrank(feed_key, 0, -1001) # Publish event for async processing self.queue.publish('photo.published', { 'user_id': user_id, 'photo_id': photo_id, 'timestamp': time.time() }) def get_feed(self, user_id, cursor=None, limit=20): """ Retrieve user's feed from cache """ feed_key = f"feed:{user_id}" # Get from Redis sorted set (sorted by timestamp) if cursor: photo_ids = self.cache.zrevrangebyscore( feed_key, max=cursor, min='-inf', start=0, num=limit ) else: photo_ids = self.cache.zrevrange( feed_key, start=0, end=limit-1 ) if not photo_ids: # Cache miss - generate from DB (fan-out on read) return self._generate_feed_from_db(user_id, limit) # Batch fetch photo metadata photos = self._batch_get_photos(photo_ids) return { 'photos': photos, 'next_cursor': photos[-1]['timestamp'] if photos else None } def _generate_feed_from_db(self, user_id, limit): """ Fallback: Generate feed from database (Fan-out on read for cache misses) """ photos = self.db.query(""" SELECT p.photo_id, p.user_id, p.photo_url, p.caption, p.created_at FROM photos p JOIN follows f ON p.user_id = f.followee_id WHERE f.follower_id = %s ORDER BY p.created_at DESC LIMIT %s """, (user_id, limit)) # Warm up cache feed_key = f"feed:{user_id}" for photo in photos: self.cache.zadd( feed_key, {photo['photo_id']: photo['created_at'].timestamp()} ) return {'photos': photos, 'next_cursor': photos[-1]['created_at'] if photos else None} ``` **Trade-offs:** | Approach | Pros | Cons | Use When | |----------|------|------|----------| | Fan-out on write | Fast reads, pre-computed | Slow writes, storage overhead | Most users have few followers | | Fan-out on read | Fast writes, less storage | Slow reads, more DB load | Users have many followers (celebrities) | | Hybrid | Best of both | Complex implementation | Production systems | ### 7. Bottlenecks & Trade-offs (5 minutes) ``` Bottleneck: Database writes (photo uploads) Solutions: 1. Shard database by user_id (consistent hashing) 2. Use write-ahead log (WAL) for durability 3. Batch writes with message queue 4. Use object storage (S3) for photos, DB only for metadata Bottleneck: Feed generation for celebrity users (millions of followers) Solutions: 1. Hybrid approach: fan-out on read for users with > 100K followers 2. Limit fan-out, deliver celebrity posts on demand 3. Use real-time stream processing (Kafka + Flink) Bottleneck: Hot partition (single shard receives too much traffic) Solutions: 1. Re-shard using different hash function 2. Add secondary indexes 3. Cache popular content (80/20 rule) Trade-off: Consistency vs Availability (CAP theorem) - Strong consistency: Users always see latest data, but system may be unavailable during network partition - Eventual consistency: System is always available, but users may see stale data temporarily - Choice: Eventual consistency for social features (likes, follows), strong consistency for payments ``` ## Core Patterns Reference ### 1. Caching ```python # Redis caching pattern with TTL and cache-aside strategy import redis import json from typing import Optional class CacheService: def __init__(self, redis_url: str): self.redis = redis.from_url(redis_url) def get(self, key: str) -> Optional[dict]: """Get from cache, return None if miss""" value = self.redis.get(key) return json.loads(value) if value else None def set(self, key: str, value: dict, ttl: int = 3600): """Set with TTL (default 1 hour)""" self.redis.setex(key, ttl, json.dumps(value)) def delete(self, key: str): """Invalidate cache entry""" self.redis.delete(key) def get_or_compute(self, key: str, compute_fn, ttl: int = 3600): """Cache-aside pattern: get from cache or compute and store""" cached = self.get(key) if cached is not None: return cached # Cache miss - compute and store value = compute_fn() self.set(key, value, ttl) return value # Usage cache = CacheService(redis_url="redis://localhost:6379/0") def get_user_profile(user_id: str): return cache.get_or_compute( key=f"user:{user_id}", compute_fn=lambda: db.fetch_user(user_id), ttl=1800 # 30 minutes ) ``` **Cache eviction policies:** - LRU (Least Recently Used) - default, good for general use - LFU (Least Frequently Used) - good for access patterns with hot keys - TTL (Time To Live) - expire after fixed time ### 2. Load Balancing ```nginx # NGINX load balancer configuration upstream api_servers { # Load balancing method least_conn; # Route to server with fewest connections # Alternative: ip_hash (sticky sessions), round_robin (default) server api1.example.com:8080 weight=3 max_fails=3 fail_timeout=30s; server api2.example.com:8080 weight=2 max_fails=3 fail_timeout=30s; server api3.example.com:8080 weight=1 backup; # Backup server # Health checks check interval=3000 rise=2 fall=3 timeout=1000; } server { listen 80; server_name api.example.com; location / { proxy_pass http://api_servers; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Timeouts proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s; # Retries proxy_next_upstream error timeout http_500 http_502 http_503; } } ``` ### 3. Sharding / Partitioning ```python # Consistent hashing for data partitioning import hashlib from bisect import bisect_right from typing import List, Dict class ConsistentHash: def __init__(self, nodes: List[str], virtual_nodes: int = 150): """ nodes: List of server addresses virtual_nodes: Number of virtual nodes per physical node """ self.virtual_nodes = virtual_nodes self.ring: Dict[int, str] = {} self.sorted_keys: List[int] = [] for node in nodes: self.add_node(node) def _hash(self, key: str) -> int: """Hash function (MD5)"""
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub