system-design · beginner
System Design Foundations — How Large Software Fits Together
Start here
System design is how you plan a software system made of many parts so that it:
- Solves a real user problem
- Handles growth in users and data
- Survives partial failure
- Stays understandable and operable by humans
- Fits time and money constraints
You should care because interviews, production incidents, and architecture reviews all use the same foundation vocabulary: clients, servers, data stores, caches, queues, load balancers, consistency, and trade-offs.
This is a map lesson. Later lessons go deep on each idea. Here you learn what the pieces are, why they exist, and which questions to ask first.
What you will learn
- Define system design in plain English.
- Name the main building blocks of internet backends.
- Separate functional requirements from non-functional ones.
- Estimate order-of-magnitude load (back-of-envelope).
- Walk a simple photo-sharing design without drowning in buzzwords.
- Recognize classic trade-offs you will meet everywhere.
- Know what “good enough” means for a first design.
What you should know first
You can start with everyday web use:
- Apps on phones talk to servers somewhere else
- Websites store accounts and content
- Sometimes apps feel slow or break
Words you need before we begin
| Term | Plain English |
|---|---|
| Client | The app or browser acting for the user. |
| Server / service | A program that accepts requests and does work. |
| Request / response | A question and answer over the network. |
| API | Application Programming Interface — the agreed way to call a service. |
| Database | Durable storage for structured data. |
| Cache | Fast temporary storage of expensive results. |
| Load balancer | Spreads traffic across healthy servers. |
| Message queue | Holds work to be done later by workers. |
| Latency | How long one operation takes. |
| Throughput | How much work finishes per unit time. |
| Availability | How often the system successfully serves users when needed. |
| Scalability | Ability to handle more load by adding resources (well). |
| Fault tolerance | Keep useful work going when some parts fail. |
| Trade-off | A gain that costs something else (speed vs cost, consistency vs availability, and so on). |
Simple story: a food court, not one giant kitchen
A single restaurant kitchen (one server + one database) can feed a neighborhood. A city food court adds:
- Many stalls (services)
- A directory and lines management (routing / load balancing)
- Prefixed menus on boards (caches of popular answers)
- Runners taking tickets to stalls (async queues)
The problem without intentional design
The ball of mud
One application does UI, payments, email, search, and admin tools. Any change risks everything. Scaling means buying a bigger single machine until you cannot.
Copy-paste microservices
Twenty tiny services, twenty deployment pipelines, no clear data ownership, every request fans out to twelve dependencies. Latency and incidents explode.
Ignoring non-functional needs
“It works on my laptop” with 10 users fails for 10,000. Design forces you to ask about load, failure, and data growth early.
Step-by-step explanation
Step 1 — Clarify what you are building
Write:
- Who uses it?
- What core actions do they take?
- What must never go wrong (money, safety, privacy)?
Step 2 — Split functional vs non-functional requirements
| Functional (what) | Non-functional (how well) |
|---|---|
| Upload a photo | p95 upload API under 300ms metadata path |
| Show feed | Feed available 99.9% of monthly window |
| Follow a user | Correct privacy: private accounts enforced |
Interviews and production both punish designs that only list features.
Step 3 — Sketch the request path
Most internet systems share a skeleton:
Client → DNS → Load balancer → App servers → Database
↘ Cache
↘ Queue → Workers
You add pieces when a measured problem appears:
- Too much read load → cache or read replicas
- Slow third-party email → queue
- One server dies → multiple instances + health checks
Step 4 — Do a back-of-envelope estimate
Rough numbers beat vibes.
Suppose:
- 10 million daily active users
- Each opens the app 5 times/day
- Each open loads a feed of 20 items
You are not seeking perfect math. You are seeking whether you need fancy machinery.
Step 5 — Choose data storage deliberately
Questions:
- Relational transactions (orders, balances)?
- Large blob files (images)? → object storage
- Search by text? → search system
- High-volume events? → log/stream
Step 6 — Plan for failure from day one (lightly)
Even a small design should answer:
- What if one app instance dies?
- What if the database primary dies?
- What if the image store is slow?
Step 7 — State trade-offs out loud
Every solid design review includes sentences like:
- “We cache feed for speed; users may see a follow change a few seconds late.”
- “We write the payment synchronously; email is async so checkout stays fast.”
Visual mental model
flowchart TB
subgraph users [Users]
Mobile[Mobile app]
Web[Browser]
end
subgraph edge [Edge]
DNS[DNS]
LB[Load balancer]
end
subgraph core [Core services]
API[API servers]
Auth[Auth service]
end
subgraph data [Data plane]
DB[(Primary database)]
Cache[(Cache)]
Obj[Object storage]
Q[Queue]
W[Workers]
end
Mobile --> DNS
Web --> DNS
DNS --> LB
LB --> API
API --> Auth
API --> Cache
API --> DB
API --> Obj
API --> Q
Q --> W
W --> DB
Learning question: Which box would you add first if the database CPU is hot on read-heavy feed traffic?
Caption: Start simple; add cache/queue/replicas when estimates or metrics demand them.
Complete worked example: tiny photo service v1
Starting situation
Build SnapMini:
- Upload photo
- Follow users
- Home feed of recent photos from people you follow
Constraints
- Three backend engineers
- One cloud account
- Must keep cost low
- Photos can be private
Decisions (v1)
| Concern | Choice | Why |
|---|---|---|
| App tier | Stateless API × 2 behind load balancer | Survive one instance death |
| Metadata | Postgres | Users, follows, photo rows, transactions |
| Bytes | Object storage (S3-style) | Cheap large files |
| Feed | SQL query recent photos from follow set + short cache | Volume still small |
| Out of scope / async later | Not core path | |
| Auth | Managed auth or simple session service | Do not invent crypto |
Execution path: upload
- Client requests upload URL or sends multipart to API.
- API authorizes user.
- Bytes land in object storage.
- API inserts photo metadata row.
- Returns photo id.
Failure behavior
- One API instance dies → balancer uses the other.
- Object storage slow → upload latency rises; metadata not committed until store succeeds (define policy).
- DB down → writes fail closed; show error.
Outcome
A boring design that can ship. Limitations: feed query may not survive 100× growth—document the scaling path (cache, fan-out on write) without building it yet.
How it works in production
Real companies encode foundations as:
- Architecture Decision Records (ADRs) for major choices
- SLIs/SLOs for reliability targets
- Runbooks for failure modes
- Progressive delivery so bad deploys hit few users first
Failure modes of “design process” itself
| Mode | Symptom | Fix |
|---|---|---|
| Premature scale | Kafka + multi-region for 200 users | Start simpler; re-evaluate with metrics |
| No numbers | Endless debate | Back-of-envelope + prototypes |
| Hidden requirements | Security/privacy bolted on late | Include non-functionals early |
| Single hero architect | Bus factor 1 | Shared design reviews |
| Diagram worship | Pretty boxes, unclear APIs | Define interfaces and data ownership |
Trade-offs you will reuse
| Tension | Classic directions |
|---|---|
| Consistency vs availability/latency | Strong reads vs faster stale reads |
| Cost vs headroom | Over-provision vs scale-to-zero risk |
| Simplicity vs isolation | Monolith vs many services |
| Build vs buy | Managed DB vs self-run |
| Sync vs async | Immediate correctness vs decoupling |
Compare with related concepts
| Lesson | Role after this foundation |
|---|---|
| Latency, throughput, bandwidth | Quantify performance |
| Availability / reliability / fault tolerance | Quantify and design for failure |
| Load balancing / caching / queues | Concrete building blocks |
| CAP / consistency models | Data correctness under distribution |
Common misunderstandings
- “System design = microservices.”
- “More boxes means better design.”
- “If it passes the interview, it is production-ready.”
- “Non-functional requirements are optional polish.”
- “We can add security later.”
Check your understanding
- What is system design in one sentence?
- Give two functional and two non-functional requirements for a ride-sharing app.
- Why estimate load before adding a cache?
- Name three building blocks in a typical web backend path.
- State one trade-off in the SnapMini v1 design.
Practice
- Write requirements for a URL shortener (functional + non-functional).
- Back-of-envelope: 1M new URLs/day, 100M redirects/day—what is stressed more, write path or read path?
- Draw a v1 design with ≤6 component types.
- List three failures and how v1 behaves.
- Propose what you would change at 100× traffic—and what you would not change yet.
Revision summary
- System design chooses structure under goals and constraints.
- Separate what the system does from how well.
- Start from user journeys and a simple path; add complexity with evidence.
- Use estimates to justify caches, queues, and splits.
- Always name trade-offs and failure behavior.
Glossary
| Term | Definition |
|---|---|
| System design | Planning components and interactions to meet requirements. |
| Non-functional requirement | Quality attribute such as latency, availability, security. |
| Stateless service | Instance that does not keep must-have session memory locally. |
| Back-of-envelope estimate | Rough capacity math to guide design. |
Abbreviations and terminology
- API — Application Programming Interface
- DNS — Domain Name System
- HTTP — Hypertext Transfer Protocol
- p95 — 95th percentile
- ADR — Architecture Decision Record
What to learn next
FAQ from first-time learners
Q: How detailed should a first diagram be?
A: Enough to show request flow, data stores, and failure points—not every class name.
Q: Do I need Kubernetes to design systems?
A: No. Learn concepts first. Orchestration is an implementation choice.
Q: Where do interviews stop and real jobs start?
A: Interviews reward clear requirements, estimates, and trade-offs. Jobs add stakeholders, migrations, and multi-year evolution.
Track: Distributed Systems
Next: CAP Theorem — Consistency, Availability, and Partition Tolerance
By Shubham Jain