Scaling Systems the Right Way: From One User to a Million

A practical walkthrough of scaling a system step-by-step, only adding complexity when real problems demand it. Learn how to evolve architecture without over-engineering.

4 min read

Scaling Systems the Right Way: From One User to a Million cover

Most engineers dream of designing systems that handle millions of users. The reality is, you don’t start there. You begin with one user, a single server, and a database. The real skill isn’t drawing a complex architecture upfront. It’s knowing when to evolve it, and why.

Start small, stay simple

When you launch a new service, the first version should be boring. A user hits your app, it talks to a database, and that’s it. No Redis, no Kafka, no Kubernetes. Adding those early doesn’t solve problems. It creates them. Debugging a distributed system is harder than debugging a single process. Deploying it is slower. Monitoring it is more complex. If you don’t need that complexity yet, don’t invite it in.

The cheapest scaling problem is the one you never have to solve. Keep the system simple until it forces you to change.

Optimize before you scale

At 100 users, you don’t need microservices or event queues. You need to make the existing system efficient. Bad queries, missing indexes, and N+1 problems don’t disappear when you add more servers. They just get worse. Fixing a slow API by optimizing a query is often faster and cheaper than scaling out. Before you distribute anything, make sure the single-node version is as fast as it can be.

  • Add the right database indexes to speed up queries.

  • Eliminate N+1 queries by batching or using joins.

  • Use connection pooling to avoid exhausting database connections.

  • Compress responses to reduce bandwidth usage.

  • Cache static assets so they don’t hit your app servers repeatedly.

  • Instrument your code to measure latency before guessing what’s slow.

Scale up before you scale out

When traffic grows to 10,000 users, your first move isn’t always horizontal scaling. A single machine can handle more load than you think. Vertical scaling, giving your server more CPU and RAM, is simpler. The architecture doesn’t change. There’s no new failure domain. No load balancer to configure. No state to synchronize. It’s just a bigger box.

But machines have limits. Eventually, you’ll hit a ceiling. That’s when the second server becomes necessary, and everything changes.

The second server introduces new problems

Adding a second app server means requests can land on either instance. If your app stores session data in memory, this breaks. A user logs in on server one, but their next request goes to server two. Their session isn’t there. The load balancer did its job. Your architecture didn’t.

This is where statelessness becomes valuable. Instead of keeping state locally, you move it to a shared store like Redis. The change isn’t about following trends. It’s about solving a real problem that appeared when you added the second server.

Caching isn’t free

At 100,000 users, repeated reads start to hurt. Fetching the same data over and over from the database is wasteful. Caching helps, but it’s not a magic fix. A cache hit saves a database round trip. A cache miss adds overhead. You have to think about TTLs, invalidation, and stale data. Some data can tolerate staleness. Other data, like booking slots, can’t. Caching isn’t just a performance decision. It’s a correctness decision too.

The bottleneck moves

At 250,000 users, your API might be fine, but the database is struggling. CPU is high, connections are maxed out, and queries are slow. Adding more API servers won’t help. The bottleneck isn’t the app tier. It’s the database. Before you shard or add replicas, look at the queries. Are they using indexes? Are they fetching too much data? Can you cache some of the reads?

  • Identify slow queries and optimize them.

  • Check for missing indexes.

  • Reduce N+1 queries or unnecessary data fetching.

  • Cache data that doesn’t change often.

  • Review connection usage to avoid leaks.

  • Only then consider scaling the database infrastructure.

Reads and writes don’t scale the same way

If your workload is read-heavy, you can offload traffic to read replicas. Writes still go to the primary, but reads can be distributed. This helps, but it introduces replication lag. A user creates a booking, then refreshes the page. If the replica hasn’t caught up, they see stale data. Scaling reads isn’t just about performance. It’s about consistency too.

Not everything needs to be synchronous

At 750,000 users, your API might be doing too much. A booking request could trigger an email, a push notification, an analytics update, and a loyalty points award. The user doesn’t need to wait for all of that. The core path, validate, reserve, persist, confirm, should be fast. The rest can happen asynchronously. An event broker lets you decouple these steps, but it introduces new challenges. What if the event fails to publish? What if the consumer crashes? What about ordering?

Asynchronous processing improves responsiveness, but it complicates consistency. You have to think about retries, dead-letter queues, and eventual consistency. The system scales, but the trade-offs become more visible.

Scale the bottleneck, not the user count

You don’t scale because you hit 100,000 users. You scale because CPU is high, memory is exhausted, or latency is spiking. User count is just context. The real signal is resource pressure. Two systems with the same number of users can have completely different architectures. One might need a CDN early. Another might prioritize consistency over raw throughput. Scale is workload-specific.

The key is to evolve the system only when a real problem forces you to. Every component should earn its place. If it’s not solving a measurable issue, it’s just adding complexity.

Building something with AI? Let's talk.

I design and ship production AI and full-stack products for US teams. See how I can help.

View all services

Join the newsletter

Be the first to read our articles.

Scaling Systems the Right Way: From One User to a Million | Muhammad Adil