System Design From Scratch — Floor 1: What One Machine Can (and Can't) Do

Second post in the series. Floor 0  taught us that some data is far away and slow to reach. This floor asks the question that follows: while you're stuck waiting for something slow, what should you actually be doing?

Share
System Design From Scratch — Floor 1: What One Machine Can (and Can't) Do
System Design From Scratch — Floor 0: Why Anything Is Fast or Slow
The first post in a series where I build system-design intuition from the ground up. No jargon dumped on you up front — we start with an everyday situation, reason it out, and only then attach the technical name.

The premise

Floor 0 ended on a villain: waiting. Some data lives on a slow shelf (the disk, the network), so fetching it takes ages in computer terms. Fine — but that raises the real question, the one this whole floor is about:

While one part of your program is stuck waiting, is the rest of it sitting idle too?

Get this right and a single cheap machine serves thousands of users. Get it wrong and a monster server chokes on a handful. And it all shows up in the kitchen.


Two kinds of work

You're making dinner. Four tasks tonight:

  1. Chopping onions
  2. Boiling a pot of water
  3. Baking garlic bread (15 min)
  4. Whisking a sauce by hand

Sort them into two buckets: tasks that need your hands the whole time, versus tasks you start and then just wait on an appliance.

  • Chopping and whisking need you non-stop. Walk away and progress stops.
  • Boiling and baking run themselves. You start them, then you're free — you're just waiting on the stove.

That split has names, and each comes with a dead-simple test: who is the bottleneck — you, or the appliance?

  • CPU-bound work — you are the bottleneck. Limited by how fast your hands/brain (the "processor") can go. Chopping, whisking, doing math, resizing an image.
  • I/O-bound work — an external device is the bottleneck and you're just waiting on it. "I/O" = input/output = the processor talking to something outside itself. Waiting on a database, downloading a file, reading a disk.

One distinction — is this limited by my effort, or by something I'm waiting on? — decides how you make it faster. Here's how.


Blocking, and the fix for it

Picture a clueless cook: starts the water, then stands and stares at the pot until it boils, then starts the bread, stares again, and only then picks up a knife. One task at a time, idle through every wait.

That idle staring has a name: blocking. The cook is "blocked" — frozen, zero progress, while waiting on an appliance. (A blocked program does exactly this: stuck waiting on the disk or network, doing nothing useful.)

The fix is what any real cook does on instinct: start the slow stuff, then do other things while it cooks. Start the water and the bread, and chop and whisk during the 15-minute bake. That overlap is concurrency — juggling many tasks in overlapping time by never sitting idle during a wait. (And the moment you put one task down to pick up another? That's a context switch — a real term you probably already felt.)

Watch what it does to the clock. Say chopping = 5 min, whisking = 5 min, water = 10, bread = 15:

Same cook, same kitchen, less than half the time. That's why concurrency is the beating heart of every server: the waits are where all the wasted time hides, and concurrency fills them.


The ceiling: where concurrency can't help

Now take away the waits. Two pure hands-on jobs: chop 5 minutes and whisk 5 minutes, both needing your full attention. No appliance, no waiting to hide inside.

Can one cook finish in 5 minutes by "switching" cleverly? No. Ten minutes is the floor — there's no wait to overlap, and one pair of hands can only do one thing at a time. The only way to 5 minutes is a second cook.

That second cook is parallelism — actually doing multiple things at the same instant. The distinction is one interviewers love:

Concurrency is dealing with many things at once; parallelism is doing many things at once.

The second cook is a CPU core; the whole kitchen is a machine (one machine has several cores).


The takeaway: match the fix to the work

This is the whole floor in two lines:

  • I/O-bound (lots of waiting)? → concurrency. One cook serves a flood of orders by filling the waits. No extra cores needed.
  • CPU-bound (pure effort, no waits)? → parallelism. Concurrency does nothing; you need more cores.

A quick gut check to lock it in. A web server spends 90% of its time waiting on the database. To serve far more users, do you add cores or add concurrency?

It's I/O-bound — the cook is standing around waiting on an appliance. Add cores and you just get idle chefs standing around, because effort was never the bottleneck. The answer is concurrency: one cook taking a hundred orders and filling every wait. Cores only help when hands are the limit.


When one kitchen isn't enough

Eventually a real workload maxes out even a smart, fully-parallel kitchen — every core busy, every wait already filled. Then you have two moves:

  • bigger kitchen — a beefier machine with more cores. This is vertical scaling.
  • More kitchens — spread the work across many machines. This is horizontal scaling.

That second door — many machines cooperating — is the entire upstairs of this building, and it's where the really interesting problems live.


What you now own

  • CPU-bound vs I/O-bound — is the task limited by my effort, or by something I'm waiting on?
  • blocking — sitting idle during a wait — and context switching
  • concurrency — one worker filling the waits → the fix for I/O-bound work
  • parallelism — multiple workers at the same instant → the fix for CPU-bound work (and concurrency ≠ parallelism)
  • the single-machine ceiling → vertical vs horizontal scaling

Next: Floor 2

We've squeezed everything out of one machine. Floor 2 opens the door to the thing that makes distributed systems both possible and painful: machines talking to each other over a network — why the network is not just slow but unreliable, and where TCPtimeouts and retries, and the choice between HTTP and gRPC come from.

See you on Floor 2.

System Design From Scratch — Floor 2: How Machines Talk to Each Other
Third post in the series. Floor 0 taught us the network is the slow shelf. Floor 1 squeezed everything out of one machine. This floor is where single-machine thinking ends and distributed thinking begins — and it’s where a lot of engineers quietly get things wrong.