Scaling up vs scaling out (and back-of-envelope estimation)

“10 crore orders on Diwali day: how much storage and how many servers?” In this lesson you learn to answer questions like this in two minutes with round numbers, and go deeper into scaling up versus scaling out: why stateless services scale easily, how autoscaling follows the festival curve, and why 64 machines can be slower than 32.

Module 3 · Distributed Systems Concepts⏱ 30 minBeginner → AdvancedRuns in your browser (Python)3 real-time scenariosCapacity estimator for your PC9 practice problems
1 The story

“How many servers for Diwali?”

Kabir is preparing for TiffinGo’s full-time interview, which follows the internship. A senior shared the question they always ask: “We expect 10 crore orders on Diwali day. How much storage and how many servers do we need?” Kabir freezes. He doesn’t know the exact answer.

Interview question“TiffinGo expects 10 crore orderson Diwali day. How much storage andhow many servers will we need?”No calculator. Five minutes.10 crore ÷ 1 lakh s/day ≈ 1,000 orders/speak × 5 → 5,000 orders/s1 KB each → ≈ 100 GB/day≈ 35 TB a year, ×3 copies ≈ 100 TBnodes = peak ÷ per-node rate + headroom Big numbers become easy with round numbers and a recipe. myphatshala.in
Kabir

Sir, how can anyone answer that without data? I'd need weeks of measurements.

Arjun Sir

Nobody expects the exact number, Kabir. They want to see you estimate: round numbers, clear assumptions, and arithmetic you can do on paper. Engineers call it a back-of-envelope estimate.

Riya

Like guessing how many samosas the canteen needs for the college fest?

Arjun Sir

Exactly that. How many students, how many will come, how many samosas each, plus some extra. Here: how many orders, how fast at the peak, how big each one is, how much one machine can handle.

Kabir

And once we know we need more than one machine?

Arjun Sir

Then you choose between a bigger machine, scaling up, and more machines, scaling out. You met both in Lesson 1.2. Today we go deeper: why some services scale out easily and others don’t, how autoscaling follows the Diwali curve, and why adding machines can sometimes make a system slower.

2 Definition

Scalability and back-of-envelope estimation

Formal definition (write this in exams)

Scalability is the ability of a system to handle increasing load by adding resources while maintaining acceptable performance. Vertical scaling (scaling up) increases the capacity of a single machine; horizontal scaling (scaling out) adds more machines and distributes load among them, typically behind a load balancer. A back-of-envelope estimate is a quick, approximate calculation of load, storage, bandwidth and machine count using rounded numbers and explicit assumptions, used to judge the feasibility and scale of a design.

In simple words

Scaling means keeping a system fast as more people use it. You can buy a bigger machine (scale up) or add more machines (scale out). A back-of-envelope estimate is quick, rough arithmetic with round numbers that tells you roughly how big the system must be.

Words you’ll meet in this lesson

ThroughputWork done per second: orders per second, events per second, MB per second.
QPS / TPSQueries (or transactions) per second.
Peak factorHow many times busier the busiest period is than the average.
Stateless serviceA service that keeps no data between requests, so any copy can serve any request.
Load balancerA component that spreads incoming requests across many servers.
AutoscalingAutomatically adding or removing servers based on load.
HeadroomSpare capacity kept free for spikes and failures.
p99 latencyThe response time that 99% of requests beat; the slowest 1% are slower.

Numbers for quick estimates

Seconds in a day86,400 ≈ 1 lakh (105) · seconds in a year ≈ 3 crore (3 × 107)
Indian units1 lakh = 105 · 1 crore = 107 · 10 lakh = 1 million · 100 crore = 1 billion
Sizes1 crore × 1 KB ≈ 10 GB · 1 crore × 1 MB ≈ 10 TB
Bandwidth1 Gbps ≈ 125 MB/s ≈ 10 TB per day if fully used
Typical record sizespayment or order row: 0.25–1 KB · app event: 0.5–2 KB · photo: 2–5 MB
Safe target loadplan for 50–70% utilisation at peak

Scaling and estimation at three levels

Pick your level. New to this? Start at Beginner and climb.

Beginner A bigger jug or more stalls

  • Scaling up (vertical): the lemonade stall buys a bigger jug and a faster juicer. Simple, but there is a biggest jug, and if the one stall closes, nobody gets lemonade.
  • Scaling out (horizontal): open more stalls and send customers to whichever is free. Needs a person at the gate (a load balancer), but you can keep adding stalls.
  • Estimation recipe: (1) how many things per day, (2) divide by about 1 lakh seconds for “per second”, (3) multiply by a peak factor, (4) multiply by size for storage, (5) divide by what one machine handles for machine count.

Handy facts: a day has 86,400 seconds, call it 1 lakh. 1 crore = 107. 1 KB × 1 crore ≈ 10 GB.

Intermediate Stateless services, load balancers and autoscaling

  • Stateless vs stateful: a stateless service keeps nothing between requests (any server can answer any request), so it scales out by adding copies. A stateful one (a database, a cache, a session held in memory) must split or copy its data first, which is much harder.
  • Move state out: keep sessions in Redis and data in databases, so web and API servers stay stateless.
  • Load balancer: spreads requests across servers (round robin, least connections) and stops sending traffic to unhealthy ones.
  • Scaling databases: read replicas for read-heavy traffic, then sharding (splitting data across machines, Lesson 3.2) when writes outgrow one machine.
  • Autoscaling: add or remove machines based on a metric (CPU above 60%, queue length, requests per second), with a minimum and maximum. Scale up early for known peaks; new machines take minutes to start.
  • Headroom: plan to run at 50–70% of capacity, so a spike or a failed machine doesn’t push you over the edge.

Advanced Why scaling out has limits

  • Universal Scalability Law (Neil Gunther): throughput with N machines ≈ N ÷ (1 + α(N−1) + βN(N−1)). α is contention (waiting for a shared resource, like Amdahl’s serial part); β is coherence (machines talking to each other to stay in agreement). With β > 0, adding machines beyond a point reduces throughput.
  • Little’s law: items in the system = arrival rate × time each spends inside (L = λW). At 5,000 requests/s and 200 ms each, about 1,000 requests are in flight at any moment, which sizes thread pools and connections.
  • Tail latency at scale: if one request fans out to 100 servers and each is slow 1% of the time, 1 − 0.99100 ≈ 63% of requests hit at least one slow server. Large systems watch p99 latency, not the average.
  • Cost per unit: measure cost per thousand orders, not just total cost; good scaling keeps it flat or falling as volume grows.

Exam tip: For “vertical vs horizontal scaling”, give the definition, a comparison table (cost, limit, failure impact, complexity, examples) and one example each (a bigger database server; adding web servers behind a load balancer). For estimation questions, always write your assumptions first, then show each step with units.

3 Types and ways to use it

Four ways to scale

Vertical (scale up)

Bigger CPU, more RAM, faster disks on one machine. No code changes. Hits a ceiling; single point of failure.

Bigger machine

Horizontal (scale out)

More identical machines behind a load balancer. Near-linear growth for stateless work; needs distributed design for data.

More machines

Diagonal

Scale up each node to a sensible size, then scale out with many of them. What most real clusters do.

Both

Elastic (autoscaling)

The number of machines follows demand automatically: more at 8 pm on Diwali, fewer at 4 am.

Follows demand

Side by side

Scale up (vertical)Scale out (horizontal)
HowBigger machineMore machines + load balancer
LimitLargest machine availablePractically very high (coordination limits it)
Cost curveRises steeply at the top endRoughly linear with machines
FailureOne machine down = outageOne of many down = reduced capacity
Code changesUsually noneStateless design, data partitioning
Easy forDatabases, legacy appsWeb/API servers, Spark, Kafka, Cassandra
ElasticityResize needs a restartAdd/remove nodes while running

In interviews, say both: “I would first scale up the database to a sensible size and add read replicas; the stateless API tier scales out behind a load balancer; when writes outgrow one database, I would shard.” That shows you know when each approach fits.

4 Step by step

A complete estimate for 10 crore orders a day

  1. Write your assumptions

    10 crore orders on Diwali day · peak hour is 5× the average · each order record ≈ 1 KB · one API server handles ≈ 400 orders/s (from a load test) · keep 3 copies of data.

    Assumptions first, out loud. If one is wrong, the interviewer (or your manager) can correct it and the rest of the calculation still stands.
  2. Orders per second, average and peak

    orders_per_day = 10_00_00_000          # 10 crore
    avg_per_s = orders_per_day / 1_00_000    # ~1 lakh seconds in a day
    peak_per_s = avg_per_s * 5               # Diwali dinner peak
    print("average:", avg_per_s, "orders/s | peak:", peak_per_s, "orders/s")
    average: 1000.0 orders/s | peak: 5000.0 orders/s
    Dividing by about 1 lakh seconds turns “per day” into “per second”. Systems must handle the peak, not the average.
  3. Storage per day and per year

    orders_per_day = 10_00_00_000
    kb_per_order = 1
    gb_per_day = orders_per_day * kb_per_order / 1024 / 1024
    tb_per_year = gb_per_day * 365 / 1024
    print(f"{gb_per_day:.0f} GB/day, {tb_per_year:.0f} TB/year, {tb_per_year * 3:.0f} TB with 3 copies")
    95 GB/day, 34 TB/year, 102 TB with 3 copies
    About 95 GB a day is manageable; the yearly total with copies, around 100 TB, is what drives cost. Add indexes, logs and events and the real figure is several times larger.
  4. Servers at the peak

    peak_per_s = 5000
    per_node = 400            # orders/s one API server can handle (measured)
    target_load = 0.6         # keep 40% headroom
    import math
    print("API servers at peak:", math.ceil(peak_per_s / (per_node * target_load)))
    API servers at peak: 21
    Divide the peak by what one server handles at the target load. The API tier is stateless, so these servers scale out behind a load balancer.
  5. Network bandwidth

    peak_per_s, kb = 5000, 1
    mb_per_s = peak_per_s * kb / 1024
    print(f"incoming: {mb_per_s:.1f} MB/s = {mb_per_s * 8:.0f} Mbps")
    incoming: 4.9 MB/s = 39 Mbps
    Order data alone is small for the network. Images, app events and replication traffic usually dominate bandwidth, so estimate them separately.
  6. Concurrency with Little’s law

    arrival = 5000          # requests per second
    time_inside = 0.2       # seconds each request spends in the system
    print("requests in flight:", int(arrival * time_inside))
    requests in flight: 1000
    At the peak about 1,000 requests are in progress at once. That sizes database connection pools and thread counts: too few and requests queue, too many and the database is overwhelmed.
5 See it happen

Diwali night: what Riya sees, and what the autoscaler does

Step through the six frames. Left: the app as Riya experiences it. Right: orders per second and the number of servers behind it.

What Riya sees, and what the autoscaler does 6 steps
What you see 6:00 pmDiwali offers!Order in 30 s✦7:30 pmBusy eveningstill 30 s✦8:00 pmVery busy!still fast✦8:30 pmPeak dinnercheckout 1.2 s✦9:30 pmOrders flowingall smooth✦11:30 pmHappy Diwaliquiet night✦ myphatshala.in
Behind the screen 6:00 pm · 10 API serversorders per secondCPU 45%7:30 pm · 16 API serversorders per secondCPU 72%8:00 pm · 32 API serversorders per secondCPU 64%8:30 pm · 40 API serversorders per secondCPU 62%9:30 pm · 24 API serversorders per secondCPU 55%11:30 pm · 10 API serversorders per secondCPU 40% myphatshala.in
6 Real-world use

Real-time scenarios: scaling in practice

Three live scenarios: a two-minute capacity estimate, a cart that vanishes when servers are added, and an advanced look at why more machines can mean less speed.

Real-time scenario 1

Estimating Diwali day in two minutes

Watch Kabir answer the interview question line by line. Every step is simple arithmetic with round numbers; the skill is the order of the steps and writing down the assumptions.

The estimation recipe 5 steps
Animated example Back-of-envelope: 10 crore orders on Diwali dayOrders per day10 crore10⁸Start with the business number and write it down.Back-of-envelope: 10 crore orders on Diwali dayOrders per day10 crore10⁸Average per second÷ 1 lakh≈ 1,000 /sPeak (× 5)× 5≈ 5,000 /sPer second ≈ per day ÷ 1 lakh. Then apply the peak factor.Back-of-envelope: 10 crore orders on Diwali dayOrders per day10 crore10⁸Average per second÷ 1 lakh≈ 1,000 /sPeak (× 5)× 5≈ 5,000 /sStorage per day× 1 KB≈ 95 GBStorage = count × size. 1 crore × 1 KB ≈ 10 GB.Back-of-envelope: 10 crore orders on Diwali dayOrders per day10 crore10⁸Average per second÷ 1 lakh≈ 1,000 /sPeak (× 5)× 5≈ 5,000 /sStorage per day× 1 KB≈ 95 GBPer year, 3 copies× 365 × 3≈ 100 TBA year with 3 copies ≈ 100 TB: a real cost item.Back-of-envelope: 10 crore orders on Diwali dayOrders per day10 crore10⁸Average per second÷ 1 lakh≈ 1,000 /sPeak (× 5)× 5≈ 5,000 /sStorage per day× 1 KB≈ 95 GBPer year, 3 copies× 365 × 3≈ 100 TBAPI servers÷ (400 × 0.6)21Servers = peak ÷ (capacity per server × target load). myphatshala.in
Real-time scenario 2

Why Riya’s cart vanished when we added servers

Scaling out is easy only for stateless services. Watch what happens to a cart stored in a server’s memory when a second and third server are added.

Stateful vs stateless 5 steps
Animated example load balancerAPI 1Riya’s cartOne server keeps Riya’s cart in its own memory.Works fine… until we add servers.load balancerAPI 1Riya’s cartAPI 2API 3next request → API 2: “your cart is empty?!”Added servers: the load balancer sends her next tapto API 2, which has never seen her cart.load balancerAPI 1Riya’s cartAPI 2API 3sticky sessions: Riya always → API 1Quick fix: sticky sessions. But if API 1 dies,her cart dies with it, and load gets uneven.load balancerAPI 1API 2API 3Redis: sessionsProper fix: move the cart to Redis. Every API serveris now stateless; any of them can serve any tap.load balancerAPI 1API 2API 3API 4API 5Redis: sessionsNow scaling out is just “add more copies”.Servers can die or be added at any time. myphatshala.in
Real-time scenario 3 · advanced

When 64 machines are slower than 32

Adding machines is not free: they wait for shared resources and must talk to each other to stay in agreement. The Universal Scalability Law shows the effect. Watch throughput as a job grows from 8 to 64 nodes.

Universal Scalability Law 5 steps
Animated example throughput (× one node)nodes →816324864perfect scaling6.4×8 nodes: 6.4× faster. Not 8×, because ofwaiting on shared resources (contention).9.7×16 nodes: about 10×. The gap to perfect grows.11.7×32 nodes: about 12×, near the peak. Nodes spendmuch of their time talking to each other.10.5×64 nodes: slower than 32! Coordination growswith every pair of nodes (coherence cost).11.7×Fix: less sharing. Partition data so nodes workindependently; avoid global locks and chatty syncs. myphatshala.in

Scaling decisions at real companies

1. Sale days and cricket finals: pre-scaling

Shopping and streaming apps scale out days in advance of known peaks (festive sales, IPL finals), run load tests at several times the expected traffic, and keep autoscaling as a safety net.

Forecast peak→Load test + pre-scale→Autoscale on the day

2. Databases: scale up, add replicas, then shard

A start-up’s single PostgreSQL server is first moved to a larger machine, then given read replicas for reports, and only when writes outgrow it is the data sharded or moved to a distributed database.

Bigger DB→Read replicas→Sharding

3. Spark clusters that grow per job

Data platforms start a cluster for a nightly job, let it autoscale from a few nodes to dozens while the heavy step runs, and shut it down afterwards, because storage lives separately in object storage.

Job starts→Autoscale workers→Cluster shuts down
Q = ?≈ 5,000/s

4. System-design interviews

Most data and backend interviews include an estimation step: users, requests per second, storage per year, bandwidth and machine count, with stated assumptions. The method matters more than the exact number.

Assumptions→Round-number maths→Design choices
7 Run the code

A back-of-envelope calculator

Change the assumptions at the top and run. The program turns “orders per day” into peak orders per second, storage per year and the number of servers. Then try the challenges: compare scaling up and out, simulate autoscaling, use Little's law, see the Universal Scalability Law and the tail-latency effect.

import math

orders_per_day = 10_00_00_000   # 10 crore
peak_factor = 5
kb_per_order = 1
copies = 3
per_server = 400                # orders/s one API server handles
target_load = 0.6               # keep 40% headroom

avg = orders_per_day / 86_400
peak = avg * peak_factor
gb_day = orders_per_day * kb_per_order / 1024 / 1024
tb_year = gb_day * 365 / 1024 * copies
servers = math.ceil(peak / (per_server * target_load))

print(f"average   : {avg:,.0f} orders/s")
print(f"peak      : {peak:,.0f} orders/s")
print(f"storage   : {gb_day:,.0f} GB/day, {tb_year:,.0f} TB/year with {copies} copies")
print(f"servers   : {servers} at {target_load:.0%} target load")
average : 1,157 orders/s peak : 5,787 orders/s storage : 95 GB/day, 102 TB/year with 3 copies servers : 25 at 60% target load
  • avg = orders_per_day / 86_400Per day ÷ 86,400 seconds = per second. In your head, divide by 1 lakh.
  • peak = avg * peak_factorSize for the busiest hour, never the average.
  • servers = math.ceil(peak / (per_server * target_load))Round up, and plan for each server to run at only 60% so spikes and failures are absorbed.

Run it on your PC: an estimate calibrated by your own CPU

Good estimates start from a measurement. This program first measures how many order events one core of your computer can parse and validate in a second, then uses that number to estimate the cores, nodes and storage needed for 10 crore orders a day. Only the standard library is used. Save it as capacity_estimator.py and run it.

Measure, then estimate

"""Back-of-envelope capacity plan, calibrated with a real measurement on your PC.
Step 1 measures how many order events one CPU core can parse and validate per second.
Step 2 uses that number to estimate the machines needed for a festival peak."""
import json, math, time

# ---- assumptions (change these)
ORDERS_PER_DAY = 10_00_00_000      # 10 crore orders a day
PEAK_FACTOR = 5                     # peak hour is 5x the average rate
EVENTS_PER_ORDER = 20               # clicks, status updates, pings per order
BYTES_PER_EVENT = 500
CORES_PER_NODE = 16
HEADROOM = 0.6                      # run nodes at 60% so spikes and failures are absorbed

def inr(x):
    """Indian digit grouping: 115741 -> 1,15,741"""
    s = str(round(x))
    head, tail = s[:-3], s[-3:]
    while len(head) > 2:
        tail = head[-2:] + "," + tail
        head = head[:-2]
    return (head + "," + tail) if head else tail


# ---- step 1: measure one core
event = json.dumps({"order_id": "O1043", "user": "U9", "city": "Pune", "amount": 420,
                    "items": [{"dish": "thali", "qty": 1}, {"dish": "lassi", "qty": 2}], "ts": 1792411000})
n, start = 0, time.perf_counter()
while time.perf_counter() - start < 1.0:
    e = json.loads(event)
    ok = e["amount"] > 0 and e["city"] and len(e["items"]) > 0
    n += 1
per_core = n / (time.perf_counter() - start)
print(f"Measured: one core parses and checks about {inr(per_core)} events per second")

# ---- step 2: estimate
events_day = ORDERS_PER_DAY * EVENTS_PER_ORDER
avg_eps = events_day / 86_400
peak_eps = avg_eps * PEAK_FACTOR
cores = peak_eps / (per_core * HEADROOM)
nodes = math.ceil(cores / CORES_PER_NODE)
storage_tb_day = events_day * BYTES_PER_EVENT / 1024**4

print(f"\nEvents per day      : {events_day / 1e7:.0f} crore")
print(f"Average events / s  : {inr(avg_eps)}")
print(f"Peak events / s     : {inr(peak_eps)}")
print(f"Cores needed at peak: {cores:.1f}  (at {HEADROOM:.0%} target load)")
print(f"Nodes ({CORES_PER_NODE} cores each): {nodes}")
print(f"Raw event storage   : {storage_tb_day:.2f} TB per day, {storage_tb_day * 365:.0f} TB per year")
print("\nReal services do far more work per event (network, database, logging),")
print("so production capacity is usually 10-100x higher than this lower bound.")

# In a terminal:  python capacity_estimator.py
Measured: one core parses and checks about 4,93,152 events per second Events per day : 200 crore Average events / s : 23,148 Peak events / s : 1,15,741 Cores needed at peak: 0.4 (at 60% target load) Nodes (16 cores each): 1 Raw event storage : 0.91 TB per day, 332 TB per year Real services do far more work per event (network, database, logging), so production capacity is usually 10-100x higher than this lower bound.

Recorded from a real run with Python 3.13 on a cloud machine; your measured rate will differ. The surprising result, a single node, is the lesson: parsing JSON is cheap, so for this workload the real limits are storage (hundreds of TB a year), network, databases and reliability, not CPU. That is why you estimate every resource, not just one.

8 Line by line

An autoscaler deciding, one line at a time

For three hours of traffic, the autoscaler picks enough servers to keep each one at about 60% load, never fewer than 2. Watch servers follow the load up and down.

Worked examples: real problems, solved and traced in memory

Four scaling and estimation problems, from a first estimate to choosing the best cluster size. Each runs live and is traced.

Example 1BeginnerInterview · estimation

Requests per second from daily users

Problem statement

An app has 2 crore daily users. Each opens it 5 times a day and makes 8 API calls per visit. The evening peak is 4 times the average. Estimate average and peak requests per second.

Input
2 crore users × 5 visits × 8 calls, peak 4×
Expected output
requests/day: 80 crore
average: 9,259/s  peak: 37,037/s

Approach

  1. Requests per day = users × visits × calls.
  2. Average per second = per day ÷ 86,400.
  3. Peak = average × peak factor.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

Example 2IntermediateCapacity planning · storage

Storage for a year, with growth and copies

Problem statement

A service stores 120 GB of new data per day today, growing 5% per month. Data is kept 1 year with 3 copies. How much raw storage is needed at the end of the year?

Input
120 GB/day, +5% per month, 12 months, 3 copies
Expected output
data for the year: 56.0 TB
with 3 copies   : 167.9 TB
daily volume at year end: 216 GB

Approach

  1. Loop month by month; daily volume grows 5% each month.
  2. Add 30 days of data per month to the running total.
  3. Multiply the year's total by the number of copies.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

Example 3IntermediateOperations · autoscaling

How many servers each hour, and the saving

Problem statement

Each server handles 500 requests/s; keep load at 60% and at least 3 servers. Given hourly traffic, compute servers per hour and compare total server-hours with running the peak count all day.

Input
6 hourly loads
Expected output
servers per hour: [3, 8, 24, 30, 14, 3]
autoscaled server-hours: 82
fixed at peak          : 180

Approach

  1. Servers per hour = max(3, ceil(load ÷ (500 × 0.6))).
  2. Add up server-hours.
  3. Compare with peak servers × 6 hours.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

Example 4AdvancedPerformance · USL

Pick the best cluster size from a fitted model

Problem statement

Load tests were fitted to the Universal Scalability Law with a = 0.03 and b = 0.0008; one node does 10 jobs per minute. Each node costs Rs 50 per hour. Among the candidate sizes, find the fastest cluster and the cheapest cost per 1,000 jobs.

Input
sizes 8, 16, 32, 48, 64 · a = 0.03 · b = 0.0008 · Rs 50/node-hour
Expected output
 8 nodes:  63.8 jobs/min, Rs  104.6 per 1,000 jobs
16 nodes:  97.4 jobs/min, Rs  136.8 per 1,000 jobs
32 nodes: 117.5 jobs/min, Rs  227.0 per 1,000 jobs
48 nodes: 113.9 jobs/min, Rs  351.2 per 1,000 jobs
64 nodes: 104.7 jobs/min, Rs  509.6 per 1,000 jobs
fastest: 32 nodes | cheapest per job: 8 nodes

Approach

  1. Throughput(N) = 10 × N ÷ (1 + a(N−1) + bN(N−1)) jobs per minute.
  2. Cost per 1,000 jobs = (N × 50 per hour) ÷ (jobs per hour) × 1,000.
  3. Track the best size for speed and for cost separately.

Solution: run it live

What happens in memory, step by step

Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.

9 Quiz and revision

Check yourself

Quick revision

Lesson 3.1 in one screen

  • Scale up = bigger machine (simple, ceiling, single point of failure). Scale out = more machines + load balancer.
  • Stateless tiers scale out by adding copies; move state to Redis/databases. Databases: scale up → read replicas → shard.
  • Autoscale on CPU/queue/RPS with min and max; pre-scale for known peaks; keep 30–50% headroom.
  • Estimation recipe: per day → ÷ 1 lakh → × peak factor → × size → ÷ per-machine capacity. State assumptions.
  • Handy: 1 crore × 1 KB ≈ 10 GB; 1 Gbps ≈ 125 MB/s; 1 year ≈ 3 crore seconds.
  • Little's law: L = λW. USL: contention (α) and coherence (β) limit scaling.
  • Fan-out amplifies tail latency: 1 − (1 − p)N. Watch p99, not the average.

Interview and viva questions

Differentiate vertical and horizontal scaling.

Vertical scaling adds resources (CPU, RAM, disk) to a single machine; it is simple and needs no code changes, but cost rises steeply, there is a hard upper limit and the machine remains a single point of failure. Horizontal scaling adds more machines and spreads the load with a load balancer or data partitioning; it scales much further and tolerates failures, but requires stateless services or distributed data design. Real systems usually combine both.

What is a back-of-envelope estimation and why is it useful?

A quick calculation using rounded numbers and stated assumptions to estimate load (requests per second), storage, bandwidth and machine count. It shows early whether a design is feasible, which resource is likely to be the bottleneck and roughly what it will cost, before any detailed work. It is a standard part of system-design interviews.

How do you scale a database?

First optimise queries and indexes and scale the server up to a sensible size. Add caching for hot reads and read replicas for read-heavy workloads. When writes or data size outgrow one machine, partition (shard) the data across servers by a key, or move to a distributed database designed for horizontal scaling.

What is autoscaling?

Automatically adjusting the number of running instances based on metrics such as CPU utilisation, request rate or queue length, within configured minimum and maximum limits. It keeps performance steady during peaks and saves cost during quiet periods; for predictable peaks, teams schedule scale-ups in advance because new instances take time to start.

Why doesn't doubling the machines always halve the time?

Because part of the work can't be parallelised (Amdahl's law), machines wait on shared resources (contention), and they must communicate and coordinate (coherence), which grows with the number of machines. The Universal Scalability Law models these effects; beyond an optimal size, adding machines can even reduce throughput.

Practice problems

Estimation and scaling exercises. The last two are business tasks.

P1Easy

Per day to per second

Convert 4 crore events per day into events per second.

Input
4 crore/day
Expected output
463
Show solution and explanation
print(round(4_00_00_000 / 86_400))
463

How it works: Divide by the 86,400 seconds in a day (≈ 1 lakh).

P2Easy

Peak rate

The average is 1,200 requests/s and the peak factor is 6. Print the peak.

Input
1200, 6
Expected output
7200
Show solution and explanation
print(1200 * 6)
7200

How it works: Systems are sized for the peak.

P3Easy

Daily storage

5 crore records a day at 2 KB each. Print GB per day.

Input
5 crore × 2 KB
Expected output
95 GB
Show solution and explanation
print(round(5_00_00_000 * 2 / 1024 / 1024), "GB")
95 GB

How it works: KB ÷ 1024 = MB, ÷ 1024 again = GB.

P4Medium

Servers with headroom

Peak is 9,000 requests/s; one server handles 500/s; target load 60%. Print servers needed.

Input
9000, 500, 0.6
Expected output
30
Show solution and explanation
import math
print(math.ceil(9000 / (500 * 0.6)))
30

How it works: Divide by the usable capacity per server, then round up.

P5Medium

Bandwidth

At peak, 20,000 events/s of 1.5 KB each arrive. Print MB/s and Mbps.

Input
20,000 × 1.5 KB
Expected output
29.3 MB/s = 234 Mbps
Show solution and explanation
mb = 20000 * 1.5 / 1024
print(round(mb, 1), "MB/s =", round(mb * 8), "Mbps")
29.3 MB/s = 234 Mbps

How it works: Multiply MB/s by 8 to get megabits per second, the unit network links use.

P6Advanced

Connection pool size

At peak, 3,000 queries/s each take 40 ms in the database. How many connections are busy on average? Add 50% safety.

Input
3000/s, 40 ms
Expected output
busy: 120.0 | pool with 50% safety: 180
Show solution and explanation
busy = 3000 * 0.040
print("busy:", busy, "| pool with 50% safety:", round(busy * 1.5))
busy: 120.0 | pool with 50% safety: 180

How it works: Little's law (L = λW) sizes connection pools and thread pools.

P7Advanced

Slow pages from fan-out

A search page queries 40 shards in parallel; each shard is slow 0.5% of the time. What percentage of pages are slow?

Input
40 shards, 0.5%
Expected output
18.2 %
Show solution and explanation
print(round((1 - (1 - 0.005) ** 40) * 100, 1), "%")
18.2 %

How it works: Even rare slowness becomes common with fan-out; techniques like hedged requests send a backup request after a short delay.

P8Business problem

Autoscaling vs fixed fleet

Peak needs 40 servers for 4 hours a day; the other 20 hours need 10. A server costs Rs 30 per hour. Compare the daily cost of a fixed 40-server fleet with autoscaling.

Input
40 × 4 h + 10 × 20 h
Expected output
fixed: Rs 28800 | autoscaled: Rs 10800 | saving: 62 %
Show solution and explanation
fixed = 40 * 24 * 30
auto = (40 * 4 + 10 * 20) * 30
print("fixed: Rs", fixed, "| autoscaled: Rs", auto, "| saving:", round((1 - auto / fixed) * 100), "%")
fixed: Rs 28800 | autoscaled: Rs 10800 | saving: 62 %

How it works: Elastic capacity is one of the biggest cost benefits of the cloud. (Prices are illustrative.)

P9Business problem

Cost per thousand orders

Infrastructure costs Rs 18 lakh a month for 9 crore orders. After growth it costs Rs 30 lakh for 18 crore orders. Print the cost per 1,000 orders before and after.

Input
18 lakh / 9 crore, 30 lakh / 18 crore
Expected output
Rs 20.00 per 1,000 orders
Rs 16.67 per 1,000 orders
Show solution and explanation
for cost, orders in [(18_00_000, 9_00_00_000), (30_00_000, 18_00_00_000)]:
    print(f"Rs {cost / orders * 1000:.2f} per 1,000 orders")
Rs 20.00 per 1,000 orders Rs 16.67 per 1,000 orders

How it works: Good scaling lowers the unit cost as volume grows, even when the total bill rises.

Real-world scenario questions (asked in company interviews)

Scaling and estimation questions from interviews and production incidents. Decide your answer first, then open the model answer.

System design · estimationFresher

Estimate storage for a photo feature

A food app will let users attach up to 2 photos to each review. 50 lakh reviews are written a day and 20% include photos. Estimate storage per year.

The interviewer is checking: can you structure an estimate with clear assumptions and units?

Show a strong answer

Assume 1.5 photos per review that has photos, 3 MB per photo, plus 2 thumbnails of 100 KB. Photos per day = 50 lakh × 20% × 1.5 = 15 lakh. Storage per day ≈ 15 lakh × 3.2 MB ≈ 4.6 TB. Per year ≈ 1.7 PB before replication (object storage handles durability internally). Then discuss: compress or resize images, move old photos to cheaper tiers, serve through a CDN.

Avoid: jumping to a number without assumptions, or forgetting thumbnails and tiers.

Scaling · architecture1–3 years

The API is slow at 8 pm every day

A delivery app’s API slows down every evening. Adding more API servers didn’t help.

The interviewer is checking: do you find the real bottleneck before scaling?

Show a strong answer
  1. Check where time goes: API CPU, database CPU and connections, cache hit ratio, external calls.
  2. If the database is the bottleneck, more API servers just add more database connections and make it worse.
  3. Fixes: cache hot reads, add read replicas, tune slow queries and indexes, pool connections, scale the database up; shard if writes are the limit.
  4. Then autoscale the stateless tier with a sensible maximum.

Avoid: adding servers blindly; scaling the wrong tier.

Reliability · statelessness1–3 years

Users get logged out randomly

After moving from one server to five behind a load balancer, users complain of random logouts and empty carts.

The interviewer is checking: do you recognise in-memory session state?

Show a strong answer

Sessions and carts are stored in each server’s memory, so requests landing on another server don’t find them. Move session state to a shared store (Redis) or use signed tokens (JWT) for authentication, and store carts in Redis or the database. Avoid relying on sticky sessions, which break when servers restart and unbalance load.

Avoid: increasing session timeouts or pinning all users to one server.

Performance · advanced3+ years

Doubling the cluster made the job slower

A distributed job ran in 40 minutes on 50 nodes. On 100 nodes it took 55 minutes.

The interviewer is checking: do you understand contention and coordination costs?

Show a strong answer

Look for shared bottlenecks (one metadata service, one database, a global lock, a single reducer) and coordination overhead (shuffles that grow with node count, many small tasks, frequent synchronisation). Measure stage by stage. Fixes include fewer, larger partitions, reducing shuffles (broadcast small tables, pre-partition data), removing global locks, and choosing the node count near the measured optimum rather than “as many as possible”.

Avoid: assuming linear scaling and adding even more nodes.