Software Engineering·July 21, 2026·9 min read

Load Balancing

What a load balancer does, how traffic gets distributed, and what you need to know as a builder using managed platforms.

load-balancingscalingnetworkinginfrastructure

A load balancer sits in front of multiple servers and spreads incoming traffic across them. It's what lets you run more than one copy of your app behind a single address — the foundation of both handling more traffic and surviving a server failure. If Cartara flagged this in your diff, you likely changed something related to how your app handles multiple instances, session state, or health checks.

Why you need one

A single server has two problems: it can only handle so much traffic, and if it dies, your app goes down. Running multiple servers solves both — but now something has to decide which server handles each request and stop sending traffic to any server that fails. That's the load balancer.

It delivers two things at once:

  • Scalability — add more servers behind the balancer to handle more load
  • Availability — if one server goes down, the balancer routes around it automatically

This only works because your app servers need to be stateless: any server can handle any request, because session data lives in shared storage (a database or Redis) rather than on one specific machine.

Layer 4 vs Layer 7

Load balancers operate at one of two levels:

  • Layer 4 (transport) — routes by IP address and port, without looking at request content. Very fast, but can't make decisions based on URLs or headers
  • Layer 7 (application) — understands HTTP. Can route /api/* to one pool of servers and /images/* to another, terminate TLS, and inspect headers

Most web apps use Layer 7 because content-aware routing is so useful.

How the balancer picks a server

  • Round robin — hands requests to servers in rotation. Works well when servers are equal and requests are similar in cost
  • Weighted round robin — gives more powerful servers a larger share
  • Least connections — sends the next request to whichever server is handling the fewest active requests. Better when request duration varies a lot
  • IP hash — routes based on the client's IP, so a given user consistently hits the same server

Round robin and least connections cover most cases. Least connections is the safer default when request cost is uneven.

Health checks

A load balancer must know which servers are actually healthy — otherwise it'll keep sending traffic into a broken instance. It does this by periodically pinging each server (usually a /health endpoint) and removing any that fail from the rotation, adding them back when they recover.

Give your app a lightweight health endpoint that genuinely reflects whether it can serve requests — not just "the process is running" but "can I reach the database."

Sticky sessions: when and why to avoid them

Sometimes you want a given user to keep hitting the same server — for example if that server holds some in-memory session state. Sticky sessions (session affinity) do this.

The honest advice: prefer to avoid needing them. Sticky sessions undermine the stateless model — they make load distribution uneven and mean a server failure logs out affected users. The better approach is to keep session state in a shared store (Redis, a database) so any server can serve any user. Reach for sticky sessions only when you genuinely can't make the app stateless.

How this fits into the broader request path

A typical request path looks like:

```
CDN (edge cache) → Load balancer / API gateway → App servers → Database
```

  • Reverse proxy — a server that receives client requests and forwards them to backends (Nginx, HAProxy). A load balancer is essentially a reverse proxy that distributes across many backends; the terms overlap heavily
  • API gateway — adds API-specific concerns: authentication, rate limiting, request shaping. Common in multi-service setups
  • CDN — serves cached content close to users before traffic ever reaches your load balancer

What You'll See in Your Code

Cartara often flags stateful patterns that break horizontal scaling. A common example is storing session data in memory:

```javascript
// Problematic: session lives in server memory
// Works fine with one server, breaks with multiple
const sessions = {}

app.post('/login', (req, res) => {
const sessionId = generateId()
sessions[sessionId] = { userId: req.body.userId } // stored in memory
res.cookie('sessionId', sessionId)
res.json({ ok: true })
})

// Better: session stored in Redis (shared across all servers)
import { redis } from './redis'

app.post('/login', async (req, res) => {
const sessionId = generateId()
await redis.set(session:${sessionId}, JSON.stringify({ userId: req.body.userId }), 'EX', 86400)
res.cookie('sessionId', sessionId)
res.json({ ok: true })
})
```

A health check endpoint the load balancer can call:

```javascript
app.get('/health', async (req, res) => {
try {
await db.$queryRawSELECT 1 // verify DB connectivity
res.json({ status: 'ok' })
} catch (err) {
res.status(503).json({ status: 'unhealthy', error: err.message })
}
})
```

For builders on managed platforms

If you're on Vercel, Railway, Render, or similar — you already have load balancing without thinking about it. These platforms run multiple instances of your app behind a single URL automatically. What's worth doing deliberately:

  • Keep your app stateless so instances can scale horizontally
  • Expose a real health-check endpoint
  • Store sessions in a shared store, not in server memory

The infrastructure is handled; the architectural discipline is on you.

Related concepts

Serverless Compute
What serverless means, when to use it, and the trade-offs that trip up builders — cold starts, statelessness, costs at scale.
Message Queues and Async Processing
Message queues let your app offload slow work to the background so users get fast responses. This guide explains queues, pub/sub, workers, and key patterns like idempotency and dead letter queues.
Networking Essentials
What actually happens when a request travels across the internet — DNS, TCP, TLS, HTTP versions, and CDNs explained for builders.

Turn shipping into understanding

Cartara measures what your team actually learns from every AI coding session.

Join the waitlist