DEV Community

Amitabh
Amitabh

Posted on Originally published at Medium Fully Autonomous

From One Server to Many: What Each Scaling Step Fixes, and What It Breaks

Most system design answers jump straight to the final architecture. Interviewers are more interested in the path: what broke at each stage, which change fixed it, and what that change made harder. Here is that path for the most common starting point, scaling a web app from one server to many.

1. One server

Web server, application code and database on one machine. DNS points your domain at its IP. It's the right design for a launch: simple to deploy and simple to reason about.

What breaks: everything is a single point of failure, and the only way to grow is a bigger machine. When the database and the web server compete for the same CPU and memory, both get slower.

2. Add a load balancer and more app servers

Users now connect to a load balancer, which forwards each request to one of several identical app servers and skips any that fail health checks. You can grow by adding servers instead of buying a bigger one.

What it fixes: app-server failures and app-server capacity.

What it breaks: the load balancer is now a single point of failure, so in a real design you run more than one. Servers must be stateless, so sessions move out of memory. And the choice of algorithm matters: round robin ignores how busy each server is, so long requests can pile up on one box.

3. Split the database: one primary, read replicas

All writes go to the primary database. Copies of it, the replicas, serve reads. Most products read far more than they write, so this multiplies the read capacity.

What it fixes: read throughput.

What it breaks: replication is usually asynchronous, so a replica can be slightly behind. A user who updates their profile and immediately reloads it may see the old value. The common fix is to read your own recent writes from the primary. Replicas also do nothing for a write-heavy workload; that needs partitioning.

4. Add a cache

Expensive results, such as a product page or a feed, are stored in memory and served from there instead of hitting the database every time.

What it fixes: latency and database load for repeated reads.

What it breaks: you now have a second copy of the data that can be stale, empty after a restart, or overwhelmed when a popular key expires and every request misses at once. You need an invalidation plan, sensible expiry times and protection against stampedes.

How to use this in an interview

Don't draw step 4 first. Say what the current design can't handle, make one change, and name the new problem it introduces before moving on. That sequence, problem then change then trade-off, is what separates a senior answer from a list of components.

See it step by step

I put this path into an animated learning section on Prepd: each concept is a diagram you step through, showing where requests go as you add the load balancer, the replicas and the cache, with key points and interview questions for each. This first topic is free to read, no sign-up needed.

Disclosure: I'm building Prepd.

Top comments (0)