← All essays

Adding More Servers Won't Save You: Understanding Horizontal Scaling, Load Balancing & Bottlenecks

Adding servers raises capacity at one layer — the bottleneck just migrates. A first-principles walk through horizontal scaling, load balancing, stateless design, shared dependencies, and finding the component that actually limits throughput.

When an application starts getting more traffic, one of the first solutions engineers think about is — and this is exactly the kind of reasoning we document on engineering at Intervues:

"Let's add more servers."

It sounds logical.

If one server handles 1,000 requests per second, then surely:

1 server  → 1,000 RPS
2 servers → 2,000 RPS
10 servers → 10,000 RPS

Right?

Not necessarily.

Adding more application servers can increase capacity—but only if the rest of your system can keep up.

Your application server might be capable of handling 10,000 RPS while your database can only handle 3,000.

Your cache might become saturated.

An external API might enforce a rate limit.

A shared connection pool might run out.

And suddenly, adding more servers doesn't solve your scalability problem.

It simply moves the bottleneck somewhere else.

This is one of the most important concepts in system design and backend architecture:

You don't scale a system by adding servers. You scale the bottleneck.

Let's understand why.


1. What Is Horizontal Scaling?

Horizontal scaling, also called scale-out, means increasing system capacity by adding more instances instead of making a single machine more powerful.

Instead of:

you add multiple servers:

The idea is straightforward:

Distribute workload across multiple machines.

Horizontal scaling is one of the fundamental techniques used to build scalable web applications, APIs, backend services, and distributed systems.

But there is a major requirement.

The workload must actually be distributable.

And that's where load balancing and stateless application design become important.


2. Load Balancing

If you have multiple application servers, something needs to decide:

Which server should handle this request?

That's the job of a load balancer.

A simplified architecture looks like:

The load balancer receives incoming traffic and distributes requests across the available application servers.

Common load-balancing strategies include:

  • Round robin
  • Weighted round robin
  • Least connections
  • Least response time
  • IP hashing
  • Consistent hashing

The exact algorithm matters less than the fundamental idea:

Incoming requests should be distributed across available capacity.


3. Request Distribution

Imagine your application receives:

3,000 RPS

and you have three identical servers.

A simple round-robin strategy might distribute traffic like:

That's great if:

  • Servers are identical
  • Requests cost roughly the same
  • All servers are healthy
  • Dependencies can handle the combined workload

But real systems aren't always this simple.

Imagine some requests are cheap:

GET /health
GET /profile

while others are expensive:

POST /generate-report
POST /process-video
POST /checkout

Sending the same number of requests to each server doesn't necessarily mean sending the same amount of work.

This is why load balancing isn't simply:

"Split requests 50/50."

The goal is:

Distribute workload so that available capacity is used effectively.


4. Stateless Application Servers

Horizontal scaling becomes much easier when application servers are stateless.

What does stateless mean?

It means:

A server does not depend on locally stored request state to correctly handle the next request.

Consider a simple API:

If the application is stateless, any server can process any request.

That's extremely useful.

But imagine Server A stores important session information in its local memory:

Then the load balancer sends the next request to Server B.

Server B doesn't know about that session.

You now have a problem.


5. Why Statelessness Enables Scaling

Consider a stateless architecture:

The servers themselves don't own important persistent state.

Instead, state lives in shared systems such as:

  • Database
  • Redis
  • Object storage
  • Distributed cache
  • Message queues

Now any server can handle any request.

If traffic increases:

3 servers → 6 servers

the new servers can immediately start processing requests.

This is one of the biggest reasons stateless architecture is so valuable for scalable web applications.


6. Stateless Does NOT Mean No State

This distinction is important.

A stateless application doesn't mean:

"The system has no state."

Obviously, most applications have state.

Users have:

  • Accounts
  • Orders
  • Preferences
  • Sessions
  • Payments
  • Messages
  • Files

The point is:

Application instances shouldn't be the authoritative owners of state that must survive between requests.

Instead:

Now application instances can be created and destroyed without losing important state.

That makes:

  • Autoscaling
  • Load balancing
  • Failover
  • Rolling deployments
  • Container orchestration

much easier.


7. The Hidden Problem: Shared Dependencies

Now we reach the real problem with:

"Just add more servers."

Consider:

You successfully scaled the application layer.

But all three servers still depend on the same database.

That's called a:

Shared dependency.

A shared dependency is a component used by multiple instances or services.

Common shared dependencies include:

  • Databases
  • Caches
  • Message brokers
  • Object storage
  • External APIs
  • Authentication services
  • Payment gateways
  • Search systems

And they can become your next bottleneck.


8. The Database Is Often the Shared Bottleneck

Let's say one application server handles:

1,000 RPS

You add five servers:

5 × 1,000
= 5,000 RPS

Sounds great.

But suppose every request generates:

5 database queries

Then:

5,000 API RPS
× 5 DB queries
= 25,000 DB queries/sec

Your application layer might be perfectly capable of handling 5,000 RPS.

But your database might not be.

The architecture is now:

Adding more application servers now makes the situation worse.

Why?

Because more servers generate even more database traffic.


9. This Is Called Bottleneck Migration

One of the most useful concepts in scalability engineering is:

Bottleneck migration.

Suppose initially:

Application CPU = 95%
Database CPU    = 40%

The application is the bottleneck.

You add more application servers.

Now:

Application CPU = 50%
Database CPU    = 95%

Congratulations.

You solved the application bottleneck.

But you created a database bottleneck.

The bottleneck migrated.

This is normal in distributed systems.

Scaling one layer can expose the next limiting component.

You can think of a system like a chain:

The system's overall capacity is constrained by its limiting components.

Improve one constraint and another may become visible.


10. Scaling the Wrong Layer

This is where many system design discussions go wrong.

Imagine your architecture is:

Someone says:

"We need more application servers."

That's probably the wrong layer to scale.

Your application layer isn't the limiting factor.

The database is.

Adding another 100 application servers would only produce:

You have spent more money without increasing meaningful system capacity.

This is why a good system designer asks:

Which component is actually limiting throughput?

before deciding:

What should we scale?


11. Cache Is Also a Shared Dependency

Caching is one of the most common techniques for improving application performance.

For example:

Instead of querying the database every time:

This can dramatically reduce database load.

But now Redis becomes a shared dependency.

Imagine:

If all ten servers depend on the same Redis cluster, Redis itself must scale with the workload.

You might eventually see:

Application CPU → 30%
Database CPU    → 30%
Redis CPU       → 95%

Your bottleneck has moved again.

Caching doesn't eliminate scalability problems.

It changes where the work happens.


12. External Services Can Become Bottlenecks

Your infrastructure isn't the only thing that needs to scale.

Imagine your application calls:

Your application can handle:

100,000 RPS

But the external service allows:

10,000 requests/minute

Your application's theoretical capacity doesn't matter.

The external service is now the constraint.

Other examples include:

  • Payment gateways
  • Email providers
  • SMS APIs
  • Maps APIs
  • AI APIs
  • Authentication providers
  • Shipping APIs
  • Third-party analytics
  • Social APIs

External dependencies introduce constraints that you may not control.

These can include:

  • Rate limits
  • Latency
  • Availability
  • Quotas
  • Connection limits
  • Pricing

So when performing system design capacity planning, external services must be included in the analysis.


13. A Realistic Example

Let's design a simple e-commerce backend.

The architecture:

Initially:

Traffic = 3,000 RPS

Application servers:

CPU = 80%

Database:

CPU = 50%

Redis:

CPU = 40%

The application layer is the limiting component.

So we scale horizontally:

3 servers → 6 servers

Now:

Application CPU = 45%

Great.

But database load increases because there are more requests being processed.

Now:

Database CPU = 95%

The database becomes the bottleneck.

We scale or optimize the database.

Perhaps we introduce:

  • Better indexes
  • Query optimization
  • Read replicas
  • Connection-pool tuning
  • Caching
  • Partitioning
  • Database sharding

Now database utilization falls.

But suddenly:

Redis = 95%

Another bottleneck appears.

That's bottleneck migration.


14. The Bottleneck Chain

A useful way to think about this is:

Every layer has a maximum sustainable capacity.

For example:

Application → 20,000 RPS
Redis       → 50,000 RPS
Database    → 8,000 RPS
Payment API → 5,000 RPS

The overall system can't magically process:

20,000 RPS

just because the application servers can.

The effective capacity is constrained by the dependency chain.

In this example, the payment service might be the ultimate constraint for payment operations.


15. Why Stateless Architecture Is So Powerful

Let's return to stateless application servers.

Suppose you have:

Any request can go to any server.

Now autoscaling becomes straightforward.

Traffic increases:

5,000 RPS

You add servers:

3 → 6

Traffic decreases:

1,000 RPS

You remove servers:

6 → 2

Because application instances don't contain irreplaceable state, instances can be created and destroyed freely.

This is one of the foundations of modern cloud-native architecture.


16. What Happens If Your Application Is Stateful?

Consider a stateful server:

Now the next request goes to Server B:

One workaround is sticky sessions.

The load balancer tries to keep the user connected to the same server.

For example:

This can work.

But it creates complications.

What happens when:

Server A crashes?

The user's state may disappear.

What happens when:

Server A becomes overloaded?

The load balancer may have difficulty distributing the user's workload elsewhere.

What happens when:

You need to deploy Server A?

You have to carefully migrate active sessions.

Stateful application servers therefore make horizontal scaling more complicated.


17. Moving State Out of the Application

A common architecture is:

Now application instances can remain disposable.

The server doesn't need to remember:

"This user must come back to me."

Instead:

"Give me the state I need from shared infrastructure."

This architectural pattern is extremely common in scalable backend systems.


18. But Shared State Has a Cost

Moving state to shared infrastructure solves one problem.

It introduces another.

Your architecture now depends heavily on:

  • Redis
  • Database
  • Object Storage
  • Message Broker

Those systems must themselves be:

  • Highly available
  • Scalable
  • Monitored
  • Capacity planned
  • Fault tolerant

This leads to an important principle:

Removing state from application servers doesn't remove state from the system. It relocates it.

And wherever you relocate it, you must account for its capacity and failure modes.


19. The Difference Between Scaling and Removing Work

There's another important technique that often gets overlooked.

Instead of asking:

"How do I process more work?"

ask:

"Can I avoid doing this work at all?"

For example:

Without caching:

With caching:

You didn't necessarily make the database faster.

You reduced the work it has to perform.

This is often more effective than simply adding infrastructure.


20. Finding the Actual Limiting Component

When a system doesn't scale, don't immediately add servers.

Measure.

Look at:

Application layer

  • CPU utilization
  • Memory utilization
  • Thread pools
  • Worker pools
  • Request queues
  • Connection pools
  • Response latency

Database

  • CPU
  • Memory
  • Connections
  • Query latency
  • Locks
  • IOPS
  • Replication lag
  • Slow queries

Cache

  • CPU
  • Memory
  • Hit rate
  • Evictions
  • Network throughput
  • Command latency

Network

  • Bandwidth
  • Packet rate
  • Connection count
  • Network latency

External services

  • Rate limits
  • API latency
  • Error rate
  • Quotas
  • Connection limits

Then ask:

Which resource approaches saturation first as traffic increases?

That is usually the most important question.


21. Don't Confuse High Utilization With the Bottleneck

Suppose you see:

Application CPU = 90%
Database CPU    = 40%

It might seem obvious that the application is the bottleneck.

But what if the application is waiting on database connections?

Maybe:

DB connections = 100%
CPU            = 40%

The database isn't CPU-bound.

The connection pool is the constraint.

Similarly:

CPU     = 40%
Memory  = 40%
Network = 99%

The application isn't CPU constrained.

It's network constrained.

Therefore:

Always identify the resource that is actually limiting throughput—not simply the resource with the highest percentage.


22. A Better Scaling Process

Instead of:

use this process:

This is a much more reliable approach to scaling distributed systems.


23. Horizontal Scaling Doesn't Mean Infinite Scaling

Horizontal scaling is powerful.

But it isn't magic.

Imagine:

1 App Server  → 1,000 RPS
2 App Servers → 2,000 RPS
4 App Servers → 4,000 RPS
8 App Servers → 7,000 RPS
16 App Servers → 8,000 RPS

Why does growth flatten?

Because something else becomes limiting.

Maybe:

  • Database
  • Network
  • Cache
  • External API
  • Connection pool

This is why scalable system design requires understanding the entire request path.


24. The Most Important System Design Question

When someone asks:

"How would you scale this application?"

Don't immediately answer:

"Add a load balancer and horizontally scale the servers."

That's only part of the answer.

Instead, walk through:

Step 1 — Understand the workload

How much traffic?

  • RPS
  • Concurrent requests
  • Data volume
  • Peak traffic

Step 2 — Understand the request path

Step 3 — Identify dependencies

What does each request touch?

Step 4 — Find the limiting component

What saturates first?

Step 5 — Scale the constraint

Only then decide whether you need:

  • More application servers
  • Better load balancing
  • Caching
  • Database replicas
  • Database partitioning
  • Queue-based processing
  • Rate limiting
  • Connection pooling
  • Service decomposition

That's actual scalability engineering.


25. The Mental Model

Remember this:

When you add application servers, you're increasing capacity at one layer.

The rest of the architecture still has to handle the additional work.

So the real question isn't:

"How many servers can we add?"

It's:

"Which component limits the system's ability to process more work?"


26. Final Takeaway

Horizontal scaling is one of the most important techniques in scalable system design.

Load balancing allows incoming requests to be distributed across multiple application servers.

Stateless application servers make this distribution much easier because any instance can handle any request.

But the application layer is only one part of the system.

Your servers still depend on:

  • Databases
  • Caches
  • Message queues
  • Storage
  • Networks
  • External APIs

These shared dependencies can become bottlenecks.

And when you scale one layer, the bottleneck may simply migrate somewhere else.

That's why:

Adding more servers won't save you if the actual bottleneck is somewhere else.

Good system design isn't about adding infrastructure everywhere.

It's about identifying the limiting component, understanding why it is limiting the system, and increasing capacity at the right layer.

The best scalability engineers don't ask:

"How can I add more servers?"

They ask:

"What's stopping me from processing more work?"

That's the mindset you need to design systems that actually scale.


Practice This Before Your Next System Design Interview

Knowing the concepts is one thing.

Being able to explain them clearly under interview pressure is another.

That's exactly why I built Intervues — a voice interview engine focused on practicing technical interviews by actually speaking through your answers. See pricing for credit packs.

No AI copilot feeding you answers.

Just you, the question, and the ability to explain your reasoning.

Practice the interview before the interview.

Visit https://intervues.club and start practicing.

Hey — it's Ayoush.

Nine years as an engineer, three interviewing candidates. I built Intervues because the gap isn't knowledge — it's saying what you know out loud. Real email at the other end: admin@intervues.club.

Keep reading

Rehearse the room. Then walk in ready.

An interview is the first day of the job — practice before it counts.

3 free credits · pay per interview · nothing recurring