What is System Design? A Complete Guide
After this topic, you will be able to:
- Define system design and explain its role in building large-scale distributed systems
- Describe the four key quality attributes: scalability, reliability, availability, and maintainability
- Compare system design requirements for different scales (1K vs 1M vs 1B users)
- Identify why system design interviews matter for senior engineering roles
TL;DR
System design is the art and science of architecting software systems that can scale to millions of users while remaining reliable, available, and maintainable. It’s the difference between a system that crashes under Black Friday traffic and one that handles 10x load gracefully. For engineers, it’s both a critical production skill and a make-or-break interview topic at companies like Google, Amazon, and Netflix.
Cheat Sheet: System design = architecture decisions for scalability (handling growth), reliability (correctness under failure), availability (uptime), and maintainability (evolution over time). Think distributed systems, not single-server apps.
The Analogy
Building a system is like designing a city’s infrastructure. A small town can get by with a single main road, one power plant, and a volunteer fire department. But as the population grows to millions, you need highway networks with redundant routes, multiple power grids with failover capability, and professional emergency services distributed across neighborhoods. You can’t just make the main road wider—you need fundamentally different architecture. System design is about planning for that growth before your “city” collapses under its own success.
Why This Matters in Interviews
Why Companies Ask
System design interviews reveal how you think about real production problems, not just algorithms. When Netflix asks you to design their video streaming platform, they’re testing whether you understand the difference between serving 1,000 users from a single server versus serving 200 million users across continents with 99.99% uptime. Companies like Google and Amazon have learned the hard way that brilliant coders who can’t architect scalable systems create technical debt that costs millions to fix. The interview isn’t about getting the “right” answer—it’s about demonstrating structured thinking, making explicit tradeoffs, and knowing when to use a cache versus a message queue versus a CDN.
What Interviewers Want
Interviewers want to see three things: (1) Can you break down ambiguous requirements into concrete technical decisions? (2) Do you understand the tradeoffs between consistency and availability, or between latency and throughput? (3) Can you estimate capacity and reason about bottlenecks quantitatively? A candidate who says “we’ll use microservices and Kubernetes” without explaining why fails. A candidate who says “at 10M daily active users, we’re looking at roughly 115 requests/second average, 500 peak, which a single database can’t handle, so we need to shard” demonstrates systems thinking. The bar rises with seniority: mid-level engineers should know common patterns, senior engineers should justify tradeoffs with numbers, and staff+ engineers should anticipate failure modes and operational complexity.
Career Impact
System design skills separate senior engineers from junior ones. You might write beautiful code, but if your API design forces clients to make 50 sequential calls instead of one batch request, you’ve created a system that can’t scale. Companies pay premium salaries ($300K+ total comp) for engineers who can architect systems that handle exponential growth without exponential cost. This skill compounds: once you understand how Uber routes millions of rides or how Stripe processes billions in payments, you can apply those patterns across domains. It’s the difference between being a feature developer and being a technical leader who shapes product strategy.
Core Concept
System design is the process of defining the architecture, components, interfaces, and data flow for a software system that meets specific functional and non-functional requirements. While coding focuses on how to implement a feature, system design focuses on what components you need and how they interact to achieve goals like handling a million concurrent users or maintaining 99.99% uptime. It’s the blueprint phase before construction begins.
The discipline emerged from necessity. In the early 2000s, companies like Google and Amazon hit walls where traditional single-server architectures couldn’t scale. Google couldn’t index the web with one machine. Amazon couldn’t handle holiday shopping spikes without crashing. They pioneered distributed systems—architectures where work is spread across many machines that coordinate through networks. System design became the methodology for building these complex, distributed systems that power modern internet-scale applications.
At its core, system design is about making tradeoffs. Every decision has costs: adding a cache improves read speed but introduces stale data problems. Replicating databases improves availability but complicates consistency. Microservices enable team autonomy but increase operational complexity. Good system design means understanding these tradeoffs and choosing the right ones for your specific requirements and constraints.
System Design Decision Layers
graph TB
Requirements["Requirements<br/><i>Functional & Non-Functional</i>"]
HLD["High-Level Design<br/><i>Architecture & Components</i>"]
DataModel["Data Model & Flow<br/><i>Entities & Interactions</i>"]
NFR["Non-Functional Requirements<br/><i>Quality Attributes</i>"]
subgraph Quality Pillars
Scalability["Scalability<br/><i>Handle Growth</i>"]
Reliability["Reliability<br/><i>Correct Under Failure</i>"]
Availability["Availability<br/><i>Minimize Downtime</i>"]
Maintainability["Maintainability<br/><i>Easy to Change</i>"]
end
Requirements --> HLD
HLD --> DataModel
DataModel --> NFR
NFR --> Scalability
NFR --> Reliability
NFR --> Availability
NFR --> Maintainability
Scalability -."drives".-> HLD
Reliability -."drives".-> HLD
Availability -."drives".-> HLD
Maintainability -."drives".-> HLD
System design operates at multiple abstraction levels, from high-level architecture down to quality attributes. The four pillars (scalability, reliability, availability, maintainability) aren’t afterthoughts—they drive fundamental architecture decisions and create feedback loops that shape the entire design.
Architectural Evolution with Scale
graph TB
subgraph "1K Users: Monolith"
M1["Single Server<br/><i>App + DB</i>"]
M1_Users["~10 req/sec"]
end
subgraph "10K Users: Vertical Scale + Cache"
V1["Web Server<br/><i>Bigger instance</i>"]
V2["Read Replica<br/><i>PostgreSQL</i>"]
V3["Primary DB<br/><i>PostgreSQL</i>"]
V4["Redis Cache"]
V1 --> V4
V1 --> V3
V1 --> V2
end
subgraph "100K Users: Horizontal Scale"
H1["Load Balancer"]
H2["App Server 1"]
H3["App Server 2"]
H4["App Server 3"]
H5["Redis Cluster"]
H6["Primary DB"]
H7["Read Replicas"]
H8["CDN<br/><i>Static Assets</i>"]
H1 --> H2 & H3 & H4
H2 & H3 & H4 --> H5
H2 & H3 & H4 --> H6 & H7
end
subgraph "1M+ Users: Distributed Systems"
D1["Global CDN"]
D2["API Gateway"]
D3["Microservice 1"]
D4["Microservice 2"]
D5["Microservice 3"]
D6["Message Queue<br/><i>Kafka</i>"]
D7["DB Shard 1"]
D8["DB Shard 2"]
D9["DB Shard 3"]
D10["Distributed Cache<br/><i>Multi-region</i>"]
D1 --> D2
D2 --> D3 & D4 & D5
D3 & D4 & D5 --> D6
D3 --> D7
D4 --> D8
D5 --> D9
D3 & D4 & D5 --> D10
end
M1 -."Scale up".-> V1
V1 -."Scale out".-> H1
H1 -."Distribute".-> D1
System architecture fundamentally changes at each order of magnitude. You can’t just add more servers—at 100K users you need load balancing and CDNs, at 1M+ users you need database sharding and microservices. Each transition requires architectural rethinking, not just resource scaling.
CAP Theorem: Consistency vs. Availability Tradeoff
graph TB
subgraph "Network Partition Occurs"
direction LR
Client["Client Request"]
Node1["Node 1<br/><i>Data: v2</i>"]
Node2["Node 2<br/><i>Data: v1</i>"]
Partition["❌ Network Split<br/><i>Nodes can't sync</i>"]
Node1 -.-x Partition
Partition x-.- Node2
end
subgraph "Choice A: Prioritize Consistency"
CA_Client["Client"]
CA_Node1["Node 1"]
CA_Node2["Node 2"]
CA_Client -->|"Read request"| CA_Node2
CA_Node2 -->|"❌ Return error<br/>(can't verify latest)"| CA_Client
CA_Result["✓ Strong Consistency<br/>❌ Lower Availability<br/><i>Example: Banking systems</i>"]
end
subgraph "Choice B: Prioritize Availability"
CB_Client["Client"]
CB_Node1["Node 1"]
CB_Node2["Node 2"]
CB_Client -->|"Read request"| CB_Node2
CB_Node2 -->|"✓ Return v1<br/>(stale data)"| CB_Client
CB_Result["✓ High Availability<br/>❌ Eventual Consistency<br/><i>Example: Social media feeds</i>"]
end
Client -."Forces choice".-> CA_Client
Client -."Forces choice".-> CB_Client
During a network partition, distributed systems must choose between consistency (all nodes show the same data) and availability (system responds to requests). Banks choose consistency—better to show an error than wrong account balance. Social media chooses availability—better to show slightly stale data than be down. This is the CAP theorem in action.
Netflix Microservices Architecture (Simplified)
graph LR
User["User Device<br/><i>Web/Mobile/TV</i>"]
CDN["Open Connect CDN<br/><i>ISP-level caching</i>"]
subgraph "API Gateway Layer"
Gateway["Zuul Gateway<br/><i>Routing & Auth</i>"]
end
subgraph "Microservices (100s of services)"
Auth["Auth Service<br/><i>User login</i>"]
Profile["Profile Service<br/><i>User data</i>"]
Recs["Recommendation<br/><i>ML-based</i>"]
Playback["Playback Service<br/><i>Streaming logic</i>"]
Encoding["Encoding Service<br/><i>Video processing</i>"]
end
subgraph "Data Layer"
EVCache["EVCache<br/><i>Distributed memcached</i>"]
Cassandra["Cassandra<br/><i>Viewing history</i>"]
S3["AWS S3<br/><i>Video storage</i>"]
end
User -->|"1. Browse catalog"| Gateway
Gateway -->|"2. Authenticate"| Auth
Gateway -->|"3. Get profile"| Profile
Gateway -->|"4. Get recommendations"| Recs
User -->|"5. Request video"| Gateway
Gateway -->|"6. Playback metadata"| Playback
Playback -->|"7. Check cache"| EVCache
EVCache -."Cache miss".-> Cassandra
User -->|"8. Stream video"| CDN
CDN -->|"Origin fetch"| S3
Encoding -->|"Store encoded video"| S3
Profile & Recs & Playback -->|"Cache reads"| EVCache
Profile & Recs & Playback -->|"Persist data"| Cassandra
Netflix’s architecture separates concerns into hundreds of microservices, each handling a specific domain. Videos stream from a global CDN (not through microservices) for performance, while metadata flows through services. EVCache reduces database load by 99%, and Cassandra provides eventual consistency for viewing history. The system can lose entire AWS regions and keep streaming because every component is replicated across availability zones.
How It Works
System design operates at multiple levels of abstraction. At the highest level, you define the overall architecture: will this be a monolithic application, a service-oriented architecture, or microservices? What are the major components (web servers, databases, caches, message queues) and how do they communicate? This is called High-Level Design (HLD).
Next, you design the data model and flow. What entities exist in your system? How do they relate? Where does data live (SQL database, NoSQL store, object storage)? How does data move through the system when a user takes an action? For a ride-sharing app like Uber, this means modeling riders, drivers, trips, and payments, then designing how a ride request flows from the rider’s phone through location services, matching algorithms, payment processing, and back to both rider and driver.
Finally, you consider non-functional requirements—the quality attributes that determine whether your system actually works in production. This is where the four pillars come in: Scalability (can the system handle growth?), Reliability (does it produce correct results even when components fail?), Availability (is the system operational when users need it?), and Maintainability (can engineers modify and debug it efficiently?). These aren’t afterthoughts—they drive fundamental architecture decisions. A system designed for 1,000 users looks completely different from one designed for 100 million users, even if they have identical features.
Ride-Sharing Request Flow (Uber Example)
sequenceDiagram
participant Rider
participant API Gateway
participant Location Service
participant Matching Engine
participant Driver
participant Payment Service
participant Database
Rider->>API Gateway: 1. Request ride<br/>(pickup location)
API Gateway->>Location Service: 2. Get nearby drivers<br/>(geospatial query)
Location Service->>Database: 3. Query driver locations<br/>(quadtree index)
Database-->>Location Service: 4. Return driver list
Location Service-->>Matching Engine: 5. Available drivers
Matching Engine->>Matching Engine: 6. Optimize match<br/>(distance, rating, ETA)
Matching Engine->>Driver: 7. Dispatch ride request
Driver-->>Matching Engine: 8. Accept ride
Matching Engine-->>Rider: 9. Driver assigned<br/>(ETA, driver info)
Note over Rider,Driver: Trip in progress
Driver->>API Gateway: 10. Complete trip
API Gateway->>Payment Service: 11. Process payment<br/>(ACID transaction)
Payment Service->>Database: 12. Record transaction
Payment Service-->>Rider: 13. Receipt & confirmation
Data flows through multiple specialized components in a ride-sharing system. The location service uses geospatial indexing for fast queries, the matching engine optimizes in real-time, and payment processing requires strong consistency (ACID transactions) while trip history can be eventually consistent.
Key Principles
principle: Scalability: Design for Growth explanation: Scalability is the system’s ability to handle increasing load by adding resources. There are two types: vertical scaling (adding more CPU/RAM to existing machines) and horizontal scaling (adding more machines). Vertical scaling is simple but hits physical limits—you can’t buy a server with infinite RAM. Horizontal scaling is how internet giants operate, but it requires designing for distribution from the start. You can’t just throw more servers at a monolithic application and expect it to magically handle 10x traffic. example: Instagram started as a monolithic Django app on a few servers. As they grew to millions of users, they had to shard their PostgreSQL database, introduce caching layers with Redis, and move photos to distributed object storage. Each architectural change was driven by hitting scalability limits. Today, they handle 500 million daily active users by horizontally scaling every component—thousands of web servers, hundreds of database shards, and petabytes of distributed storage.
principle: Reliability: Embrace Failure explanation: Reliability means the system continues to work correctly even when things go wrong—and in distributed systems, things always go wrong. Hard drives fail. Networks partition. Data centers lose power. Reliable systems are designed with the assumption that components will fail, and they use techniques like replication, redundancy, and graceful degradation to maintain correctness. The goal isn’t to prevent all failures (impossible), but to ensure failures don’t cascade into total system collapse. example: Netflix’s Chaos Monkey randomly kills production servers to ensure their systems can handle failures. This seems crazy, but it works: when AWS had a major outage in 2017, Netflix stayed online because their architecture assumed servers would die. They replicate data across availability zones, use circuit breakers to isolate failing services, and design every API call with timeouts and retries. Reliability isn’t a feature you add later—it’s baked into the architecture.
principle: Availability: Minimize Downtime explanation: Availability is the percentage of time a system is operational and accessible. “Five nines” (99.999%) means only 5.26 minutes of downtime per year. Achieving high availability requires eliminating single points of failure through redundancy, using load balancers to route around unhealthy instances, and designing for fast recovery when failures occur. Availability often trades off with consistency—during a network partition, you might choose to serve slightly stale data rather than return errors. example: Amazon’s DynamoDB promises 99.999% availability by replicating data across multiple availability zones and using a leaderless replication model. If one zone goes down, requests automatically route to healthy zones. This architecture prioritizes availability over strong consistency—you might briefly read stale data, but the system never goes down. For an e-commerce platform where downtime directly costs revenue, this tradeoff makes business sense.
principle: Maintainability: Design for Change explanation: Maintainability is how easily engineers can understand, modify, and debug the system over time. Complex systems evolve constantly—new features, bug fixes, performance optimizations. Maintainable systems have clear interfaces, good observability (logging, metrics, tracing), and modular architecture where changes to one component don’t break others. Poor maintainability is why companies do expensive rewrites: the system becomes so tangled that adding features takes months instead of days. example: Stripe’s API design prioritizes maintainability through versioning and backward compatibility. When they need to change an API, they release a new version while keeping old versions working. Their codebase uses strong typing and extensive testing to catch bugs before production. They invest heavily in observability—every request is traced, every error logged with context. This upfront investment in maintainability lets them ship features quickly despite having a massive, complex payments platform.
Deep Dive
Types / Variants
Scale Progression
System design requirements change dramatically with scale, and understanding these inflection points is crucial. At 1,000 users, a monolithic application on a single server with a single database works fine—you’re handling maybe 10 requests/second, and your database has no trouble with that load. At 10,000 users, you start hitting limits: database queries slow down, so you add read replicas and a cache layer like Redis. At 100,000 users, your single web server can’t handle the traffic, so you add a load balancer and multiple application servers. At 1 million users, your database becomes the bottleneck again—you need to shard data across multiple databases and introduce a CDN for static assets. At 10 million+ users, you’re in distributed systems territory: microservices, message queues for async processing, distributed caching, multi-region deployments for global users. Each order of magnitude requires architectural changes, not just more servers.
Architectural Evolution
Most systems start as monoliths—a single codebase deployed as one unit. This is the right choice for early-stage products: fast development, easy debugging, no network overhead between components. But monoliths don’t scale well organizationally or technically. As teams grow, everyone modifies the same codebase, causing conflicts. As load grows, you can’t scale components independently—if your payment processing needs more resources, you have to scale the entire monolith. This drives the evolution to Service-Oriented Architecture (SOA), where the system is split into coarse-grained services (user service, payment service, inventory service) that communicate over a network. Eventually, many companies move to microservices—fine-grained services owned by small teams, each with its own database and deployment cycle. Microservices enable organizational scaling but introduce complexity: distributed tracing, service discovery, eventual consistency, and operational overhead. The key insight: architecture should match your organizational and scale needs, not follow trends.
Trade-offs
dimension: Consistency vs. Availability option_a: Strong consistency guarantees that all clients see the same data at the same time. Reads always return the most recent write. This requires coordination between replicas, which means higher latency and potential unavailability during network partitions. Banks use strong consistency—you can’t have your account balance be different across ATMs. option_b: Eventual consistency allows replicas to temporarily diverge, with the guarantee that they’ll converge eventually. This enables high availability and low latency—reads can be served from any replica without coordination. Social media feeds use eventual consistency—if your friend’s post takes a few seconds to appear in your feed, that’s acceptable. decision_framework: Choose based on business requirements. Financial transactions need strong consistency. User-generated content, analytics, and caching can tolerate eventual consistency. The CAP theorem formalizes this: in a distributed system during a network partition, you must choose between consistency and availability—you can’t have both.
dimension: Latency vs. Throughput option_a: Optimizing for latency means minimizing the time to process a single request. This often involves caching, keeping data in memory, and avoiding network hops. Real-time systems like gaming or video calls prioritize latency—users notice delays over 100ms. option_b: Optimizing for throughput means maximizing the number of requests processed per second. This often involves batching, queuing, and accepting higher per-request latency. Batch processing systems like data pipelines prioritize throughput—it’s fine if each job takes seconds as long as you process millions per hour. decision_framework: User-facing features need low latency. Background jobs and analytics need high throughput. Sometimes you need both: Netflix’s video streaming needs low latency for playback start but high throughput for encoding millions of videos. The solution is often to separate these concerns into different components.
Common Pitfalls
pitfall: Premature Optimization why_it_happens: Engineers design for billions of users when they have thousands, adding complexity that slows development without providing value. They choose microservices for a team of three engineers, or implement complex sharding when a single database would work fine for years. how_to_avoid: Start simple and scale when you hit actual limits. Instagram ran on a monolithic Django app until they had millions of users. Measure first, optimize second. In interviews, explicitly state your assumptions about scale and explain how your design would evolve as requirements change.
pitfall: Ignoring Operational Complexity why_it_happens: Designs look elegant on whiteboards but become nightmares in production. Microservices sound great until you’re debugging a request that touches 15 services with no distributed tracing. Eventual consistency sounds fine until you’re explaining to users why their data disappeared. how_to_avoid: Consider the operational burden of every architectural decision. Who’s on-call when this breaks? How do you debug failures? What’s the blast radius of a bug? In interviews, discuss monitoring, alerting, and failure scenarios—this shows production maturity.
pitfall: Designing Without Requirements why_it_happens: Jumping straight to solutions without understanding constraints. Proposing a globally distributed system when all users are in one city. Choosing a NoSQL database without knowing query patterns. how_to_avoid: Always start with requirements: How many users? What’s the read/write ratio? What’s the acceptable latency? What’s the consistency requirement? In interviews, spend the first 10 minutes clarifying requirements before drawing any boxes. For the structured approach to gathering these requirements, see How to Approach System Design?.
Real-World Examples
company: Netflix system: Video Streaming Platform usage_detail: Netflix serves 230+ million subscribers across 190 countries, streaming billions of hours per month. Their system design prioritizes availability and performance over strong consistency. They use a microservices architecture with hundreds of services: one for user authentication, another for recommendations, another for video encoding. Videos are stored in AWS S3 and distributed globally via their Open Connect CDN—servers placed inside ISP networks to minimize latency. They use Cassandra (eventually consistent NoSQL) for viewing history because it’s fine if your “continue watching” list takes a few seconds to update. They use EVCache (distributed memcached) to cache everything from user profiles to video metadata, reducing database load by 99%. Their architecture can lose entire AWS regions and keep streaming because every component is replicated across availability zones. The key lesson: Netflix’s design matches their requirements—global scale, high availability, and acceptable eventual consistency for non-critical data.
company: Uber system: Ride Matching and Dispatch usage_detail: Uber processes millions of rides daily across 10,000+ cities, with strict latency requirements—riders expect a match within seconds. Their system design uses geospatial indexing (quadtrees) to efficiently find nearby drivers, sharded databases to distribute load across regions, and a dispatch system that optimizes matches in real-time. They use Kafka message queues to handle spikes in demand—ride requests are queued and processed asynchronously rather than overwhelming the system. Their payment processing requires strong consistency (you can’t charge a rider twice), so they use transactional databases with ACID guarantees. But their trip history can be eventually consistent—it’s fine if your past trips take a few seconds to appear. They use Redis for caching driver locations and surge pricing data, which changes constantly. The architecture evolved from a monolith to microservices as they scaled from one city to global operations. The key lesson: different parts of the system have different consistency and latency requirements, so Uber uses different technologies for different components.
company: Facebook (Meta) system: News Feed usage_detail: Facebook’s news feed serves personalized content to 3 billion users, processing millions of posts, likes, and comments per second. Their system design uses a multi-layered caching strategy: TAO (a distributed cache layer) sits in front of MySQL databases, serving 99% of reads from cache. They use a fan-out-on-write model for posts—when you publish, the system immediately writes to the feeds of all your friends, trading write amplification for fast reads. They shard data by user ID across thousands of database servers. They use eventual consistency for likes and comments—you might see different counts on different devices for a few seconds, which is acceptable for social media. They replicate data across multiple data centers for availability, using a custom consensus protocol to handle failures. For quantitative analysis of these capacity requirements, see Back-of-the-Envelope Estimation. The key lesson: Facebook optimizes for read latency (feeds must load instantly) by accepting write complexity and eventual consistency, matching their business requirements.
Interview Expectations
Mid-Level
Mid-level engineers should understand basic distributed systems concepts: load balancers, caching, database replication, and horizontal scaling. You should be able to design a simple system like a URL shortener or a basic social media feed, explaining why you chose SQL vs. NoSQL and where you’d add a cache. You don’t need to know every technology, but you should understand common patterns and be able to justify your choices with simple reasoning (“we need a cache because database queries are slow”).
Senior
Senior engineers must demonstrate quantitative thinking and tradeoff analysis. When designing Twitter, you should estimate request rates, storage requirements, and bandwidth needs, then use those numbers to drive decisions. You should know multiple solutions to each problem (“we could use Redis for caching OR Memcached, here’s the tradeoff”) and discuss failure scenarios (“if the cache goes down, we fall back to the database but implement rate limiting to prevent overload”). You should proactively mention monitoring, alerting, and operational concerns.
Staff+
Staff+ engineers must demonstrate strategic thinking and anticipate second-order effects. You should discuss how the system evolves over time, organizational impacts of architectural choices (“microservices enable team autonomy but require investment in platform tooling”), and cost implications (“this design costs $X/month at scale, here’s how we’d optimize”). You should identify subtle failure modes (“during a network partition, this design could lead to split-brain”) and propose solutions. You should connect technical decisions to business outcomes (“this architecture reduces time-to-market for new features, which is critical for our competitive position”).
Common Interview Questions
Design a URL shortener (tests basic CRUD, database design, and scaling)
Design Instagram (tests image storage, feed generation, and caching)
Design Uber (tests geospatial indexing, real-time matching, and consistency)
Design a rate limiter (tests distributed systems and algorithms)
Design Netflix (tests CDN, video streaming, and global distribution)
Red Flags to Avoid
Jumping to solutions without clarifying requirements or constraints
Using buzzwords without explaining tradeoffs (“we’ll use Kubernetes” without saying why)
Ignoring scale—designing for 1000 users when the requirement is 100 million
Not discussing failure scenarios or operational concerns
Being unable to estimate capacity or reason quantitatively about bottlenecks
Designing a perfect system that would take years to build instead of starting simple and evolving
Key Takeaways
System design is about architecting software systems that scale to millions of users while remaining reliable, available, and maintainable. It’s the difference between a system that crashes under load and one that handles exponential growth gracefully.
The four pillars—scalability, reliability, availability, and maintainability—drive every architectural decision. You can’t optimize for all of them simultaneously; good design means making explicit tradeoffs based on business requirements.
Architecture must match scale. A system for 1,000 users looks completely different from one for 100 million users. Start simple and evolve as you hit actual limits—premature optimization adds complexity without value.
Every design decision has tradeoffs: consistency vs. availability, latency vs. throughput, simplicity vs. flexibility. Understanding these tradeoffs and choosing the right ones for your context is the essence of system design.
In interviews, demonstrate structured thinking by clarifying requirements first, estimating capacity quantitatively, justifying tradeoffs explicitly, and discussing failure scenarios and operational concerns. Companies hire engineers who can build systems that work in production, not just on whiteboards.
Related Topics
Prerequisites
Basic understanding of databases, web servers, and HTTP
Familiarity with common data structures and algorithms
Next Steps
How to Approach System Design? - Learn the structured framework for tackling system design interviews
Back-of-the-Envelope Estimation - Master the quantitative analysis techniques for capacity planning
Scalability - Deep dive into horizontal vs. vertical scaling and distributed systems patterns
Database Design - Understand SQL vs. NoSQL, sharding, replication, and consistency models
Related Concepts
Load Balancing - Distribute traffic across multiple servers
Caching Strategies - Improve performance with Redis, Memcached, and CDNs
Message Queues - Handle async processing with Kafka and RabbitMQ
Microservices Architecture - Design distributed systems with independent services