Intermediate System design concept · Handling Scale & Bottlenecks · 22 mins read

Scaling Systems

Choosing between a bigger machine and more machines is the first scaling fork.

  1. Thinking in Systems
  2. How Systems Actually Work
  3. Handling Scale & BottlenecksYou are here
  4. Distributed Systems & Data
  5. Real Systems & Production Thinking

Horizontal Scaling

Add more identical application instances to handle more traffic without relying on one ever-bigger machine.

Intuition

A single server can only grow so far before CPU, memory, network, or maintenance windows become the bottleneck. When traffic spikes, one oversized box also concentrates failure risk in one place.

Horizontal scaling is the default way modern web tiers survive growth, bursts, and failures. Interviewers care because it forces you to explain statelessness, load balancing, shared state, and why scaling compute is easier than scaling a primary database.

Mental Model

Vertical scaling means making one machine bigger. Horizontal scaling means making many machines interchangeable so a load balancer can spread requests across them. That only works cleanly when app instances do not depend on local in-memory session or file state; otherwise adding servers just creates inconsistency. Think of it like: A restaurant can either buy one giant oven or add more cooking stations. More stations help only if any chef can prepare any order from shared ingredients instead of each station hiding its own private pantry.

Building Blocks

  • Interchangeable app instances behind one stable endpoint. Any instance should be able to serve the same request shape, so scaling adds capacity instead of special cases.
  • A load balancer with health checks. It spreads traffic across the fleet and removes bad instances before they keep hurting users.
  • Externalized state such as Redis sessions and object storage. Once state stops living on one box, users can move between instances safely.
  • Autoscaling tied to demand signals like request rate, queue depth, or latency. It matters only if new instances can start fast enough to help during a spike.

Definitions

Horizontal Scaling
Adding more servers and sharing traffic across them so total capacity grows with instance count.
  • It improves both throughput and fault tolerance at the app tier.
  • It works best when servers are interchangeable.
Stateless Service
A service whose next request can be handled by any healthy instance because important state is stored elsewhere.
  • Local memory or disk should not be required for user continuity.
  • Statelessness is the main enabler for clean scale-out.
Load Balancer
The entry point that forwards requests to one backend from a pool of healthy backends.
  • It hides server churn from clients behind one stable address.
  • It is what makes many instances feel like one service.
Autoscaling
Automatically changing the number of instances based on measured demand or pressure.
  • It absorbs normal traffic variation without manual intervention.
  • It does not fix slow code or a saturated database.

Patterns

  • Scale the stateless web or API tier first; it is usually the easiest place to add more capacity safely.
  • Move sessions, uploads, and long-running work out of the instance before adding more servers.
  • Keep one stable front door so instances can be added or removed without changing clients.

Strategies

  • Externalize the blockers to scale-out first: sessions to Redis, files to object storage, and background work to queues.
  • Scale on demand signals that map to user pain, such as sustained latency or queue depth, not CPU alone.
  • Check downstream limits before celebrating a larger fleet, because the bottleneck often shifts to the database or another dependency.

The real prerequisite for horizontal scaling

Adding servers is easy; making them interchangeable is the hard part. If login state, uploads, or workflow progress live only on one instance, the load balancer cannot freely move requests and the extra servers add inconsistency instead of resilience.

That is why horizontal scaling is really an application-design choice, not just an infrastructure choice. Once the service is stateless at the request path, a load balancer can spread traffic, health checks can remove bad nodes, and autoscaling can add capacity without breaking user flows.

The common failure mode is scaling the app tier while leaving a shared dependency unchanged. If every new server doubles traffic to the same primary database, the architecture did scale, but only one layer did.

Tradeoffs

  • Compared with vertical scaling, scale-out gives elasticity and redundancy, but it adds more moving parts and network hops.
  • Externalized state enables interchangeable servers, but it introduces new dependencies whose latency and availability now matter.
  • Autoscaling saves cost during quiet periods, but poor thresholds can scale too late or flap during noisy traffic.

Real World

  • Netflix keeps web and API tiers horizontally scalable so instance failures and traffic bursts are absorbed by fleets instead of one oversized server.
  • Shopify adds application capacity during flash-sale events while keeping shared state outside the app node so any healthy instance can serve the next request.

Interview

Questions interviewers ask

  • Why does horizontal scaling usually require stateless application servers?
  • What breaks if session data stays only in local memory?
  • Why do horizontally scaled services still need a load balancer?
  • How do you know whether the app tier or a downstream dependency is the real bottleneck?

What a strong answer covers

Candidates should contrast scale-up with scale-out, explain statelessness as the key prerequisite, mention shared state stores, and note that new app servers often move the bottleneck downstream rather than removing it.

Common traps

  • Saying "just add servers" without explaining where sessions, files, or other sticky state live.
  • Treating autoscaling as a cure for slow queries or overloaded downstream systems.
  • Forgetting that one stable entry point and health-aware routing are part of the pattern.

Quiz

What is the main architectural precondition for scaling an application tier horizontally?
  1. Using a larger VM type
  2. Keeping the service stateless or moving state outside the instance
  3. Running on bare metal only
  4. Using SQL instead of NoSQL

If requests depend on local instance memory or disk, adding more servers creates inconsistent behavior. Horizontal scaling works cleanly when any instance can handle any request.

Why is a load balancer typically placed in front of horizontally scaled app servers?
  1. To distribute traffic across healthy instances behind one stable endpoint
  2. To make every request strongly consistent
  3. To replace the database primary
  4. To eliminate all network latency

The load balancer hides backend churn from clients and spreads requests across the server pool. Without it, clients would need to track instance addresses and failures themselves.

Which statement best describes vertical scaling?
  1. Adding more instances behind a dispatcher
  2. Splitting the database into shards
  3. Moving sessions into Redis
  4. Making one machine bigger by adding more CPU, RAM, or bandwidth

Vertical scaling upgrades one node instead of increasing node count. It is simple, but it has cost and hardware ceilings.

An app stores login sessions only in process memory. What is the biggest risk after adding more app instances?
  1. TLS will stop working
  2. The load balancer cannot perform health checks
  3. Users may appear logged out when their next request lands on a different server
  4. CPU usage will always decrease

Local-only session state ties a user to one machine. Once requests spread across multiple servers, follow-up requests may miss the in-memory session entirely.

What is the best way to think about autoscaling?
  1. A replacement for performance tuning
  2. A way to reduce the need for monitoring
  3. A mechanism to adjust instance count to changing load
  4. A guarantee that the database will never bottleneck

Autoscaling adds or removes capacity as demand changes. It helps with elastic load, but it does not fix inefficient code or slow downstream systems.

Load Balancer vs Reverse Proxy

Learn which edge responsibilities belong to a reverse proxy, which belong to load balancing, and why one product can play both roles.

Intuition

Teams often call every front-door component a “load balancer,” even when it is also terminating TLS, compressing responses, caching assets, or routing by URL path. That vocabulary blur makes it hard to reason about what the component is actually doing and where bottlenecks or failures will show up.

In interviews and real systems, you need to separate “who receives traffic and shapes HTTP behavior” from “who distributes requests across a backend pool.” The same software can do both jobs, but the design tradeoff changes when you deploy them as one tier versus separate tiers.

Mental Model

A reverse proxy stands in front of servers and speaks to clients on their behalf. It can terminate TLS, normalize headers, route requests, compress responses, and cache content. A load balancer has a narrower core responsibility: select one backend from many backends for each connection or request. Products like NGINX, Envoy, and HAProxy can act as both because distributing traffic is one kind of proxy behavior. Think of it like: Think of a hotel front desk. It can greet guests, check identity, answer questions, and hand out room keys—that is the reverse-proxy role. Deciding which available room a guest should get is the load-balancing role. One person can do both in a small hotel; large hotels split the jobs.

Building Blocks

  • A reverse proxy as the public-facing hop. It can terminate TLS, normalize headers, and apply HTTP policies before traffic reaches services.
  • A load-balancing function that chooses one healthy backend from a pool. That choice is about distribution and failover, not broader HTTP behavior.
  • HTTP-aware features such as routing, caching, and compression. These belong to the proxy role even when the same product also balances.
  • Clear tier boundaries when edge processing and backend fan-out need different ownership or scaling profiles.

Definitions

Reverse Proxy
A server that accepts client requests on behalf of internal services and can shape HTTP behavior before forwarding.
  • Common jobs include TLS termination, routing, header changes, compression, and caching.
  • It gives clients one controlled public entry point.
Load Balancer
A component whose core job is to distribute traffic across multiple backends.
  • It combines backend selection with health awareness.
  • It may exist inside a reverse proxy or as its own tier.
TLS Termination
Decrypting HTTPS at the edge so certificate handling and handshake cost are centralized.
  • It simplifies cert rotation and policy enforcement.
  • It also enables HTTP-aware routing and caching decisions.
Combined Tier
A deployment where one product handles both reverse-proxy features and load balancing in the same hop.
  • This is common with NGINX, Envoy, and HAProxy.
  • It is simple, but it concentrates more work in one place.

Patterns

  • Use one combined proxy-plus-balancer tier when traffic is moderate and one layer can comfortably handle edge processing and backend distribution.
  • Split the roles when TLS, caching, WAF policy, or routing logic scales differently from backend fan-out.
  • Route different URL spaces to different backend pools through the reverse-proxy role, then balance within each pool.

Strategies

  • Start combined for simplicity, then separate only when you have a concrete scaling, security, or ownership reason.
  • Keep client-facing HTTP concerns conceptually distinct from backend-selection logic, even if one product does both jobs.
  • Watch proxy CPU and memory closely: compression, cache misses, and TLS can make the edge tier the first bottleneck.

Why the terms overlap but are not identical

The same software can be both because load balancing is one specialized thing a reverse proxy can do. Once a proxy is already accepting traffic on behalf of backends, it is naturally in position to choose among multiple upstreams.

The useful distinction is about responsibility. Reverse proxy emphasizes client-facing HTTP behavior such as TLS termination, routing, compression, or caching. Load balancer emphasizes backend selection, capacity sharing, and failover.

In small systems, one tier often does both and that is fine. In larger systems, separating the roles can keep the edge tier from becoming an overloaded catch-all for every request-path concern.

Tradeoffs

  • A combined tier reduces hops and operational overhead, but it also concentrates more CPU work and blast radius in one layer.
  • Separate tiers create cleaner ownership and scaling boundaries, but they add cost, latency, and more failure points.
  • Centralized TLS and caching can reduce backend load, but both consume resources on the proxy tier itself.

Real World

  • Cloudflare acts as a reverse proxy at the edge by terminating TLS, caching assets, and applying HTTP protections before origin systems see the traffic.
  • NGINX is often deployed as a combined reverse proxy and load balancer, using one layer to route requests and spread them across app servers.

Interview

Questions interviewers ask

  • What is the difference between a reverse proxy and a load balancer?
  • Why can NGINX or Envoy accurately be described as both?
  • When is one combined tier enough, and when would you split the roles?
  • Why is TLS termination usually discussed as a reverse-proxy responsibility?

What a strong answer covers

Candidates should define both roles clearly, explain that one product can play both, and justify whether the design needs a combined tier or separate layers based on traffic shape and operational boundaries.

Common traps

  • Using the two terms as perfect synonyms without separating HTTP-edge behavior from backend selection.
  • Assuming dedicated hardware is required for load balancing rather than thinking in terms of role.
  • Ignoring the fact that compression, caching, and TLS can make the proxy tier the bottleneck.

Quiz

Which responsibility is most closely associated with a reverse proxy rather than load balancing specifically?
  1. Distributing requests across three app servers
  2. Removing unhealthy instances from a backend pool
  3. Terminating TLS and applying HTTP compression
  4. Selecting a backend based on pool membership

TLS termination and compression are classic reverse-proxy jobs. Load balancing is specifically about choosing among backend servers.

Why can software like NGINX or Envoy be described as both a reverse proxy and a load balancer?
  1. Because one proxy layer can both process HTTP features and choose among multiple upstream backends
  2. Because every client device includes a built-in load balancer
  3. Because TLS termination removes the need for backend pools
  4. Because reverse proxies work only for static sites

Once a proxy accepts traffic on behalf of backends, it is already positioned to route and distribute that traffic. The same product can therefore play both roles.

When is splitting reverse-proxy and load-balancing duties into distinct tiers most justified?
  1. When there is only one backend server
  2. When the system has no TLS and no routing rules
  3. When HTTP edge concerns and backend scaling have different operational or performance needs
  4. When you want to avoid all extra network hops

Separate tiers make sense when edge processing, security policy, or caching evolve differently from backend fan-out. That lets each tier scale and be owned independently.

What is one major benefit of terminating TLS at the reverse proxy?
  1. It makes databases strongly consistent
  2. It removes the need for health checks
  3. It allows centralized certificate management and enables HTTP-aware routing decisions
  4. It guarantees lower latency than plain HTTP

Centralized TLS simplifies certificate handling and exposes decrypted HTTP information for routing, caching, or compression decisions. It does not solve unrelated consistency or health-check concerns.

What is the biggest downside of using one combined proxy-and-load-balancer tier?
  1. It prevents horizontal scaling entirely
  2. It concentrates more responsibilities and resource usage in one place
  3. It makes backend health checks impossible
  4. It only works with UDP traffic

A combined tier is simpler, but it also means the same layer handles TLS, routing, caching, and backend distribution. That concentrates both CPU work and failure impact.

L4 vs L7 Load Balancing

Compare transport-layer and application-layer load balancing so you know when fast connection routing is enough and when HTTP awareness is required.

Intuition

Not every load balancer makes decisions at the same layer. Some only see IPs, ports, and connections, while others inspect HTTP paths, headers, and cookies. If you pick the wrong layer, you either pay for features you do not need or miss capabilities your application depends on.

This distinction appears constantly in interviews because it reveals whether you understand what information is available at different layers of the stack. It directly affects performance, routing flexibility, observability, and features like path-based routing or sticky sessions.

Mental Model

A layer 4 load balancer works at the TCP/UDP level. It knows connection metadata such as source IP, destination IP, and port, but it does not understand the HTTP request contents. A layer 7 load balancer terminates or parses application traffic, usually HTTP, so it can route based on path, host, header, cookie, or method. That extra awareness enables richer behavior but costs more per request. Think of it like: L4 is like a traffic cop sending cars to parking lots based only on lane and destination zone. L7 is like a receptionist reading each visitor’s appointment details and then deciding which department door they should use.

Building Blocks

  • An L4 path that sees transport metadata like IP, port, and protocol. It forwards connections quickly but does not understand HTTP meaning.
  • An L7 path that can inspect host, path, header, method, or cookie fields. That application awareness enables richer routing decisions.
  • Health checks matched to the layer, from simple port reachability at L4 to HTTP readiness endpoints at L7.
  • Backend pools whose needs decide the layer: generic TCP/UDP services usually fit L4, while multi-route web apps usually need L7.

Definitions

Layer 4 Load Balancer
A load balancer that makes decisions using transport-layer data such as IP addresses, ports, and protocol.
  • It is a natural fit for raw TCP or UDP forwarding.
  • It cannot route by URL path, header, or cookie.
Layer 7 Load Balancer
A load balancer that understands application-layer traffic, most commonly HTTP and HTTPS.
  • It can route by host, path, method, header, or cookie.
  • That flexibility costs more CPU and configuration complexity.
Connection-Based Routing
Choosing a backend using connection metadata rather than request contents.
  • This is the normal model for L4 devices.
  • It is efficient but blind to per-request HTTP intent.
Request-Based Routing
Choosing a backend using fields from the application request itself.
  • Examples include sending /api and /static traffic to different pools.
  • This requires L7 visibility.

Patterns

  • Use L4 for high-throughput TCP or UDP traffic when request inspection adds no value.
  • Use L7 when one endpoint must route different hostnames or URL paths to different services.
  • Layer them when you want an outer high-scale connection distributor and an inner HTTP-aware routing tier.

Strategies

  • Choose the layer from required features first: host routing, path routing, header rules, and cookie affinity immediately point to L7.
  • Choose L4 for non-HTTP workloads or pass-through cases where the extra parsing cost of L7 buys nothing.
  • Treat L7 overhead as a trade for useful application awareness, not as wasted work by default.

The real cost of choosing L7

L7 balancing does more work on the request path because it must terminate or parse application traffic before making a decision. That extra visibility enables path-based routing, host-based ingress, header policies, and cookie-aware behavior that L4 simply cannot provide.

So the decision is not just about speed. It is about whether the application needs request-aware routing. If all you need is fast connection distribution for TCP or UDP, L4 is simpler. If many web services share one domain or one edge, L7 often removes far more architecture pain than its overhead adds.

A common interview shortcut is AWS NLB versus ALB: NLB is the L4 mental model, ALB is the L7 mental model.

Tradeoffs

  • L4 has lower overhead and broader protocol support, but it cannot make HTTP-aware decisions.
  • L7 enables rich routing and policy enforcement, but it does more per request and is more complex to operate.
  • Choosing L7 can simplify multi-service web architectures, while choosing L4 keeps generic pass-through paths fast and simple.

Real World

  • AWS Network Load Balancer is the standard L4 example for fast transport-layer distribution of TCP or UDP traffic.
  • AWS Application Load Balancer is the standard L7 example for web systems that need host-based or path-based routing across multiple services.

Interview

Questions interviewers ask

  • What information can L4 use that L7 also uses, and what extra information does L7 gain?
  • Why can L4 not send /api and /images to different backends on the same hostname?
  • When would you choose NLB over ALB, or ALB over NLB?
  • What feature needs usually force a design toward L7?

What a strong answer covers

Candidates should map L4 to transport metadata and L7 to application-aware routing, then give concrete examples such as NLB versus ALB and explain the performance-versus-flexibility trade.

Common traps

  • Saying L4 is always better because it is faster without checking whether the app needs HTTP-aware routing.
  • Claiming L4 can route by URL path or cookie even though it cannot see those fields.
  • Ignoring that non-HTTP protocols naturally fit L4 better than L7.

Quiz

Which requirement most clearly forces a load balancer choice toward L7?
  1. Handling generic UDP traffic
  2. Routing /api and /images to different backend services
  3. Forwarding raw TCP streams quickly
  4. Passing through traffic without inspecting it

Path-based routing requires the balancer to read the HTTP request. That is an application-layer capability, so L7 is required.

What information does an L4 load balancer primarily use to make forwarding decisions?
  1. IP addresses, ports, and protocol details
  2. HTTP cookies and URL paths
  3. JSON request bodies
  4. Rendered HTML responses

L4 operates at the transport layer, so it sees connection metadata rather than application content. That is why it is fast but content-blind.

In AWS terms, which pairing is correct?
  1. ALB is L4 and NLB is L7
  2. Both ALB and NLB are L4 only
  3. NLB is for DNS only and ALB is for databases
  4. NLB is L4 and ALB is L7

AWS Network Load Balancer is the classic L4 example, while Application Load Balancer is the HTTP-aware L7 option.

Why is L7 generally heavier than L4?
  1. Because it always stores more data in the database
  2. Because it parses application-layer details before routing
  3. Because it can only run on physical appliances
  4. Because it requires clients to know all backend IPs

L7 spends extra CPU and sometimes latency budget understanding HTTP requests. That added context is what enables richer routing features.

When is L4 usually the better fit?
  1. When you need cookie-based affinity and path routing
  2. When each request must be routed by HTTP headers
  3. When the workload is high-throughput TCP or UDP and request inspection adds no value
  4. When many services share one hostname and one ALB path table

If the traffic is non-HTTP or does not need application-aware routing, L4 avoids unnecessary parsing overhead. It is optimized for fast connection handling.

Practice scaling systems in PRISM

Concepts stick when you watch them fail. Build an architecture that depends on scaling systems, push traffic through it in the PRISM simulator, and see the latency and error rates change as you adjust the design.