At a Glance
- Scaling = handling growth. Vertical (bigger box) vs horizontal (more boxes).
- Latency = time per request; throughput = requests per second. Different problems, different fixes.
- Availability is counted in nines: each extra nine ≈ 10× harder.
- Stateless services scale horizontally for free; stateful ones need sticky sessions or an external store.
- Amdahl's Law: the serial fraction of your system caps your speedup, no matter how many servers you add.
- Napkin math: 1 day ≈ 100K seconds, 2^10 ≈ 1K, 2^30 ≈ 1B. State assumptions out loud.
What Does "Scaling" Actually Mean?
Your app is on one server, happily serving 100 users. Then it lands on the front page of Hacker News and 100,000 people show up. Scalability is the property that lets your system handle that growth — in users, data, or traffic — without falling over or requiring a rewrite.
There are exactly two directions you can grow, and the difference between them shapes almost every other decision in this course.
Vertical scaling makes one box bigger. Horizontal scaling adds more boxes.
Vertical scaling (scale up)
Swap your server for a beefier one: more CPU cores, more RAM, faster disks. Zero code changes. But hardware has a ceiling, prices grow non-linearly at the high end, and you still have a single point of failure.
Horizontal scaling (scale out)
Add more commodity machines and spread the work. Nearly unlimited headroom and built-in redundancy — but now you need load balancing, shared state, and you've officially entered distributed-systems territory.
| Vertical (Up) | Horizontal (Out) | |
|---|---|---|
| Complexity | Low — same code, bigger box | High — LBs, replication, partitioning |
| Ceiling | Hard hardware limit | Practically unbounded |
| Fault tolerance | Single point of failure | Survives individual machine deaths |
| Cost curve | Steep at the top end | Linear with commodity hardware |
| Best first move for | Databases, early-stage apps | Stateless web/API tiers |
Real systems do both! Stack Overflow famously ran on a handful of very tall servers for years, while Google went wide with hundreds of thousands of cheap ones. Scale up until it hurts, then scale out where it counts.
Stateless vs Stateful Scaling
Horizontal scaling has a prerequisite that trips up almost every team: where does the session live? The answer determines whether "just add more servers" actually works.
Stateless
No server stores session data between requests. Any request can go to any server — the load balancer routes freely, dead servers are replaced without drama. Examples: REST APIs, static file servers.
Stateful
The server keeps your session in its own memory. Now the same user must hit the same server every time (sticky sessions), or you need an external session store like Redis. Lose the server, lose the session.
Stateless servers are cattle; stateful servers are pets. Cattle scale.
Making a stateful app stateless
- Move session data out of server memory into Redis or memcached — any server can fetch any user's session in <1 ms.
- Or skip server sessions entirely with JWTs: the signed token carries the user's identity, so the server stores nothing.
- Push files to object storage (S3), not local disk; push caches to a shared tier.
- Once stateless, autoscaling, rolling deploys and instant failover all come for free.
Netflix made its entire streaming backend stateless — any of their ~10,000 servers can handle your request. That's what lets Chaos Monkey kill random production servers all day without a single customer noticing.
Amdahl's Law: The Ceiling on "Just Add Servers"
If part of your workload can't be parallelized, that part caps your maximum speedup — forever. Gene Amdahl formalized it:
# P = fraction of work that is parallelizable, N = processors/servers
Speedup = 1 / ((1 − P) + P / N)
# Example: 90% parallelizable (P = 0.9)
N = 10 → speedup = 1 / (0.1 + 0.09) ≈ 5.3×
N = 100 → speedup = 1 / (0.1 + 0.009) ≈ 9.2×
N = ∞ → speedup = 1 / 0.1 = 10× (hard ceiling!)
Going from 100 servers to 1,000 buys you 0.6× more speedup. The serial 10% — locks, single-threaded coordinators, the database — is the bottleneck.
- If 90% of your code is parallelizable, max speedup is 10× — no matter how many cores or servers.
- The serial portion hides in plain sight: locks, single-leader databases, coordination services, "the one queue everything flows through."
- This is the mathematical reason "just add more servers" hits diminishing returns — scale-out only attacks the parallel fraction.
- The highest-leverage optimization is often shrinking the serial part (sharding the DB, removing locks), not adding node #101.
Performance vs Scalability
These two words get used interchangeably, but they describe different problems:
- A performance problem: your system is slow for a single user. Even with one request in flight, it takes 4 seconds.
- A scalability problem: your system is fast for one user but slow under load. One request takes 50 ms; ten thousand concurrent requests take 30 seconds each.
The fixes differ too. Performance problems call for better algorithms, indexes, or caching. Scalability problems call for more capacity and removing bottlenecks — the slowest shared resource (often the database) determines how far you can grow.
- Fast but unscalable: great demo, terrible launch day.
- Scalable but slow: handles a million users, annoys every single one.
- You need both — measure each separately and fix the right one.
Latency vs Throughput
Latency is how long one request takes (measured in milliseconds). Throughput is how many requests you complete per unit of time (measured in requests per second, or QPS).
Think of a highway: latency is how long your car takes to get downtown; throughput is how many cars pass the toll booth per minute. Adding lanes (horizontal scaling) raises throughput but doesn't make any individual car faster. Raising the speed limit (performance work) lowers latency for everyone.
Engineers quote latency as p50 / p95 / p99 — the time within which 50%, 95%, or 99% of requests finish. Averages hide pain: a 20 ms average with a 3-second p99 means 1 in 100 users is having a miserable time. Amazon famously found that every 100 ms of extra latency cost ~1% in sales.
Availability: Counting the Nines
Availability is the fraction of time your system is up and answering. It's quoted in "nines" — and each extra nine is roughly 10× harder (and more expensive) to achieve.
| Availability | Nickname | Downtime / year | Downtime / month |
|---|---|---|---|
| 99% | "two nines" | ~87.6 hours (3.65 days) | ~7.3 hours |
| 99.9% | "three nines" | ~8.77 hours | ~43.8 minutes |
| 99.99% | "four nines" | ~52.6 minutes | ~4.4 minutes |
| 99.999% | "five nines" | ~5.26 minutes | ~26 seconds |
Five nines means your total allowed outage for an entire year fits inside a coffee break — including deploys, hardware failures, and that one DNS change everyone regrets. This is why phone carriers and payment networks invest enormously in redundancy, while an internal dashboard happily lives at two nines.
If a request must pass through two services that are each 99.9% available, the path is only 99.9% × 99.9% ≈ 99.8% available. Chains of dependencies quietly eat your nines — Topic 03 covers how to fight back with redundancy in parallel.
Latency Numbers Worth Memorizing
These rough magnitudes (popularized by Jeff Dean at Google) explain why system design patterns exist. Caching exists because RAM is ~100× faster than SSD. CDNs exist because cross-ocean round trips cost ~150 ms no matter how good your code is.
| Operation | Approx. time | Relative magnitude (log scale) | Scaled up (1 ns → 1 sec) |
|---|---|---|---|
| L1 cache reference | ~1 ns | 1 second | |
| L2 cache reference | ~4 ns | 4 seconds | |
| Main memory (RAM) read | ~100 ns | ~2 minutes | |
| SSD random read | ~100 µs | ~28 hours | |
| Read 1 MB sequentially from SSD | ~1 ms | ~12 days | |
| HDD seek | ~10 ms | ~4 months | |
| Round trip within a datacenter | ~0.5 ms | ~6 days | |
| Round trip US ↔ Europe | ~150 ms | ~5 years |
Memory is fast, disk is slow, and the network across the world is glacial. Every caching layer, replica, and CDN in this course is just an attempt to move data up that table.
Back-of-Envelope Estimation
Interviewers (and capacity planners) want quick, defensible estimates — not precision. The trick is a small set of conversions and aggressive rounding.
e.g. 10 million daily active users (DAU), each making ~10 requests per day → 100 million requests/day.
A day has ~86,400 seconds — round to 100k. So 100M ÷ 100k ≈ 1,000 QPS average. Peak traffic is typically 2–5× average → plan for ~3,000–5,000 QPS.
If 1% of requests write a 1 KB record: 1M writes/day × 1 KB = 1 GB/day ≈ 365 GB/year (before replication — multiply by 3 for safety copies).
1,000 QPS × 100 KB average response ≈ 100 MB/s outbound. That's the number that tells you whether you need a CDN (you do).
# Handy conversions to memorize
1 day ≈ 100,000 seconds # actually 86,400 — round it
1M requests/day ≈ 12 QPS # so 100M/day ≈ 1,200 QPS
2^10 ≈ 1 thousand (KB) 2^20 ≈ 1 million (MB)
2^30 ≈ 1 billion (GB) 2^40 ≈ 1 trillion (TB)
"Assuming 10M DAU and a 5× peak factor…" is a sentence that wins interviews. The estimate matters less than showing you know which knobs exist.
Powers of Two — Your Mental Abacus
All capacity math in system design runs on powers of two. Memorize these four anchors and you can estimate anything in your head:
| Power | Exact value | Approximation | Name |
|---|---|---|---|
| 210 | 1,024 | ~1 thousand | 1 KB (kilo) |
| 220 | 1,048,576 | ~1 million | 1 MB (mega) |
| 230 | 1,073,741,824 | ~1 billion | 1 GB (giga) |
| 240 | 1,099,511,627,776 | ~1 trillion | 1 TB (tera) |
Storage conversions
1 byte = 8 bits
1 KB = 1,024 bytes 1 MB = 1,024 KB
1 GB = 1,024 MB 1 TB = 1,024 GB
Sizes of common things
ASCII char
1 byte. A tweet of 280 ASCII chars ≈ 280 bytes.
UTF-8 char
1–4 bytes. Emoji and CJK characters take the most.
Integer
4 bytes. A long is 8 bytes.
UUID
16 bytes raw (36 chars as text — store it raw!).
Quick storage estimates
- 10M users × 100 bytes each = 1 GB — fits in RAM on a laptop.
- 1B users × 100 bytes each = 100 GB — fits on one server's SSD.
- The lesson: user metadata is almost never your storage problem. Media, logs, and events are.
Worked Estimation: A Twitter-Scale App
Let's put all the napkin math together on a real interview classic. State assumptions first, then crunch:
300M daily active users. Each user posts 2 tweets/day and reads 100 tweets/day. ~86,400 seconds per day.
300M × 2 ÷ 86,400 ≈ 7,000 writes/sec average. Apply a 5× peak factor → ~35,000 writes/sec at peak.
300M × 100 ÷ 86,400 ≈ 350,000 reads/sec average; peak ≈ 1.75M reads/sec. Notice the ratio: reads:writes = 50:1.
Average tweet ~300 bytes of text + ~200 bytes of metadata = 500 B/tweet. 500 B × 7,000/sec × 86,400 × 365 ≈ 110 TB/year — manageable with sharding.
If 10% of tweets carry an image (~200 KB avg): 600M tweets/day × 10% × 200 KB ≈ 12 TB/day → petabytes per year. CDN + object storage are mandatory, not optional.
# Twitter-scale summary
writes: 7K/sec avg → 35K/sec peak
reads: 350K/sec avg → 1.75M/sec peak # 50× more reads than writes!
tweets: ~110 TB/year of text+metadata
media: petabytes/year → CDN mandatory
Because reading is 50× more common than writing, Twitter pre-computes timelines into read-optimized fanout caches: a write does extra work once so that millions of reads become cheap cache hits. The estimation isn't busywork — it tells you where to spend your complexity budget.
Quick Check
Your API is blazing fast for one user but grinds to a halt during peak hours. What kind of problem is this?
Interview Q&A
Three questions interviewers actually ask about fundamentals — click to reveal strong answers.
Latency = time for one operation. Throughput = operations/sec. They often trade off — batching improves throughput but hurts individual latency. You optimize latency with faster paths (caches, better algorithms, fewer hops). You optimize throughput with concurrency, batching, and eliminating serial bottlenecks. In practice: optimize for latency first for user-facing services, throughput first for batch/analytics pipelines.
99.9% × 99.9% × 99.9% ≈ 99.7% — about 26 hours of downtime per year. This is why microservices systems need circuit breakers, retries with backoff, and aggressive timeouts. Services in parallel (any one can serve the request) multiply uptime instead: 1 − (0.1%)³ ≈ 99.9999%.
Vertical is simpler — no distributed-systems complexity. Choose it first: scale a single powerful node until the economics break (~$20K/month for a massive RDS instance). Horizontal requires statelessness, load balancing, replication, sharding. Databases often stay vertical longer because distributed writes are hard; stateless web tiers go horizontal naturally.
Anti-Patterns to Avoid
Premature horizontal scaling
Adding nodes before making the app stateless. Creates sticky-session nightmares — sessions vanish on every deploy and the load balancer can never rebalance.
Ignoring percentiles
Optimizing for p50 (median) when p99 is 10× worse. The median user is fine; the 1-in-100 user is rage-quitting. Always look at p95/p99.
Confusing performance and scalability fixes
Caching a slow algorithm doesn't make it scalable; it just delays the problem until the cache misses pile up. Diagnose which problem you actually have first.
IP Addresses
An IP (Internet Protocol) address is a unique numerical label assigned to every device on a network. It serves two purposes: identifying the host or network interface, and providing the location of the device in the network so data can be routed to it correctly.
IPv4 uses 32-bit addresses written as four decimal octets separated by dots (e.g., 192.168.1.1). This gives roughly 4.3 billion possible addresses — a number that ran out as the internet grew. IPv6 uses 128-bit addresses written in hexadecimal groups separated by colons (e.g., 2001:0db8:85a3::8a2e:0370:7334), providing 340 undecillion addresses — effectively limitless for the foreseeable future. IPv6 also adds built-in security (IPSec), better routing efficiency, and no need for NAT.
Public vs. Private IP Addresses: Public IPs are globally routable on the internet and assigned by ISPs — every web server you access has one. Private IPs are used inside local networks (home, office, data centre) and are not routable on the public internet. The reserved private ranges are 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16. Your laptop at home has a private IP; your router has the public IP.
NAT — Network Address Translation: Because private IPs cannot communicate directly on the internet, NAT lets an entire network share a single public IP. The router rewrites the source IP of outgoing packets to the public IP, tracks active connections in a translation table, and rewrites incoming responses back to the originating private IP. This is how millions of home devices share one public address from an ISP.
CIDR and Subnets: CIDR (Classless Inter-Domain Routing) notation expresses an IP address range compactly: 10.0.0.0/24 means the first 24 bits are the network prefix and the remaining 8 bits are host addresses (256 addresses, 254 usable). Subnetting divides a large network into smaller segments to improve security, reduce broadcast traffic, and allow fine-grained routing. Cloud VPCs (Virtual Private Clouds) always use CIDR to define address spaces.
Static vs. Dynamic IPs: A static IP is fixed and never changes — used for servers, printers, and devices that must always be reachable at the same address. A dynamic IP is assigned temporarily by DHCP (Dynamic Host Configuration Protocol) and may change when a device reconnects. Home users typically get dynamic IPs from their ISP; production servers require static IPs (or elastic IPs in cloud environments).
Loopback and Special Addresses: 127.0.0.1 (or localhost) is the loopback address — traffic sent here never leaves the machine and is used for testing local services. 0.0.0.0 means "all interfaces" when binding a server socket. 255.255.255.255 is the broadcast address (send to all devices on the subnet).
The Client-Server Model
The client-server model is the foundational architecture of the modern web. A client is any process that initiates a request for a service or resource. A server is a process that listens for and responds to those requests. The two communicate over a network using a well-defined protocol (HTTP, TCP, WebSockets, etc.).
How it works: The client sends a request (e.g., GET /api/products) including headers, authentication tokens, and optionally a body. The server receives the request, validates it, executes the business logic (queries a database, calls other services), and returns a response with a status code, headers, and a body (HTML, JSON, binary data). The client then renders or processes that response. This request-response cycle repeats for every interaction.
Types of clients: Web browsers (Chrome, Safari), mobile applications (iOS, Android), command-line tools (curl, wget), API clients (Postman, other backend services), and IoT devices all act as clients. The defining characteristic is that the client initiates the communication.
Types of servers: Web servers (NGINX, Apache) serve HTTP responses. Application servers (Node.js, Tomcat, Gunicorn) run business logic. Database servers (PostgreSQL, MySQL, MongoDB) store and query data. File servers store and serve files. Cache servers (Redis, Memcached) serve fast in-memory data. A real production system chains several of these together — the client talks to a web server, which delegates to an application server, which reads from a database server.
Client-Server vs. Peer-to-Peer (P2P): In client-server, the server is a dedicated, always-on machine and the clients are transient. In P2P (BitTorrent, blockchain nodes), every participant can be both client and server simultaneously. P2P is more resilient (no single point of failure) but harder to secure and control. Most production systems use client-server because it is easier to reason about, scale, monitor, and secure.
Scaling the server side: A single server eventually becomes the bottleneck. Vertical scaling (bigger machine) has limits. Horizontal scaling (multiple server instances behind a load balancer) is the standard approach for production. Stateless servers — where the server holds no session data between requests — are easy to scale horizontally because any server can handle any request. Stateful servers (holding session data in memory) require sticky sessions or external session stores (Redis) to scale.
The two-tier and three-tier models: A two-tier model has the client talk directly to the database — simple but insecure and hard to scale. The three-tier model (client → application server → database) is the industry standard: the application server enforces business logic and access control, and the database is never exposed directly. Additional tiers (cache, API gateway, CDN) are added as traffic grows.
Real-world example — how a tweet loads: Your browser (client) sends an HTTPS GET request to Twitter's CDN edge. The CDN serves cached assets; the dynamic feed request goes to Twitter's load balancer, which routes to an application server. The application server queries a distributed database cluster and a Redis cache for your timeline. The response travels back through the chain in milliseconds — each layer adding capability while hiding complexity from the client.
Fundamentals Interview Questions
All key questions and answers from this topic that appear in system design interviews.
IP Addresses Questions
IPv4 is a 32-bit address (e.g., 192.168.1.1) supporting ~4.3 billion unique addresses. It is the most widely used today but is running out of available addresses.
IPv6 is a 128-bit address (e.g., 2001:db8::1) supporting approximately 340 undecillion addresses. It was created to solve IPv4 exhaustion and has built-in security features (IPSec).
Key difference: IPv4 uses dotted decimal notation (32-bit); IPv6 uses hexadecimal notation separated by colons (128-bit) and has a vastly larger address space.
Private IP addresses are reserved ranges not routable on the public internet. They are used within local networks (LANs) to allow multiple devices to share a single public IP.
Private IP ranges: 10.0.0.0–10.255.255.255, 172.16.0.0–172.31.255.255, 192.168.0.0–192.168.255.255.
Why needed: IPv4 address exhaustion — billions of devices share a limited pool of public IPs. Private IPs also improve security by hiding internal network topology from the internet.
NAT translates private IP addresses into a public IP address when traffic leaves a local network, and reverses the translation for incoming responses.
How it works: A device with private IP 192.168.1.10 sends a request. The router translates this to its public IP (e.g., 203.0.113.5) and tracks the mapping in a NAT table. When the response arrives, the router maps it back to the original private device.
Why it matters: NAT allows many devices to share one public IP, conserving IPv4 addresses while providing an additional security layer (internal addresses are hidden from the internet).
A load balancer sits between clients and backend servers and uses IP addresses to route traffic efficiently.
Virtual IP (VIP) — The load balancer exposes a single public IP (VIP) to clients. All incoming traffic goes to this address.
Backend IPs — The load balancer distributes requests to multiple backend servers using their private IPs.
Source NAT — The load balancer may replace the client's IP with its own when forwarding requests, or preserve the original IP using X-Forwarded-For headers.
DNS translates human-readable domain names (e.g., google.com) into IP addresses that computers use to communicate.
In large-scale systems: DNS can return multiple IP addresses for a single domain (round-robin DNS load balancing). Geo-DNS routes users to the nearest server based on their IP location. Low TTL values allow rapid failover when server IPs change. DNS-based health checks remove failed server IPs from responses.
DNS and IP addresses together are the backbone of request routing in distributed systems.
Client-Server Model Questions
The client-server model is an architectural pattern where two components — the client and the server — communicate over a network. The client is any device or application that requests a service or resource. The server is a system that receives the client's request, processes it, and returns a response.
Most internet-based applications (websites, mobile apps, APIs) follow this model. It enables scalable service delivery by centralizing logic and data on servers.
Clients communicate with servers using network protocols, most commonly HTTP/HTTPS. The flow: (1) Client sends a request (e.g., HTTP GET). (2) Request travels over the network using TCP/IP. (3) Server receives and processes the request. (4) Server sends back a response with data and a status code. (5) Client receives and renders or processes the response.
Web Browsing — Browser (client) requests a page from a web server. Email — Email client (Outlook, Gmail app) talks to an email server. Mobile Apps — Uber app (client) fetches ride data from Uber's servers. Gaming — Game client connects to game servers for real-time multiplayer. Databases — Application server (client) queries a database server.
Client: Initiates communication; sends requests; typically handles UI/UX (browser, mobile app); has limited processing power.
Server: Responds to client requests; always available and listening; handles business logic, database queries, authentication; high processing power and storage capacity.
Note: A machine can be both — a web server (serving clients) can itself be a client when querying a database server.
The HTTP request-response cycle is the foundation of web communication:
1. Client resolves the domain via DNS → gets the server IP. 2. Client establishes a TCP connection (three-way handshake). 3. Client sends an HTTP request (method, headers, optional body). 4. Server processes the request (queries DB, runs logic). 5. Server returns HTTP response (status code, headers, body). 6. Client processes the response and renders the result.
Synchronous: Client waits for server response before continuing. Simpler but can block the user. Example: Traditional HTTP request/response — the browser waits for the page to load.
Asynchronous: Client sends request and continues doing other work; processes response when it arrives. More complex but non-blocking. Example: AJAX calls — the page stays interactive while data loads in the background. WebSockets — real-time updates without the client polling.
1. DNS Resolution — Browser resolves the domain name to an IP address. 2. TCP Connection — Browser establishes a TCP connection (three-way handshake). 3. HTTP Request — Browser sends a GET request for the page. 4. Server Response — Server returns HTML, CSS, JS files. 5. Rendering — Browser parses HTML, loads CSS/JS, executes scripts, and renders the page. 6. Sub-resources — Browser makes additional requests for images, fonts, and APIs.
Stateless Server — Does not retain information between requests. Each request is independent and must contain all necessary data. Easier to scale horizontally. Example: RESTful APIs — every request includes auth tokens and required parameters.
Stateful Server — Retains client state between requests (e.g., session data). More complex to scale (sessions must be tied to a specific server). Example: Traditional session-based login systems.
Modern large-scale systems prefer stateless servers and offload state to distributed caches (Redis) or databases.
Caching stores frequently requested data at various layers to avoid redundant processing:
Browser Cache — Stores static assets (CSS, images) locally so the browser doesn't re-download them.
CDN Cache — Edge servers store content closer to users, reducing latency.
Server-Side Cache (Redis/Memcached) — Stores query results in memory to avoid hitting the database on every request.
Database Query Cache — Frequently run queries are cached at the DB level.
A load balancer sits between clients and servers, distributing incoming requests across multiple server instances. From the client's perspective, it communicates with a single address (the load balancer). The load balancer routes each request to the least busy or most appropriate server. This improves availability (server failures are handled transparently), scalability (add more servers to handle more load), and performance (requests go to healthy, available instances).
Man-in-the-Middle (MITM) Attacks — An attacker intercepts communication. Solution: Use HTTPS/TLS encryption.
DDoS Attacks — Flooding the server with excessive requests. Solution: Rate limiting, CDN, firewalls.
SQL Injection — Malicious SQL in client input affects the database. Solution: Parameterized queries, input validation.
Unauthorized Access — Clients accessing resources without permission. Solution: Authentication (JWT, OAuth) and authorization (RBAC).
Data Breaches — Sensitive data stolen in transit or at rest. Solution: Encryption at rest and in transit.
HTTP — Client initiates every request; connection closes after each response; one-directional (client pulls data). Suitable for standard web pages and REST APIs.
WebSockets — Persistent, full-duplex connection; both client and server can send messages at any time; low overhead after initial handshake. Suitable for real-time applications: chat, live feeds, collaborative tools, multiplayer games.
Server Dependency — If the server goes down, clients cannot access services.
Scalability Challenges — A single server can become a bottleneck under high load.
Latency — Geographic distance between client and server adds latency.
Cost — High-availability, scalable server infrastructure is expensive.
Security Surface — Centralized servers are prime targets for attacks.
Solutions include horizontal scaling, CDNs, distributed architectures, and caching layers.
Global Load Balancers — Route users to the nearest data center using GeoDNS.
CDN — Serve static content (images, CSS, JS) from edge locations close to users.
Horizontal Scaling — Multiple server instances behind a load balancer; auto-scaling based on traffic.
Caching Layer — Redis/Memcached to reduce database load for repeated queries.
Database Replication — Read replicas close to users; write to primary database.
Microservices — Decompose monolithic server into independent services for independent scaling.
Monitoring & Auto-Recovery — Health checks, alerting, and automatic failover.
Scalability & Performance Interview Questions
Key interview questions covering scalability, scaling strategies, system performance, and autoscaling.
Scalability Questions
Scalability is the ability of a system to handle increased load — whether that's more users, more data, or more traffic — without compromising performance or reliability. In system design, this often means the system can grow linearly or elastically with demand, using techniques like horizontal scaling (adding more machines) or vertical scaling (upgrading hardware). A scalable system maintains responsiveness, throughput, and availability as demand increases.
One famous example is Twitter. Initially, Twitter struggled to scale and became known for its "fail whale" downtime during traffic surges. Their early monolithic architecture couldn't handle rapid user growth. They later migrated to a distributed, microservices-based architecture to enable horizontal scaling.
Alternatively, Zoom successfully scaled during the COVID-19 pandemic, going from 10 million to over 300 million daily participants. Their use of cloud-native infrastructure and autoscaling allowed them to absorb the sudden spike in demand.
Some of the biggest challenges include:
Latency — More components and network hops introduce delays. Bottlenecks — A single overloaded component can affect the entire system. Downtime — More nodes and dependencies increase the risk of failure. Cost — Scaling up infrastructure, especially in the cloud, can become expensive.
These challenges require proactive architectural planning, monitoring, and scaling policies to handle gracefully.
To identify bottlenecks: (1) Use observability tools (like Prometheus, Grafana, Datadog) to monitor performance metrics. (2) Look for sudden spikes in CPU, memory, or latency. (3) Trace requests end-to-end using distributed tracing tools like OpenTelemetry or Jaeger. (4) Use load testing tools (like JMeter or k6) to simulate traffic and see where degradation begins. (5) Identify services or layers where queue lengths grow or response times increase disproportionately.
As systems scale: more services are added (more network calls), data is sharded or partitioned (aggregation needed), and dependency chains grow (longer request paths).
Mitigation strategies: Caching frequently accessed data (e.g., Redis); asynchronous processing for non-critical tasks (queues, background jobs); reducing network hops via edge computing or CDNs; optimizing DB queries and reducing cross-service chatter.
Balancing scalability with cost involves: using autoscaling with upper/lower bounds to prevent overprovisioning; choosing serverless or FaaS (like AWS Lambda) for event-driven workloads; implementing tiered caching to reduce load on primary databases; leveraging spot or reserved instances where possible; and monitoring and right-sizing infrastructure regularly based on usage patterns. A cost-effective system scales efficiently while only using resources when needed.
Scaling Strategies Questions
Horizontal Scaling (Scaling Out) — Adding more machines or instances to distribute the load. Example: Adding more application servers behind a load balancer. Scales well for web apps and microservices, but adds complexity in state management, coordination, and deployment.
Vertical Scaling (Scaling Up) — Increasing the resources (CPU, RAM, disk) of a single server. Example: Upgrading a database server from 16GB RAM to 64GB RAM. Simpler and faster to implement but has physical/OS limits and can be a single point of failure.
Diagonal Scaling is a hybrid approach: start with vertical scaling (easier, cheaper for small scale), then move to horizontal scaling as load increases beyond a single machine's limits.
Use cases: Startups — begin with minimal infra and gradually scale horizontally as demand grows. Systems with burst traffic — use vertical scaling for fast reactions, and horizontal for long-term elasticity. This approach balances simplicity and future-proofing, avoiding premature complexity.
Performance: Horizontal scaling can scale almost linearly (with effort); vertical scaling is limited by single machine capacity. Complexity: Horizontal requires distributed system design; vertical is easier to manage initially. Fault Tolerance: Horizontal is more resilient (failover possible); vertical is a single point of failure. Cost: Horizontal has higher operational cost and complexity; high-capacity machines for vertical are expensive. Deployment: Horizontal requires orchestration and load balancing; vertical has simple deploys with fewer moving parts.
Summary: Horizontal = more scalable, resilient, but complex. Vertical = quick wins, limited long-term.
Horizontal scaling won't help when: the workload isn't parallelizable (e.g., a legacy monolithic app that relies on shared state or global locks); there's a non-distributable bottleneck like a single-threaded operation or a relational DB that doesn't scale well horizontally; or there's a stateful service without session persistence or sticky sessions in the load balancer.
Real-world example: Scaling a PostgreSQL database for analytics queries — simply adding more nodes won't help unless sharding or read replicas are implemented.
Choose vertical scaling when: you need a quick fix without refactoring code; the system is monolithic or tightly coupled; your team lacks experience with distributed systems; you're in the early stages of a project/startup; or the system has low concurrency needs and doesn't warrant added complexity.
Vertical scaling is ideal when you don't need high elasticity or can't justify the cost/complexity of a distributed architecture.
State Management — Solution: Use external stores like Redis or Memcached for sessions.
Data Consistency — Solution: Implement distributed transactions, eventual consistency patterns, or use CQRS.
Load Distribution — Solution: Use smart load balancers (e.g., with least-connections or IP hashing).
Service Discovery — Solution: Use tools like Consul, Eureka, or cloud-native DNS-based discovery.
Deployment & Orchestration — Solution: Use containers (Docker) and orchestrators like Kubernetes for managing instances.
Increased Latency — Solution: Minimize inter-service calls, implement caching, and optimize communication paths.
System Performance Questions
Latency is the time it takes for a system to respond to a request (e.g., response time in milliseconds). Throughput is the number of requests the system can handle in a given time (e.g., requests per second).
Analogy: If a highway is a system, latency is how fast one car can travel, while throughput is how many cars can pass per hour. These metrics often trade off — optimizing one can hurt the other.
SLA (Service Level Agreement) — A contractual commitment (e.g., 99.9% uptime for a paid service). SLO (Service Level Objective) — Internal performance target (e.g., 95% of API requests respond <200ms). SLI (Service Level Indicator) — The actual measured metric (e.g., current API latency = 92% <200ms).
Example: An e-commerce platform might offer an SLA of 99.9% uptime, have an SLO of 99.95%, and monitor uptime via SLI metrics from uptime checks.
Averages can hide outliers — 90% of requests might be fast, but 10% could be very slow. P95 means 95% of requests are faster than this threshold. P99 captures the tail latency — the slowest 1% of requests that often impact user experience.
Monitoring percentiles helps surface real-world slowness that affects users and drives better performance tuning.
Use profiling and monitoring tools (e.g., New Relic, Grafana, Datadog). Break down the request path: DB calls, service-to-service latency, cache hits/misses. Look at resource metrics: CPU, memory, disk I/O, network latency. Perform load testing to simulate pressure and expose limits. Analyze logs and distributed traces to locate slow operations.
Use asynchronous processing for non-critical paths (e.g., background jobs). Implement caching layers (e.g., Redis, CDN) to reduce backend load. Apply rate limiting and load shedding to maintain system health. Ensure horizontal scalability of components (e.g., stateless services). Monitor tail latencies and auto-scale based on traffic.
Testing: JMeter, k6, Locust for load/stress testing. Monitoring: Prometheus + Grafana, New Relic, Datadog, AWS CloudWatch. Use APM tools to trace requests and identify slow spans. Use synthetic monitoring for uptime, and real user monitoring (RUM) for client-side performance. Set alerts on SLO breaches, CPU/memory spikes, or error rates.
Use autoscaling (e.g., AWS Auto Scaling Groups, Kubernetes HPA). Use CDNs to serve static content and reduce origin server load. Implement queueing systems (e.g., Kafka, SQS) for smoothing bursts. Apply circuit breakers to prevent cascading failures. Keep services stateless so they can scale out quickly.
Faster performance often requires more resources, which means higher cost (e.g., provisioned IOPS, larger instances). There is a trade-off between reserved vs. on-demand capacity. Caching and batching improve performance without linearly increasing cost. Auto-scaling and serverless can optimize cost-per-request, but might introduce cold start latencies. Cost-performance decisions should align with business SLAs/SLOs and traffic patterns.
Autoscaling & Cloud Best Practices Questions
Autoscaling is the automatic adjustment of compute resources based on current demand. It's critical in distributed systems because it ensures: high availability by scaling out during peak traffic; cost-efficiency by scaling in during idle times; and better user experience by minimizing latency and avoiding overload.
Predictive autoscaling uses historical usage patterns and machine learning to forecast future demand. Based on this prediction, resources are provisioned in advance. Example: AWS Predictive Scaling uses past traffic patterns to scale EC2 instances proactively before load increases.
AWS — Uses Auto Scaling Groups (ASGs) tied to CloudWatch metrics. Works with EC2, ECS, EKS, Lambda.
Azure — Uses Virtual Machine Scale Sets (VMSS), App Service Plans, and AKS with rules from Azure Monitor.
GCP — Uses Managed Instance Groups (MIGs), Cloud Functions, and Cloud Run with GCP Monitoring. GKE uses Horizontal Pod Autoscaler (HPA).
Use an orchestrator like Kubernetes or ECS/EKS/AKS. Set up a Horizontal Pod Autoscaler based on CPU usage or custom metrics. Use a load balancer to distribute traffic across scaled pods. Configure monitoring and alerting to validate scaling behavior.
Key metrics to monitor: CPU and memory usage; request rate per instance; queue depth (e.g., SQS, Kafka); latency and error rates; custom business metrics (e.g., active users, transactions/sec).
Set scaling thresholds wisely to avoid unnecessary scaling. Use spot/preemptible instances for non-critical workloads. Employ scale-to-zero for idle services (Cloud Run, Lambda). Regularly rightsize instances based on usage. Use budgets and alerts to monitor cloud spending.
Scaling latency — Time taken to spin up new instances may cause temporary lag. Cold starts — Especially in serverless platforms like Lambda or Cloud Functions. Over-provisioning — Can lead to higher costs. Under-provisioning — If thresholds are too high or scaling is delayed, it can cause outages.
Further Reading
Interactive: Scaling Simulator
Drag the sliders below to see how adding servers and increasing traffic affects latency, error rate, and throughput in real time. Watch what happens when load exceeds capacity.