Skip to content

📈 Level 2 — Scalability

Learn how to design systems that grow from 100 users to 100 million.


2.1 Vertical vs Horizontal Scaling

Visual Comparison

VerticalHorizontal
CostExpensive hardwareCommodity servers
LimitHardware ceilingNearly unlimited
Fault ToleranceSingle point of failureRedundant
ComplexitySimpleRequires coordination

💻 JS Example: Horizontal Scaling with Node.js Cluster

In Node.js, you can scale horizontally across multiple CPU cores on a single machine using the cluster module.

javascript
const cluster = require("cluster");
const http = require("http");
const numCPUs = require("os").cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers for each CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker) => {
    console.log(`worker ${worker.process.pid} died. Respawning...`);
    cluster.fork();
  });
} else {
  // Workers can share any TCP connection
  http
    .createServer((req, res) => {
      res.writeHead(200);
      res.end("Hello from scaled server!\n");
    })
    .listen(8000);
}

2.2 CAP Theorem

A distributed system can only guarantee 2 out of 3 properties simultaneously.

System TypeGuaranteesExample Scenario
CPCorrect data, may be unavailableBanking: Reject transaction if network split to avoid double spending.
APAlways responds, may return stale dataSocial Media: Show old feed rather than an error page.
CAConsistent + AvailableLocal DB: High reliability on a single machine, but fails on network split.

2.3 Consistency Models

Eventual vs Strong Consistency

💻 JS Example: Handling Eventual Consistency

When dealing with eventual consistency, clients often need to retry or "poll" for the latest state.

javascript
async function getConsistentData(id, expectedValue, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const data = await fetch(`/api/data/${id}`).then((r) => r.json());
    if (data.value === expectedValue) return data;

    console.log(`Stale data detected, retry ${i + 1}...`);
    await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1s
  }
  throw new Error("Failed to reach consistency");
}

2.4 How to Scale: Step by Step

The 5-Stage Journey


2.5 Rate Limiting

Protects your system from being overwhelmed by too many requests.

💻 JS Example: Basic Token Bucket Implementation

A conceptual look at how a rate limiter might track requests in memory.

javascript
class RateLimiter {
  constructor(limit, windowMs) {
    this.limit = limit;
    this.requests = new Map(); // Store counts per IP
  }

  isAllowed(ip) {
    const now = Date.now();
    const count = this.requests.get(ip) || 0;

    if (count >= this.limit) {
      return false; // Rate limit exceeded
    }

    this.requests.set(ip, count + 1);
    // Reset limit after window expires
    setTimeout(() => this.requests.delete(ip), 60000);
    return true;
  }
}

✅ Checklist Before Moving On

  • [ ] I can explain CAP theorem with a real-world example
  • [ ] I know when to scale vertically vs horizontally
  • [ ] I understand eventual vs strong consistency
  • [ ] I can draw the 5-stage scaling journey

➡️ Next: Level 3 — Databases

Released under the ISC License.