System design is the process of deciding how a software system should be structured so it can meet its requirements at the expected scale.
It involves more than drawing boxes and arrows. Good system design explains what the system must do, how data moves through it, which components are responsible for which jobs, where it can fail or slow down, and what trade-offs the design is making.
In a small application, one server and one database may be enough. As traffic, data, users, and reliability expectations grow, the design usually needs more pieces: load balancers, caches, queues, replicas, partitions, monitoring, and failure handling.
The aim is the simplest architecture that satisfies the requirements.
System design is about making trade-offs under constraints.
If users need very low latency, you may add caching or serve data from regions closer to them. If the system must stay available during failures, you may add redundancy and failover. And if writes are growing too fast for one database, you may partition the data.
There is rarely one perfect design. A good design is one where the choices match the problem.
A common pitfall is starting from technologies: "Use Kafka, Redis, Kubernetes, and Cassandra."
A better approach is to start from requirements: "We need to handle high write volume, absorb traffic spikes, and process events asynchronously. That points toward a queue or log-based system."
A system design discussion usually revolves around a few recurring questions:
These questions shape the architecture more than any fixed template. The same product can have very different designs depending on its constraints.
For example, a chat app for a small internal team has different requirements than WhatsApp. A food delivery app in one city does not need the same multi-region strategy as a global marketplace.
A typical system is built from a small set of components. Not every system needs all of them, but each one solves a specific kind of problem.
System design is less about listing these components and more about explaining how requests and data flow through them.
For example, when a user opens a feed, does the request hit a cache first? If the cache misses, which service queries the database? Is the feed computed on read, precomputed on write, or built using a hybrid approach? Those choices are the real design.
A good design usually starts simple and adds complexity only when the requirements justify it. The process usually moves through five steps, with the architecture being revised as new pressures appear.
Before choosing an architecture, understand the problem.
Ask:
Requirements decide the architecture.
For example, "users can view a timeline" is not enough. A timeline with 10,000 daily users can be generated differently from a timeline with 500 million daily users and a 200 ms latency target.
Rough numbers help avoid designing the wrong system.
Estimate:
The aim is not exact math but to identify design pressure. A system handling 100 writes per second and one handling 1 million writes per second will not have the same bottlenecks.
Begin with the smallest design that can work:
Then add components only when there is a clear reason. A cache helps when repeated reads overload the database or latency is too high. A queue helps when work can happen asynchronously or when traffic spikes need buffering. Replicas help when read traffic grows or when availability needs the redundancy. Partitioning becomes necessary when a single machine can no longer hold or serve the data. Regional deployment makes sense when users are far away or availability requirements demand it.
Real systems evolve this way, one constraint at a time.
Once the baseline design is clear, look for weak points. What happens if the database goes down, or if traffic suddenly increases 10x? What happens if the cache is empty, or a queue consumer falls behind, or a third-party API becomes slow or unavailable? Which data can be stale, and which data must be correct immediately?
A good design acknowledges these failures and explains how the system behaves when they occur.
Every meaningful design choice has a cost.
Caching can reduce latency, but it introduces invalidation problems. Replication can improve availability and read throughput, but it creates consistency lag. Sharding can increase write capacity, but it makes queries and operations harder. Asynchronous processing can absorb spikes, but users may not see results immediately.
A useful design discussion covers both sides: why a choice helps and what new problem it creates.
In real life, system design is an ongoing process. Teams design, build, measure, learn, and revise. Existing systems also come with constraints: legacy code, team ownership, budgets, migrations, compliance rules, and operational history.
Interviews compress this into a 45 to 60 minute conversation about an ambiguous problem. A useful answer in that format clarifies the scope before solving, picks an architecture that matches the requirements, reasons about scale and bottlenecks, handles failures and trade-offs, and communicates the design clearly.
The same fundamentals apply in both settings: understand the problem, start simple, identify pressure points, and justify the choices.
System design is the skill of turning requirements into an architecture that can handle real-world constraints: scale, latency, reliability, correctness, operations, and cost.
The rest of this course focuses on building design judgment. For every component, the questions are the same: why does it exist, what problem does it solve, and what trade-off does it introduce?