Art of Codeartofcode
LLeeaarrnniinngg
BBlloogg
TToooollss
GGaammeess AArreennaa
JJoobbss
LLiivvee CCoouurrsseess

Courses

  • System DesignHigh-level system design, one everyday scene at a time: 30 concepts and 15 interview rounds.712 lessons
All coursesTutorialsMy Learning

Categories

  • 01AI & Tools
  • 02Tutorials
  • 03Engineering
  • 04Reviews
  • 05Build Logs
  • 06Tales of Misfits
  • 07Opinion
  • 08Career
  • 09News

Read & write

All storiesEvery post, newest first.Write for usPublish your stories and earn from what you write.Writer leaderboardsWho is writing the most read posts.

Categories

  • PDF Tools25
  • Development09
  • Utilities10
  • Design04
  • Converters04
  • Generators02

Featured

JSON FormatterFormat, validate and minify JSON.Regex TesterTest patterns against live text.Merge PDFCombine PDFs in your browser.JWT DecoderRead a token’s header and claims.Password GeneratorStrong passwords, generated locally.Gradient GeneratorBuild CSS gradients visually.
All 53 tools

Play a round

DevdleOne developer word a day, six guesses.Python RushClassic snake, with coding icons to eat.Blink or BreakA quick test of your reflexes.
Games Arena

Learn with us

Live CoursesInstructor-led courses. Join a waitlist and be first in when one starts.LearningStructured programming courses with AI assistance and certificates.

System Design

6. Vertical vs Horizontal Scaling

Lesson 14 of 16

6. Vertical vs Horizontal Scaling

1
The picture to keep in your head
2
6.1 What does "scaling" actually mean?
3
6.2 What is vertical scaling?
4
6.3 What is horizontal scaling?
5
6.4 Why does scaling out need stateless servers?
6
6.5 If servers are stateless, where does the state go?
7
6.6 What is autoscaling, and how do you get it right?
8
6.7 What about the database? Why is it the hardest part to scale?
9
6.8 What does each kind of scaling really cost?
10
6.9 So when do you choose which?
11
Memory card and where the analogy breaks
12
Practice: five levels
13
Interview questions
14
Solutions
15
Sources
16
Code lab
6. Vertical vs Horizontal Scaling

Solutions

3 min read+10 XP

Solutions

Level 1

reveal

Level 2

reveal

Level 3

reveal

Level 4

reveal

Level 5 (sketch)

reveal

No tracks available

Lofi Player

artofcode

artofcode.in

The den of devs.

Stories, tools, games, and gear for developers.

Read the StoriesEExxpplloorree TToooollss
Art of Codeartofcode

Platform

  • BBlloogg
  • TToooollss
  • GGaammeess AArreennaa
  • CCoouurrsseess
  • LLeeaarrnniinngg
  • CCaammppuuss

Company

  • AAbboouutt UUss
  • JJoobbss && IInntteerrnnsshhiippss
  • CCoonnttaacctt

Legal

  • TTeerrmmss ooff SSeerrvviiccee
  • PPrriivvaaccyy PPoolliiccyy
  • RReeffuunndd PPoolliiccyy

Copyright © 2026 artofcode.in, All Rights Reserved.

IST·--:--

1Show the Level 1 solution
2**A.**
31. **Scale up** (more cores), and fix slow queries. A database is hard to split, and 4 cores is small.
42. **Scale out.** It's stateless; add servers and autoscaling.
53. **Scale out.** Independent jobs split perfectly; add workers based on queue length.
64. **Move the sessions out first** (to Redis), then scale out. Short term, scaling up the single server is the quick fix, but it stays a single point of failure for the launch.
7
8**B.**
91. **No.** Nothing important is on it.
102. **Yes.** The photos on its disk are lost (and invisible to other servers anyway). Store them in object storage.
113. **No**, if the queue is set up properly: an unacknowledged job becomes visible again and another worker takes it (Chapter 22). The work is redone, so the job must be safe to run twice.
Show the Level 2 solution
1. Normal: 2,000 ÷ (500 × 0.6) = 6.7 → 7, plus 1 spare = **8**. Sale: 20,000 ÷ 300 = 66.7 → 67, plus 1 = **68**.
2. 1 ÷ (0.1 + 0.9 ÷ 10) ≈ **5.3×** with 10 servers; 1 ÷ (0.1 + 0.9 ÷ 100) ≈ **9.2×** with 100; the limit is 1 ÷ 0.1 = **10×**.
3. Always 68: 68 × 24 = **1,632 server-hours**. Autoscaled: 68 × 2 + 8 × 22 = **312**. About **81% less**.
4. For 3 minutes, 20,000 − 3,500 = 16,500 requests a second can't be served: 16,500 × 180 = **2,970,000 requests** dropped (or queued until they time out).
1Show the Level 3 solution
2Tested solution (Python 3):
3
4```python
5import math
6
7
8def servers_needed(rps, per_server_rps, target_util):
9    return max(1, math.ceil(rps / (per_server_rps * target_util)))
10
11
12def simulate(traffic, per_server_rps=500, target_util=0.6, min_servers=2, max_servers=100,
13             warmup=3, schedule=None, start=None):
14    ready = start if start is not None else min_servers
15    pending = []                                  # minutes when ordered servers become ready
16    history, dropped = [], 0
17    for minute, rps in enumerate(traffic):
18        ready += sum(1 for t in pending if t == minute)
19        pending = [t for t in pending if t > minute]
20        want = servers_needed(rps, per_server_rps, target_util)
21        if schedule:
22            floors = [n for m, n in schedule.items() if m <= minute]
23            if floors:
24                want = max(want, max(floors))     # keep the scheduled minimum
25        want = min(max(want, min_servers), max_servers)
26        coming = ready + len(pending)
27        if want > coming:
28            pending += [minute + warmup] * (want - coming)   # scale out
29        elif want < ready and not pending:
30            ready = want                                     # scale in
31        dropped += max(0, rps - ready * per_server_rps) * 60
32        history.append(ready)
33    return history, dropped
34```
35
36All three checks pass: steady traffic settles at 7 servers with nothing dropped; the sudden jump drops **2,970,000** requests during the 3-minute warm-up and settles at 67; scheduling ahead drops **0**. The full file (with Amdahl's law and the Level 4 code) is in `code/ch06_scaling.py`.
1Show the Level 4 solution
2**The bug:** the "add" and "remove" thresholds are **the same number**, and there's **no cooldown**, so the fleet **flaps**.
3
41. Minute 1: 4 servers, CPU ≈ 65% > 60% → **add** → 5.
52. Minute 2: 5 servers, CPU ≈ 52% < 60% → **remove** → 4.
63. Minute 3: back to 65% → add → 5.
74. Minute 4: 52% → remove → 4. And so on: **30 changes in 30 minutes** in the tested simulation, each one a server starting or being killed (and dropping its in-flight requests).
8
9**The fix:** a **gap** between thresholds (hysteresis) and a **cooldown**:
10
11```python
12def autoscale(servers, avg_cpu, minutes_since_change, cooldown=5):
13    if minutes_since_change < cooldown:
14        return servers
15    if avg_cpu > 0.70:
16        return servers + 1
17    if avg_cpu < 0.40:
18        return servers - 1
19    return servers
20```
21
22At 65% the fleet now stays at 4 (zero changes in the simulation). Also drain servers before removing them, so in-flight requests finish (Chapter 7).
1Show the Level 5 (sketch) solution
21. **Move the state out:** sessions → **Redis** (or signed tokens); book covers → **object storage** served through a CDN (Chapters 11 and 26); emails and image processing → a **queue** with workers; the database → its **own machine**.
32. **New layout:** DNS → load balancer → **N stateless app servers** (at least 2) → Redis, the database primary plus a read replica, object storage; workers reading from a queue.
43. **Database for the sale:** find and fix the top queries, add indexes; cache the book pages and catalogue in Redis; scale the database machine **up** before the sale; add **read replicas** for browsing traffic; put a **connection pooler** in front; keep checkout on the primary.
54. **Autoscaling:** **scheduled** minimum of about 68 app servers from 30 minutes before the sale; **target tracking** on requests per server (about 300) as a safety net; minimum 8, maximum about 100, capped by what the database can take; workers scale on queue length.
65. **Risks:** the database or connection limits saturate before the app tier does; caches start cold at the opening minute. **Test:** a load test at the sale peak in a production-like environment; warm the cache with the top books before opening; rehearse the scheduled scaling once.