Skip to content

πŸš€ Scaling from 1 to 1 Billion Users β€” The Complete Journey ​

IntermediateIntermediate1–3 yrs experience Builds on the foundations. Expect trade-off discussions, light math, and short code snippets.Prerequisites: Solid grasp of networking, databases, and at least one web framework.

A step-by-step architectural evolution of a real-world system, from a single laptop server to a globally distributed platform serving billions of users β€” with real company examples, detailed diagrams, code, and failure-mode analysis at every stage.


πŸ—ΊοΈ The Full Road Map at a Glance ​

PhaseUsersKey BottleneckKey Solution
11 – 1 KEverything on one boxMonolith β€” just ship it
21 K – 10 KApp & DB fight over CPU/RAMSeparate the database
310 K – 100 KSingle app server maxes outLoad balancer + read replicas
4100 K – 1 MDB reads are too slow/expensiveRedis cache + CDN
51 M – 10 MSingle DB can't hold all data/writesDatabase sharding
610 M – 100 MMonolith deployment is unmanageableMicroservices + message queues
7100 M – 1 BLatency from physics (speed of light)Multi-region global distribution

IMPORTANT

There is no single "correct" architecture. Each phase is a response to a real bottleneck. The goal is to solve today's problem without over-engineering tomorrow's problem.


🏒 Real-World Company Reference ​

Throughout this guide, we will track how real companies evolved through these exact phases:

CompanyPhase 1Where they are today
InstagramDjango + PostgreSQL on a single serverMulti-region microservices with Cassandra, Memcached
TwitterRuby on Rails monolithDistributed Manhattan DB + Kafka event bus
WhatsAppErlang + FreeBSD on a single server2 billion users, ~50 engineers, Erlang BEAM clusters
NetflixDVD mailers β†’ single Java monolithAWS-native, 3-region active-active, 1000s of microservices
SlackPHP monolithVitess (MySQL sharding) + Kafka + multi-datacenter

Phase 1 β€” The Monolith (1 to 1,000 Users) ​

Philosophy: "Make it work." ​

Everything lives on a single machine. Web server, application logic, and the database all run on the same box.

What it looks like in practice ​

You deploy a Node.js + Express app with SQLite or PostgreSQL on a single cloud instance. DNS (myapp.com) points directly to the server's public IP address.

myapp.com  β†’  A record  β†’  104.21.45.200 (your single server)

Code β€” The Classic Express Monolith ​

javascript
// server.js β€” Everything in one file
const express = require("express");
const Database = require("better-sqlite3");

const app = express();
const db = new Database("./myapp.db");

// Route: Get a user by ID
app.get("/api/users/:id", (req, res) => {
  const user = db
    .prepare("SELECT * FROM users WHERE id = ?")
    .get(req.params.id);

  if (!user) return res.status(404).json({ error: "Not found" });
  return res.json(user);
});

// Route: Create a user
app.post("/api/users", express.json(), (req, res) => {
  const { name, email } = req.body;
  const result = db
    .prepare("INSERT INTO users (name, email) VALUES (?, ?)")
    .run(name, email);

  return res.status(201).json({ id: result.lastInsertRowid, name, email });
});

app.listen(3000, () => console.log("πŸš€ Server running on port 3000"));

Pros & Cons ​

βœ… Pros❌ Cons
Extremely simple to build and deploySingle Point of Failure β€” server crash = 100% outage
Cheapest possible ($5–$20/month)Cannot handle traffic spikes
Zero operational complexityApp & DB compete for the same CPU and RAM
Fast iteration cyclesVertical scaling has a hard ceiling

🏒 Instagram at Phase 1 ​

In 2010, Instagram launched on a single EC2 m1.large instance running Django + PostgreSQL. They had 25,000 users on launch day. The single server held until about 1 week post-launch.

πŸ’₯ Failure Mode at this Phase ​

πŸ“± 10,000 users hit the site at once
    ↓
πŸ–₯️ Single server CPU β†’ 100%
    ↓
πŸ’€ DB queries time out (same CPU, competing with web requests)
    ↓
πŸ”΄ All users see 502 Bad Gateway
    ↓
⏱️ Server takes 10 minutes to recover after traffic subsides

Trigger to move to Phase 2: You notice the DB process consuming 60–70% CPU, starving the web server. Response times exceed 500ms.


Phase 2 β€” Database Separation (1,000 to 10,000 Users) ​

Philosophy: "Stop the fighting over resources." ​

Move the database to its own dedicated server so they no longer compete for CPU and RAM.

Key insight: Private Network ​

The app server and DB server communicate over a private VPC subnet β€” never exposed to the internet. This is both faster (no TLS overhead) and safer (DB is not reachable from outside).

App Server IP:  10.0.1.10  (private)
DB  Server IP:  10.0.2.20  (private β€” NOT routable from internet)

Vertical Scaling Strategy ​

At this phase you scale both servers independently by vertical scaling β€” buying a bigger machine:

With 28 GB of RAM dedicated to PostgreSQL, the DB can cache entire tables in memory β€” making reads 100Γ— faster.

🏒 WhatsApp at Phase 2 ​

WhatsApp ran a highly optimized Erlang server on a single FreeBSD machine before separating their Mnesia (Erlang DB) to dedicated nodes. At 1 million users, they had 2 servers total β€” one for app logic, one for messages.

πŸ’₯ Failure Mode at this Phase ​

πŸ“ˆ Traffic grows: one app server can't handle 10K concurrent users
    ↓
πŸ–₯️ App server CPU pegged at 100%
    ↓
πŸ“Š DB is fine (it has headroom), but the app can't forward requests fast enough
    ↓
⏱️ New requests queue up β†’ timeout β†’ users see 504 Gateway Timeout

Trigger to move to Phase 3: CPU on the app server consistently above 70% during peak hours.


Phase 3 β€” Horizontal Scaling & Load Balancers (10,000 to 100,000 Users) ​

Philosophy: "Stop relying on one horse. Build a team." ​

Vertical scaling has limits. Instead of one powerful server, run many commodity servers and distribute traffic among them.

⚠️ Critical Rule: Stateless App Servers ​

For horizontal scaling to work, app servers MUST be stateless. This means:

❌ WRONG β€” Stateful (breaks with multiple servers):
   User logs in β†’ Session stored in App Server 1's memory
   Next request goes to App Server 2 β†’ "Who are you? Please log in again."

βœ… CORRECT β€” Stateless (works with any number of servers):
   User logs in β†’ Session token stored in Redis (shared)
   Next request goes to App Server 3 β†’ Reads token from Redis β†’ Authenticated βœ…

Code β€” Stateless Session Management with Redis ​

javascript
// session-middleware.js
const session = require("express-session");
const RedisStore = require("connect-redis")(session);
const redis = require("redis");

// βœ… Sessions stored in Redis β€” NOT in app server memory
const redisClient = redis.createClient({
  url: process.env.REDIS_URL, // e.g. redis://10.0.3.10:6379
});

module.exports = session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true, // HTTPS only
    httpOnly: true, // No JS access to cookie
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  },
});

// Now ANY app server can handle ANY user's request
// because session data lives in shared Redis, not local RAM

Load Balancing Algorithms ​

AlgorithmBest ForGotcha
Round RobinEqual-capacity servers, stateless APIsBad for long-lived connections
Least ConnectionsMixed request durations (some fast, some slow)Requires connection tracking
IP HashWhen you need "sticky" sessionsUneven distribution if few IPs
WeightedMixed hardware (some servers are bigger)Manual weight tuning required

Master-Slave Read/Write Split ​

Most applications are 80–90% reads and only 10–20% writes. Read replicas exploit this asymmetry:

🏒 Twitter at Phase 3 ​

By 2007, Twitter introduced a Nginx load balancer in front of multiple Rails servers. They split reads/writes across a MySQL master/slave setup. The famous "fail whale" era β€” their bottleneck wasn't the LB, it was the DB write master becoming a hot spot.


Phase 4 β€” Caching & CDNs (100,000 to 1 Million Users) ​

Philosophy: "Never compute the same thing twice." ​

The database is now the bottleneck β€” not because it's underpowered, but because you're asking it the same questions millions of times. The solution is caching.

Two Layers of Caching ​

Layer 1: Application Cache (Redis) β€” for dynamic data ​

Layer 2: CDN β€” for static assets ​

Code β€” The Cache-Aside Pattern (Full Implementation) ​

javascript
// cache-service.js β€” Production-grade cache wrapper
const redis = require("redis");

class CacheService {
  constructor() {
    this.client = redis.createClient({
      url: process.env.REDIS_URL,
      socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 2000) },
    });
    this.client.connect();
  }

  /**
   * Cache-Aside Pattern: Read-Through with automatic population
   * @param {string} key       - Unique cache key (e.g. "product:789")
   * @param {Function} fetcher - Async function to fetch from DB on cache miss
   * @param {number} ttl       - Time-to-live in seconds (default: 5 min)
   */
  async getOrSet(key, fetcher, ttl = 300) {
    // 1️⃣ Try cache first (ultra-fast)
    const cached = await this.client.get(key);

    if (cached !== null) {
      console.log(`⚑ CACHE HIT: ${key}`);
      return JSON.parse(cached);
    }

    // 2️⃣ Cache miss β€” go to DB
    console.log(`🐒 CACHE MISS: ${key} β€” querying database...`);
    const data = await fetcher();

    if (data === null) return null;

    // 3️⃣ Populate cache for the next request
    await this.client.setEx(key, ttl, JSON.stringify(data));
    return data;
  }

  // Invalidate cache when data changes (e.g. user updates their profile)
  async invalidate(key) {
    await this.client.del(key);
    console.log(`πŸ—‘οΈ  Cache invalidated: ${key}`);
  }
}

// ─────────────────────────────────────────────────────────
// Usage in a route handler
// ─────────────────────────────────────────────────────────
const cache = new CacheService();

app.get("/api/products/:id", async (req, res) => {
  const { id } = req.params;

  const product = await cache.getOrSet(
    `product:${id}`, // cache key
    () => db.query("SELECT * FROM products WHERE id = ?", [id]), // DB fetcher
    600 // 10-minute TTL
  );

  if (!product) return res.status(404).json({ error: "Not found" });
  res.json(product);
});

// When a product is updated, invalidate its cache
app.put("/api/products/:id", async (req, res) => {
  const { id } = req.params;
  await db.query("UPDATE products SET ... WHERE id = ?", [id]);
  await cache.invalidate(`product:${id}`); // πŸ—‘οΈ Clear stale cache
  res.json({ success: true });
});

Cache Hit Rate Math β€” Why This Matters ​

πŸ“Š Scenario: Product page queried 100,000 times/hour
             DB query cost: $0.00001 per query
             Cache hit rate: 99%

Without cache:
  100,000 queries Γ— $0.00001 = $1.00 / hour = $720 / month

With 99% cache hit rate:
  1,000 DB queries Γ— $0.00001 = $0.01 / hour = $7.20 / month

πŸ’° Savings: ~$712/month just on one endpoint
⚑ Latency: 50ms β†’ 0.5ms (100Γ— faster for cached requests)

Caching Strategies Compared ​

StrategyHow It WorksBest ForRisk
Cache-AsideApp checks cache β†’ miss β†’ query DB β†’ populate cacheGeneral purpose readsThundering herd on cold start
Write-ThroughEvery write updates DB AND cache simultaneouslyData that's written and read frequentlyWrite latency increases
Write-BehindWrite to cache first, async flush to DBVery high-write scenariosData loss if cache crashes before flush
Read-ThroughCache sits between app and DB; cache fetches from DB on missWhen you want cache logic hidden from appCache library required

🏒 Instagram at Phase 4 ​

Instagram deployed Memcached (later Redis) in front of their PostgreSQL database at ~100K users. Their cache hit rate reached 99.5% β€” meaning only 1 in 200 requests actually hit the DB. They also adopted CloudFront for photo serving, reducing their bandwidth costs by 75%.


Phase 5 β€” Database Sharding (1 Million to 10 Million Users) ​

Philosophy: "When one database can't hold the world, give it neighbors." ​

Even with caching, eventually:

  • The Master DB disk fills up (100 million rows = hundreds of GBs)
  • Write throughput hits hardware limits (~5,000 writes/sec for a single PostgreSQL instance)
  • Table locks during writes cause read slowdowns

Sharding splits data horizontally across multiple independent database servers.

Sharding Key Selection β€” The Most Important Decision ​

Code β€” Shard Router Implementation ​

javascript
// shard-router.js β€” Simple hash-based shard routing
const crypto = require("crypto");

class ShardRouter {
  constructor(shards) {
    // shards: array of database connection pools
    // e.g. [pool_shard0, pool_shard1, pool_shard2, pool_shard3]
    this.shards = shards;
    this.numShards = shards.length;
  }

  /**
   * Determine which shard holds data for a given key
   * Uses consistent hashing to avoid modulo remapping issues
   */
  getShardId(shardKey) {
    // Convert shardKey to a consistent numeric hash
    const hash = crypto
      .createHash("md5")
      .update(String(shardKey))
      .digest("hex");

    // Take first 8 hex chars β†’ integer β†’ modulo
    return parseInt(hash.substring(0, 8), 16) % this.numShards;
  }

  /**
   * Get the database pool for a specific user
   */
  getDb(userId) {
    const shardId = this.getShardId(userId);
    console.log(`πŸ”€ user_id=${userId} β†’ Shard #${shardId}`);
    return this.shards[shardId];
  }
}

// ─────────────────────────────────────────────────────────
// Usage
// ─────────────────────────────────────────────────────────
const router = new ShardRouter([
  createPool("postgresql://shard-0:5432/myapp"),
  createPool("postgresql://shard-1:5432/myapp"),
  createPool("postgresql://shard-2:5432/myapp"),
  createPool("postgresql://shard-3:5432/myapp"),
]);

app.get("/api/users/:userId/orders", async (req, res) => {
  const { userId } = req.params;

  // πŸ”€ Automatically routes to the correct shard
  const db = router.getDb(userId);
  const orders = await db.query(
    "SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC",
    [userId]
  );

  res.json(orders);
});

Sharding Strategies Compared ​

StrategyHowProsCons
Hash-basedhash(key) % NEven data distributionResharding moves ALL data
Range-basedIDs 0–1M β†’ Shard A, 1M–2M β†’ Shard BEasy range scansHotspots (new users all on latest shard)
GeographicUS users β†’ US shardData sovereignty complianceUneven sizes
Consistent HashingKeys map on a ring; add node β†’ minimal migrationMinimal data movement when adding shardsMore complex
Directory-basedLookup table maps key β†’ shardFlexible, easy re-assignmentLookup table = single point of failure

Cross-Shard Queries β€” The Painful Reality ​

WARNING

Cross-shard joins are the #1 pain point of sharding. Avoid them by designing your shard key around your most common access pattern. For analytics queries that span all shards, consider a separate data warehouse (BigQuery, Redshift) that receives replicated data from all shards.

🏒 Slack at Phase 5 ​

Slack uses Vitess (a MySQL sharding framework, also used by YouTube) to shard their channel/message data by workspace_id. Each workspace's messages live on a single shard, making queries fast. Cross-workspace analytics go to a separate Redshift data warehouse.


Phase 6 β€” Microservices & Message Queues (10 Million to 100 Million Users) ​

Philosophy: "Conway's Law β€” your architecture mirrors your org chart." ​

"Organizations which design systems are constrained to produce designs which are copies of the communication structures of those organizations." β€” Mel Conway, 1967

At 10M+ users, you have hundreds of engineers. A monolith means:

  • One team's buggy code can crash the entire app
  • You can't deploy the payment fix without redeploying the whole 500,000-line codebase
  • You can't scale media processing without scaling everything

Microservices split the monolith into independently deployable services.

API Gateway Pattern ​

The API Gateway is the single entry point for all clients. It handles:

Message Queues β€” The Shock Absorber ​

Why this is powerful:

  • API responds in milliseconds, not 30 minutes
  • If 10,000 videos are uploaded at once, they queue up β€” workers process at their own pace without crashing
  • If a worker crashes mid-encoding, the message is not acknowledged β†’ Kafka requeues it β†’ another worker picks it up β†’ at-least-once delivery

Code β€” Producer/Consumer with Kafka (Node.js) ​

javascript
// producer.js β€” API Server side (fast, returns immediately)
const { Kafka } = require("kafkajs");

const kafka = new Kafka({ brokers: ["kafka-broker:9092"] });
const producer = kafka.producer();

async function enqueueVideoProcessing(videoId, userId, s3Key) {
  await producer.send({
    topic: "video-processing-tasks",
    messages: [
      {
        key: String(videoId), // Kafka partitions by key β†’ same video always same partition
        value: JSON.stringify({
          videoId,
          userId,
          s3Key,
          enqueuedAt: new Date().toISOString(),
        }),
      },
    ],
  });

  console.log(`πŸ“¬ Enqueued video encoding task for videoId=${videoId}`);
}

// API Route β€” returns immediately
app.post("/api/videos/upload", async (req, res) => {
  const s3Key = await uploadToS3(req.file); // Store raw file
  const video = await db.insertVideo({ userId: req.user.id, s3Key });

  await enqueueVideoProcessing(video.id, req.user.id, s3Key);

  res.status(202).json({
    message: "Video uploaded! Processing will complete in a few minutes.",
    videoId: video.id,
  });
});

// ─────────────────────────────────────────────────────────
// consumer.js β€” Worker side (separate process / container)
// ─────────────────────────────────────────────────────────
const consumer = kafka.consumer({ groupId: "video-encoding-group" });

async function startWorker() {
  await consumer.connect();
  await consumer.subscribe({ topic: "video-processing-tasks" });

  await consumer.run({
    eachMessage: async ({ message }) => {
      const { videoId, userId, s3Key } = JSON.parse(message.value);
      console.log(`βš™οΈ Processing video ${videoId}...`);

      try {
        // Encode video to multiple qualities (takes minutes)
        const outputs = await ffmpegEncode(s3Key);
        await db.updateVideo(videoId, { status: "ready", outputs });

        // Notify the user
        await notificationService.push(userId, {
          title: "🎬 Your video is ready!",
          body: `Video #${videoId} has finished processing.`,
        });

        // βœ… Commit offset β€” message is "done"
        // If we crash before this line, Kafka will re-deliver to another worker
      } catch (err) {
        console.error(`❌ Failed to encode video ${videoId}:`, err);
        // Message NOT committed β†’ Kafka will retry
      }
    },
  });
}

Microservices vs Monolith Trade-offs ​

Comparing

Monolith

Single deployable, shared DB

Microservices

Independent services, distributed data

  • Deployment
    One deployable unit
    Independent per service
  • Scaling
    Must scale everything together
    Scale only what needs it
  • Fault isolation
    One bug can crash everything
    One service down β‰  system down
  • Developer experience
    Simple local dev
    Complex (Docker Compose, service mesh)
  • Data consistency
    Easy (same DB, transactions)
    Hard (distributed transactions, sagas)
  • Network overhead
    Function calls (nanoseconds)
    HTTP/gRPC (milliseconds)
  • Team ownership
    Shared, risky
    Clear per-service ownership

TIP

Don't start with microservices. Start with a modular monolith (well-organized code with clear boundaries), and extract services only when you have a clear scaling or team-independence need. Martin Fowler's rule: "Don't start with microservices. Let them emerge from a monolith."

🏒 Netflix at Phase 6 ​

Netflix migrated from a single Java monolith to 700+ microservices between 2009–2012. Key extractions:

  • Zuul (API Gateway) β€” handles auth, routing, A/B testing
  • Hystrix (Circuit Breaker) β€” stops cascade failures
  • Eureka (Service Discovery) β€” services find each other dynamically They created most of these tools and then open-sourced them as Netflix OSS.

Phase 7 β€” Global Distribution (100 Million to 1 Billion Users) ​

Philosophy: "You cannot beat the speed of light. You can only get closer." ​

Light travels through fiber optic cable at ~200,000 km/s. Distance from New York to Tokyo: ~10,800 km. Minimum one-way latency: ~54 ms (and that's just physics, before any processing).

For 1 billion users across continents, you must bring the servers geographically closer to the users.

Active-Active vs Active-Passive ​

AspectActive-ActiveActive-Passive
CapacityFull capacity across all DCs50% capacity (standby wasted)
Failover timeInstant (DNS health check)30–120 seconds
ComplexityHigh (data conflicts, write routing)Lower
CostHigher (all DCs running)Lower (standby is cheaper)
Who uses itNetflix, Google, FacebookMost mid-size companies

Data Replication at Global Scale ​

The Conflict Problem β€” When Two DCs Write the Same Record ​

πŸ‡ΊπŸ‡Έ US User: Updates their profile bio at T=0ms
πŸ‡ͺπŸ‡Ί EU User (same account, different device): Updates profile bio at T=100ms

Both DCs accept the write locally.
At T=200ms, replication arrives:
    US DC receives EU's update
    EU DC receives US's update

⚠️ Which one wins?

Common Resolution Strategies:

StrategyHowRisk
Last-Write-Wins (LWW)Highest timestamp winsClock skew β†’ wrong winner
CRDTsData structures that auto-mergeOnly works for specific data types (counters, sets)
Vector ClocksTrack causality chainsComplex client-side logic
Write to one regionAll writes go to US primary, reads anywhereAdds latency for EU/AP writers

Auto-Scaling at Global Scale ​

yaml
# aws-autoscaling-policy.yaml
AutoScalingGroup:
  MinSize: 5
  MaxSize: 50
  DesiredCapacity: 10
  HealthCheckType: ELB
  HealthCheckGracePeriod: 300

ScaleOutPolicy:
  MetricName: CPUUtilization
  Threshold: 70
  ComparisonOperator: GreaterThanThreshold
  EvaluationPeriods: 2
  AdjustmentType: PercentChangeInCapacity
  ScalingAdjustment: 50 # Add 50% more instances

ScaleInPolicy:
  MetricName: CPUUtilization
  Threshold: 30
  ComparisonOperator: LessThanThreshold
  EvaluationPeriods: 10 # Be conservative β€” don't scale in too fast
  AdjustmentType: ChangeInCapacity
  ScalingAdjustment: -2 # Remove 2 instances at a time

🏒 Netflix at Phase 7 β€” "Chaos Engineering" ​

Netflix runs in 3 AWS regions (us-east-1, eu-west-1, ap-southeast-1) in an active-active configuration. To ensure their system truly survives regional failures, they created Chaos Monkey β€” a tool that randomly kills production servers to verify that auto-recovery works.

They extended this to Chaos Kong, which simulates an entire AWS region going offline. Their systems must automatically reroute 1/3 of global traffic with no user impact.


🌐 The Complete Architecture Picture β€” 1 to 1 Billion ​


πŸ“Š The Numbers at Each Phase ​

PhaseUsersRPS (est.)DB Reads/sEngineersAWS Monthly Cost (est.)
11K10501–2~$20
210K1005002–5~$200
3100K1,0005,0005–15~$2,000
41M10,000500*15–50~$10,000
510M100,0005,000*50–200~$100,000
6100M1,000,00050,000*200–2K~$1,000,000
71B10,000,000500,000*2K–10K+~$10,000,000+

* Cache-adjusted: actual DB reads are a tiny fraction of total reads at scale


⚑ Latency Numbers Every Engineer Must Know ​

OperationTime
L1 cache reference0.5 ns
L2 cache reference7 ns
Main memory (RAM) reference100 ns
SSD random read150,000 ns (0.15 ms)
Redis GET (local network)500,000 ns (0.5 ms)
PostgreSQL query (indexed, cached)10,000,000 ns (10 ms)
PostgreSQL query (disk read)50,000,000 ns (50 ms)
HTTP request NYC β†’ London80,000,000 ns (80 ms)
HTTP request NYC β†’ Tokyo150,000,000 ns (150 ms)

TIP

These numbers reveal why caching is so impactful: a Redis hit (0.5ms) is 100Γ— faster than a DB query (50ms). Serving a user in Tokyo from a Tokyo data center (15ms) vs Virginia (150ms) is 10Γ— faster.


πŸ† The Golden Rules of Scalability ​

#RuleWhy
1Keep web tier statelessEnables horizontal scaling β€” add/remove servers freely
2Build redundancy at every tierEliminates single points of failure
3Cache aggressively90%+ of DB load can be absorbed by Redis
4Decouple with queuesSystems absorb traffic bursts without cascade failures
5Separate reads from writesMost apps are 80–90% reads β€” exploit this asymmetry
6Shard data horizontallyWhen one DB can't hold the world
7Go globalPhysics limits latency; proximity is the only fix
8Measure everything"You can't scale what you can't measure"
9Embrace eventual consistencyStrong consistency is expensive; most apps don't need it
10Don't pre-optimizeSolve today's bottleneck, not tomorrow's imagined one

NOTE

Continue learning:

Released under the ISC License.