Back to Scenarios & Design Questions
Q65Question 65 of 67

Why should you first clarify what "1 million users" means

Key Concepts
  • One million users is a marketing number, not an engineering one: registered, daily-active, and concurrent differ by orders of magnitude
  • The real engineering driver is peak requests per second, derived from daily-active users with a peak-to-average multiplier
  • The read-to-write ratio shapes the entire data strategy: read-heavy benefits from caching, write-heavy needs sharding
  • Data size and growth rate drive storage and partitioning decisions
  • Latency and consistency requirements are the decisive drivers: a bank balance demands strong consistency
  • These four drivers, not the user count, determine capacity, data architecture, and the consistency model
  • Always derive these numbers before designing to avoid over-engineering or under-provisioning

Definition

One million users is a marketing number, not an engineering one: you must convert it into peak requests per second, read-to-write ratio, data size, and latency and consistency targets before you can design anything.
  • Registered users – everyone who ever signed up; most are dormant and irrelevant to capacity.
  • Daily-active users (DAU) – those who actually use the system in a day; the basis for request estimation.
  • Concurrent users – those active at the same instant; what hardware must survive at peak.
  • Peak requests per second – derived from DAU, per-user request rate, and a peak-to-average multiplier.
  • Read-to-write ratio – shapes the data strategy: caching for reads, sharding for writes.
  • Data size and growth – determines whether a single database suffices or sharding is needed.
  • Latency and consistency – tight latency forces caching; strong consistency constrains distribution.

Imagine someone tells National Bank of India "we have a million customers, build a branch big enough." That tells you almost nothing about how many counters to build. A million customers on the books might mean only fifty thousand visit in a day, and of those maybe two thousand are inside at the busiest moment on salary day. If you built for a million at once, you waste a fortune. If you built for the average and ignored salary-day rush, the branch overflows. So before pouring any concrete, a good architect asks: how many are actually inside at peak, how many just read their balance versus make a transaction, how much paperwork do we store, and how fast must each customer be served?

Understanding the Concepts

Why is "1 million users" ambiguous?

Registered users are everyone who ever signed up, and most are dormant. Daily-active users are those who actually use the system in a day. Concurrent users are those active at the same instant, and peak concurrency is what hardware must survive. These three can differ by one or two orders of magnitude. A million registered users might translate to a hundred thousand daily-active and only a few thousand concurrent at peak.

JAVA
package com.bank.capacity;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class CapacityCalculator {

    public static void main(String[] args) {
        long registeredUsers = 1_000_000;
        double dauPercentage = 0.10; // 10% are daily active
        long dau = (long) (registeredUsers * dauPercentage);

        double concurrentPercentage = 0.05; // 5% of DAU at peak
        long peakConcurrent = (long) (dau * concurrentPercentage);

        System.out.println("Registered users:  " + registeredUsers);
        System.out.println("Daily active:      " + dau);
        System.out.println("Peak concurrent:   " + peakConcurrent);
    }
}
TEXT
Registered users:  1,000,000
Daily active:      100,000
Peak concurrent:   5,000

Designing for 1,000,000 concurrent is 200x over-provisioned!

How do you derive peak requests per second?

The real engineering driver is requests per second, especially peak. You derive it from DAU, a per-user request rate, and a peak-to-average multiplier. Traffic is never flat: salary day, market open, or a marketing push can cause three to ten times the average rate.

JAVA
package com.bank.capacity;

public class RequestRateEstimator {

    public static void main(String[] args) {
        long dau = 100_000;
        int requestsPerUserPerDay = 30;
        int activeHoursPerDay = 10;
        double peakMultiplier = 5.0; // Salary day spike

        long totalRequestsPerDay = dau * requestsPerUserPerDay;
        double avgRequestsPerSecond =
            totalRequestsPerDay / (activeHoursPerDay * 3600.0);
        double peakRequestsPerSecond =
            avgRequestsPerSecond * peakMultiplier;

        System.out.printf("Total requests/day:  %,d%n",
            totalRequestsPerDay);
        System.out.printf("Avg requests/sec:    %.0f%n",
            avgRequestsPerSecond);
        System.out.printf("Peak requests/sec:   %.0f%n",
            peakRequestsPerSecond);
    }
}
TEXT
Total requests/day:  3,000,000
Avg requests/sec:    83
Peak requests/sec:   417

A system sized for average (83 rps) will collapse at peak (417 rps)

How does the read-to-write ratio shape the data strategy?

A read-heavy system benefits enormously from caching and read replicas. A write-heavy system needs batching, async processing, and sharding. Knowing the ratio tells you where to spend your scaling effort.

JAVA
package com.bank.capacity;

public class ReadWriteAnalyzer {

    public static void main(String[] args) {
        // National Bank of India mobile app analysis
        int balanceChecks = 2000;   // reads per second at peak
        int transfers = 50;          // writes per second at peak
        int statementViews = 300;    // reads per second at peak

        int totalReads = balanceChecks + statementViews;
        int totalWrites = transfers;
        double readPercentage =
            (totalReads * 100.0) / (totalReads + totalWrites);

        System.out.printf("Reads/sec:   %d%n", totalReads);
        System.out.printf("Writes/sec:  %d%n", totalWrites);
        System.out.printf("Read ratio:  %.1f%%%n", readPercentage);
        System.out.println("Strategy:    Cache + read replicas");
    }
}
TEXT
Reads/sec:   2300
Writes/sec:  50
Read ratio:  97.8%
Strategy:    Cache + read replicas (most traffic never touches primary DB)

| Ratio | Strategy | Example |
|---|---|---|
| 90%+ reads | Cache + read replicas | Balance checks |
| 50-50 | Write optimization + sharding | Trading platform |
| 90%+ writes | Async batching + wide-column DB | Audit logs |

How does data size drive partitioning decisions?

Ten gigabytes fits comfortably on one node. Ten terabytes growing by a terabyte a month forces you to think about sharding, archival, and tiered storage early.

JAVA
package com.bank.capacity;

public class DataSizeEstimator {

    public static void main(String[] args) {
        long accounts = 1_000_000;
        int bytesPerAccount = 500;      // account record
        int transactionsPerAccountPerMonth = 30;
        int bytesPerTransaction = 200;

        long accountDataMB = (accounts * bytesPerAccount) / (1024 * 1024);
        long txPerMonth = accounts * transactionsPerAccountPerMonth;
        long txDataPerMonthGB =
            (txPerMonth * bytesPerTransaction) / (1024L * 1024 * 1024);

        System.out.printf("Account data:     %d MB (fits one node)%n",
            accountDataMB);
        System.out.printf("Tx data/month:    %d GB%n", txDataPerMonthGB);
        System.out.printf("Tx data/year:     %d GB%n",
            txDataPerMonthGB * 12);
        System.out.println("Decision: archive old transactions, " +
            "consider sharding after 1 year");
    }
}
TEXT
Account data:     476 MB (fits one node)
Tx data/month:    5 GB
Tx data/year:     67 GB
Decision: archive old transactions, consider sharding after 1 year

How do latency and consistency requirements drive design?

A tight latency target like P99 under 100 milliseconds forces caching and co-location. Strong consistency for bank balances constrains how aggressively you can cache, replicate, and distribute, while a social feed can tolerate eventual consistency and scale much more freely.

JAVA
package com.bank.capacity;

public class ConsistencyDecision {

    public enum Consistency {
        STRONG,   // Bank balance: must be exactly correct
        EVENTUAL  // Statement view: can be seconds behind
    }

    public static Consistency decide(String useCase) {
        return switch (useCase) {
            case "balance-check" -> Consistency.STRONG;
            case "transfer"      -> Consistency.STRONG;
            case "statement"     -> Consistency.EVENTUAL;
            case "notification"  -> Consistency.EVENTUAL;
            default -> Consistency.EVENTUAL;
        };
    }

    public static void main(String[] args) {
        System.out.println("Balance:      " + decide("balance-check"));
        System.out.println("Transfer:     " + decide("transfer"));
        System.out.println("Statement:    " + decide("statement"));
        System.out.println("Notification: " + decide("notification"));
    }
}
TEXT
Balance:      STRONG    -> read from primary DB, no stale cache
Transfer:     STRONG    -> ACID transaction, no eventual consistency
Statement:    EVENTUAL  -> cache + read replica acceptable
Notification: EVENTUAL  -> async queue, seconds of delay acceptable

| Requirement | Impact on Design |
|---|---|
| P99 < 100ms | Must cache, co-locate, use CDN |
| Strong consistency | Read from primary, limit caching TTL |
| Eventual consistency | Cache freely, use read replicas |
| High write volume | Shard, batch, async |

What are common mistakes in capacity planning?

The most common mistake is treating the headline user count as concurrent users. Another is sizing for the average instead of the peak, which causes outages on busy days.

JAVA
package com.bank.capacity;

public class CapacityMistakes {

    public static void main(String[] args) {
        // WRONG: treating registered as concurrent
        long wrongCapacity = 1_000_000; // "1M users"
        System.out.println("Wrong: designing for " +
            wrongCapacity + " concurrent");
        System.out.println("Result: massive over-provisioning");

        // WRONG: sizing for average, not peak
        double avgRps = 83;
        System.out.println("\nWrong: sizing for " + avgRps + " rps");
        System.out.println("Result: collapses on salary day");

        // CORRECT: derive the four drivers
        double peakRps = 417;
        System.out.println("\nCorrect: sizing for " + peakRps +
            " peak rps");
        System.out.println("With headroom: " + (peakRps * 1.5) +
            " rps capacity");
    }
}
TEXT
Wrong: designing for 1000000 concurrent
Result: massive over-provisioning

Wrong: sizing for 83.0 rps
Result: collapses on salary day

Correct: sizing for 417.0 peak rps
With headroom: 625.5 rps capacity

What are the four derived drivers?

The four drivers that actually determine the architecture are peak requests per second, read-to-write ratio, data size and growth, and latency and consistency requirements.

| Driver | Question | Banking Example |
|---|---|---|
| Peak RPS | How many requests at the busiest moment? | 417 rps on salary day |
| Read/Write ratio | What percentage are reads vs writes? | 97.8% reads |
| Data size | How much data, how fast is it growing? | 67 GB/year |
| Latency + Consistency | How fast and how correct? | P99 < 100ms, strong for money |

What should a senior engineer present to stakeholders?

A senior engineer converts the vague headline into a one-page capacity brief before any design work begins.

TEXT
Capacity Brief: National Bank of India Mobile App

  Registered users:       1,000,000
  Daily active users:     100,000 (10%)
  Peak concurrent:        5,000 (5% of DAU)
  Peak requests/sec:      417 (5x average)
  Read/write ratio:       97.8% reads
  Data growth:            67 GB/year transactions
  Latency target:         P99 < 100ms for balance checks
  Consistency:            Strong for balances and transfers
                          Eventual for statements and notifications

  Design implications:
  - Cache-heavy architecture (97.8% reads)
  - 3-5 stateless app instances behind LB
  - Redis cache with 30s TTL for balances
  - Single primary DB + 2 read replicas
  - Kafka for async statement/notification
  - No sharding needed for first 2 years

Visual Overview

DIAGRAM
  "1 Million Users"
       │
       ▼
  ┌──────────────────────────────────────┐
  │        Clarify the Number             │
  │                                       │
  │  Registered:    1,000,000 (irrelevant)│
  │  Daily Active:  100,000               │
  │  Peak Concurrent: 5,000               │
  └──────────┬───────────────────────────┘
             │
             ▼
  ┌──────────────────────────────────────┐
  │      Derive the Four Drivers          │
  │                                       │
  │  1. Peak RPS:        417              │
  │  2. Read/Write:      97.8% reads      │
  │  3. Data Growth:     67 GB/year       │
  │  4. Latency/Consist: P99<100ms/Strong │
  └──────────┬───────────────────────────┘
             │
             ▼
  ┌──────────────────────────────────────┐
  │         Design the Architecture       │
  │                                       │
  │  Cache-heavy + read replicas          │
  │  Stateless app + LB                   │
  │  Kafka for async                      │
  │  Single primary (no sharding yet)     │
  └──────────────────────────────────────┘

Key Takeaway

Before designing anything for scale, always clarify what a number like one million users actually means. Registered users, daily-active users, and concurrent users can differ by orders of magnitude. A million registered users might be only a hundred thousand daily-active and a few thousand concurrent at peak. Designing for the wrong one is the classic mistake that leads either to a wildly expensive over-build or to an outage on the first busy day.

The real work is converting that headline into four derived drivers: peak requests per second, derived from DAU and a peak-to-average multiplier; the read-to-write ratio, which shapes the entire data strategy; data size and growth rate, which determines partitioning needs; and latency and consistency requirements, which are often the decisive constraints. At National Bank of India, a product manager said "build for one million users," and the first instinct was to treat that as a million concurrent. Once we clarified it into about a hundred thousand DAU and a few thousand concurrent at peak, the right design was dramatically simpler.

These four drivers, not the user count, determine capacity, data architecture, and the consistency model. A 97.8% read-heavy workload with strong consistency on balances leads to a very different architecture than a write-heavy trading platform with eventual consistency.

Remember one sentence: one million users is the start of the conversation, not the answer: the real work is converting it into peak requests per second, read-to-write ratio, data size, and latency and consistency requirements.

---

Share
Use ← → arrow keys to navigate