Logo suyashsavant.com
Back to blog
Scaling: From 10 to 1 Million Users

Scaling: From 10 to 1 Million Users

S

Suyash Kelvin Savant

3/15/2024

A battle-tested guide to scaling both your technology and your engineering teams.

scalingsystem-designleadership

The Two Sides of Scaling

When people say “we need to scale,” they usually mean one of two things:

  • System scaling: your product needs to handle more load (requests per second, data volume, concurrency, reliability expectations).
  • Organizational scaling: your team needs to handle more work (more engineers, more features, more customers, more operational burden).

These two problems feel different day-to-day, but they often share a root cause: coupling.

  • A tightly-coupled system forces every change to ripple across the whole codebase.
  • A tightly-coupled organization forces every decision to route through a small set of people.

Scaling is the art of decoupling without losing coherence.

This guide is written for founders, engineering leads, and senior engineers who are approaching the “10 to 1 million users” phase (or the equivalent in B2B: more customers, more data, and higher expectations).

First Principle: Measure Before You Add Complexity

Scaling decisions are expensive. Before you add read replicas, caches, queues, or microservices, ask:

  • What’s the bottleneck today?
  • What metric shows it clearly?
  • What would “good” look like?

In practice, establish:

  • Service-level objectives (SLOs): e.g., “99% of requests under 200ms.”
  • Key metrics: latency (p50/p95/p99), error rate, throughput, saturation (CPU, memory, DB connections), queue depth.
  • A baseline: current traffic + headroom.

If you can’t measure it, you’ll end up scaling the wrong thing.

System Scaling: A Pragmatic Progression

There isn’t one universal path. Sometimes the cache breaks first. Sometimes a third-party API is your limiter. Often, the database becomes a pressure point because it concentrates state.

The goal is not to “reach sharding.” The goal is to keep the system reliable and changeable as demand grows.

Stage 0: Buy Time With Simple Wins

Before you distribute your system, squeeze the obvious inefficiencies. These changes are usually low-risk and high ROI:

  • Fix slow queries: add indexes, avoid N+1 queries, remove unnecessary joins.
  • Connection pooling: prevent your DB from melting due to connection churn.
  • Payload discipline: compress responses, paginate lists, avoid sending huge JSON blobs.
  • CDN + caching headers: offload static assets and cacheable responses.
  • Rate limits: protect the system from spikes and abuse.
  • Remove accidental work: avoid doing heavy computation on every request.

Many “scaling problems” are actually “we’re doing wasteful work at scale” problems.

Stage 1: Vertical Scaling and Isolation

When load increases, the simplest lever is vertical scaling:

  • bigger instances for app and DB
  • better storage IOPS
  • more memory for hot working sets

This isn’t “bad architecture.” It’s a rational move early on because it preserves simplicity.

At the same time, improve isolation:

  • separate background job workers from web servers
  • isolate read-heavy endpoints
  • set timeouts and concurrency limits

Isolation prevents one noisy area from taking down everything.

Stage 2: Read Replicas (When Reads Dominate)

Many products have a read-heavy pattern, but don’t assume an 80/20 split—measure it.

Read replicas can increase read throughput and reduce load on the primary database.

Key considerations:

  • Replication lag: replicas can be behind; don’t use them for read-after-write consistency unless you handle it.
  • Query routing: decide which queries can tolerate eventual consistency.
  • Schema changes: operational complexity increases; migrations need planning.

Read replicas are a great “first distribution” step because they keep write consistency simple.

Stage 3: Caching (Avoid Work, Not Just Latency)

Caching is not only about speed. It’s about reducing load on systems that are hard to scale (databases, third-party APIs).

Common caching layers:

  • Browser / edge caching (CDN): best ROI for public content.
  • Application caching (in-process): fastest but not shared across instances.
  • Distributed caching (Redis/Memcached): shared and flexible.

The Cache-Aside Pattern

Cache-aside is the most common pattern: your application checks cache first, falls back to DB on a miss, then populates the cache.

def get_user_profile(user_id):
    cache_key = f"user:{user_id}"

    cached = redis.get(cache_key)
    if cached:
        return deserialize(cached)

    user = db.query("SELECT * FROM users WHERE id = %s", user_id)

    # Choose TTL intentionally: long enough to reduce load, short enough to avoid stale pain.
    redis.set(cache_key, serialize(user), ex=3600)
    return user

Caching Pitfalls You Should Plan For

  • Stale data: decide what can be stale and for how long.
  • Cache stampedes: when many requests miss at once, they can overwhelm the DB.
  • Hot keys: one popular key can overload a single cache node.
  • Invalidation complexity: “just delete the cache on update” sounds simple until updates are distributed.

Mitigations:

  • add jitter to TTLs
  • use request coalescing or locks on recompute
  • cache at the right granularity (not too big, not too small)
  • design data models that minimize invalidations

Stage 4: Asynchronous Work and Queues

As you grow, you’ll discover that not everything belongs in the request/response path.

Common candidates for async processing:

  • sending emails/notifications
  • image/video processing
  • analytics events
  • generating reports
  • syncing with third-party systems

Queues help you:

  • absorb traffic spikes
  • control concurrency
  • retry failures safely

When you introduce queues, treat idempotency as a first-class requirement. Messages will be duplicated. Workers will crash mid-task. If retries can double-charge a customer or send 10 emails, you’ll learn the hard way.

Stage 5: Partitioning and Sharding (When a Single Writer Can’t Keep Up)

Sharding is not a milestone; it’s a last resort when:

  • write throughput exceeds the capacity of a single primary
  • the dataset is too large for a single machine’s storage/IO profile
  • operational requirements (tenant isolation, region constraints) demand separation

Common partitioning strategies:

  • By tenant: best for B2B SaaS (strong isolation).
  • By user ID: common in consumer products (even distribution).
  • By geography: when latency/regulation requires it.

Sharding introduces serious complexity:

  • cross-shard joins become difficult or impossible
  • transactions across shards are expensive
  • rebalancing shards later is operationally challenging

If you anticipate sharding, invest early in:

  • clean data ownership boundaries
  • a routing layer (logical partition key)
  • avoiding “global joins” in core workflows

Stage 6: Service Decomposition (Don’t Jump to Microservices Too Early)

Microservices can help, but they also add distributed systems costs: consistency, observability, deployments, and coordination.

A common progression that works well:

  • start with a modular monolith (one deploy, clear internal modules)
  • extract services when you have a clear boundary and a reason:
    • independent scaling needs
    • independent deploy cadence
    • different reliability requirements

If you can’t describe the boundary and the ownership clearly, microservices will slow you down.

Reliability Is Part of Scaling

At small scale, you can get away with heroics. At large scale, reliability must be designed.

Foundational practices:

  • Timeouts: no unbounded waits.
  • Retries with backoff: retry only safe operations, cap retry budgets.
  • Circuit breakers: stop hammering dependencies that are failing.
  • Bulkheads: prevent one dependency from consuming all resources.
  • Graceful degradation: serve a reduced experience rather than total failure.
  • Load shedding: protect the core when overwhelmed.

Scaling without reliability is just making failures happen faster.

Observability: You Can’t Operate What You Can’t See

As traffic grows, bugs become rarer but more expensive. You need fast diagnosis.

Minimum viable observability:

  • structured logs (with request IDs)
  • metrics dashboards (RED/USE is a good starting point)
  • distributed tracing for critical flows
  • alerting tied to user impact (SLO-based alerts beat noisy CPU alerts)

Operational maturity also includes:

  • an on-call rotation
  • runbooks for common incidents
  • blameless postmortems that produce concrete follow-ups

Team Scaling: The Communication Bottleneck

As your team grows, communication paths grow roughly quadratically: $N(N-1)/2$. That doesn’t mean growth is doomed; it means you need structure.

The goal is to avoid a world where:

  • every decision goes through one “architect”
  • engineers depend on tribal knowledge
  • teams block each other constantly

Stage 1: Make Ownership Explicit

Before you create squads and platform teams, make ownership visible:

  • who owns which services/modules
  • who owns on-call for which systems
  • who owns documentation and runbooks

Ownership isn’t bureaucracy; it’s clarity.

Stage 2: Split Into Squads (Small Teams With a Mission)

At ~8–10 engineers, the “everyone works on everything” model starts to fail.

Splitting into squads/pods (typically 4–8 people) works when squads are:

  • cross-functional: backend, frontend, design, product collaboration
  • mission-driven: aligned to outcomes (“Activation”, “Growth”, “Payments”, “Platform”)
  • empowered: able to ship end-to-end changes without constant approvals

Autonomy doesn’t mean chaos. You still need shared standards (security, reliability, data governance).

Stage 3: Create Clear Interfaces (Contracts, Not Conversations)

As teams multiply, informal coordination collapses. Replace “tap on the shoulder” with defined interfaces:

  • API contracts (OpenAPI/GraphQL schemas)
  • event contracts (schemas and versioning)
  • SLAs/SLOs for internal services
  • backward-compatibility rules

This is Conway’s Law in action: your architecture will mirror your communication. Design your org so the mirror looks good.

Stage 4: Invest in a Platform (When Product Teams Start Rebuilding the Same Things)

When product squads repeatedly solve the same problems—CI, observability, deployments, auth integration—you need a platform approach.

A good platform team is an enabler, not a gate:

  • provides paved roads (templates, libraries, tooling)
  • reduces cognitive load
  • sets reliability and security baselines

If platform becomes a bottleneck, you’ve recreated the monolith—just in people.

Stage 5: Processes That Scale Without Killing Speed

Process should reduce risk and rework, not increase meetings.

Practices that scale well:

  • Lightweight RFCs: for changes that affect multiple teams.
  • Architecture decision records (ADRs): document why decisions were made.
  • Strong CI/CD: automated tests, linting, security checks.
  • Release discipline: feature flags, gradual rollouts, fast rollback.
  • Incident reviews: treat outages as system signals, not personal failures.

The meta-rule: write things down when the cost of repeating the conversation exceeds the cost of documentation.

Hiring and Onboarding: Scaling the Rate of Learning

The fastest teams aren’t the ones with the most people. They’re the ones with the fastest onboarding and the least hidden knowledge.

To improve onboarding:

  • create a “first week” checklist
  • maintain a living architecture overview
  • pair new hires on real tasks quickly
  • keep developer environments reproducible

If it takes 3 months for someone to contribute, your system (technical and organizational) is too opaque.

Closing Thought

Scaling is painful because it forces you to confront constraints: performance constraints, reliability constraints, and human constraints.

If you approach scaling as decoupling—clear boundaries in code, clear ownership in teams, and clear contracts between both—you can grow without turning your product into a brittle maze.

Scaling is a good problem to have. It means you’ve found demand. The job now is to build the systems and the organization that can keep up.