Stateful vs Stateless Architecture
Updated June 3, 2026One of the most consequential architectural decisions you make when building a backend service is whether your servers hold onto client state between requests. This distinction — stateful vs stateless — determines how easily your system scales, how it recovers from failures, and how much operational complexity you inherit.
Stateful: The Server Remembers You
Stateful — sticky sessions, uneven load, session lost on failure
A stateful server stores information about a client between requests. It remembers who you are, what you've done, and what you're in the middle of.
Classic examples: a traditional gaming server that keeps your position and inventory in memory; an old-school web app that stores your shopping cart in server-side memory; a TCP connection where the server tracks the connection state.
The experience feels seamless. The server has context. It doesn't need you to re-identify yourself on every request.
The problem: that context is tied to a specific server. If your load balancer sends your next request to a different server, that server has no idea who you are or what you were doing. You lose your session. Your cart is gone.
This forces a technique called sticky sessions (or session affinity) — the load balancer is configured to always send a specific user to the same server. And now you've lost one of the main benefits of having multiple servers in the first place.
What happens when that specific server goes down? The user's session is lost. Worse, the load balancer now can't evenly distribute traffic — some servers end up overloaded while others sit idle.
What is the core scaling problem that sticky sessions introduce in a stateful architecture?
Stateless: Every Request Stands Alone
Stateless — any server handles any request, state in Redis
A stateless server treats every request as if it's seeing the client for the first time. The request must contain everything the server needs to process it — authentication, context, parameters. Nothing is stored on the server between calls.
This is how REST was designed to work. Each HTTP request is self-contained.
What this enables:
- Any server in your fleet can handle any request
- No sticky sessions needed
- Load balancing becomes trivially even
- Servers can be added or removed without affecting clients
- A server dying loses no client data — the next request just goes to another server
The trade-off: the client (or some shared store) has to carry more information per request. Authentication tokens, session IDs, user context — something has to track this. It doesn't disappear; it just moves somewhere else.
A stateless server stores no state at all — all application data is derived purely from each incoming request.
Externalizing State
Here's the key insight: going stateless doesn't mean having no state. It means moving state out of your application servers and into dedicated storage that all servers can access.
Sessions in Redis
Instead of storing a user's session in the application server's memory, you store it in Redis with a session ID. The client holds the session ID (usually in a cookie). On every request, any server can look up the session from Redis in a millisecond.
Request: GET /dashboard
Cookie: session_id=abc123
Server A:
→ looks up session abc123 in Redis
→ gets { userId: 42, role: 'admin' }
→ handles requestServer B could handle the next request identically. It doesn't matter. The state lives in Redis, not in Server A's memory.
Which approach allows any server in a fleet to handle a user's authenticated request without calling a shared session store?
JWT Tokens
JSON Web Tokens take a different approach: put the state in the token itself. A JWT is a cryptographically signed blob that contains claims — userId, role, expiration time. Because it's signed with a secret key, the server can verify it without looking anything up.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// decoded payload: { userId: 42, role: 'admin', exp: 1748940000 }No database lookup required. Completely stateless. The trade-off: you can't revoke a JWT before it expires (without introducing a token blacklist, which puts you back in stateful territory).
How does JWT-based authentication achieve statelessness?
Why Stateless Is Preferred at Scale
| Concern | Stateful | Stateless |
|---|---|---|
| Horizontal scaling | Hard (sticky sessions) | Easy (any server handles any request) |
| Failure recovery | Session lost if server dies | Next request routes normally |
| Load balancing | Uneven (session affinity) | Perfectly even |
| Deployment | Tricky (draining sessions) | Simple (kill server anytime) |
| Complexity | State lives in app server | State externalized to Redis/DB |
The story is pretty clear. Stateless wins at scale. The cost is a small bit of added latency (a Redis lookup per request) or slightly larger request payloads (with JWTs). Both are worth it.
Practical note: Most modern web frameworks default to stateless design. Express, FastAPI, Spring Boot — they all encourage you to externalize sessions. The default has shifted because the industry learned this lesson the hard way.
JWT tokens can be revoked before they expire without any additional infrastructure.
Where Stateful Still Makes Sense
Stateless isn't always the right answer:
- Real-time connections: WebSocket connections are inherently stateful — the connection itself carries state. But you manage this by using a message broker (like Redis Pub/Sub or Kafka) so any server can route messages, even if the connection lives on one server.
- Long-running computations: If a task takes minutes and requires in-memory state, it might make sense to let it live on one machine. Use a work queue, not direct connections.
- Game servers: Position, physics simulation, and game state are often better in-memory for performance reasons. Dedicated game servers with session affinity are a legitimate pattern here.
WebSocket connections are an example of where stateful architecture is still appropriate.
Summary
Stateful servers remember client context between requests — convenient but problematic at scale because requests must return to the same server. Stateless servers treat every request as self-contained, enabling any server to handle any request and making horizontal scaling trivial. The key technique is externalizing state: sessions go into Redis, authentication context goes into JWT tokens or database-backed sessions. Stateless design is the default choice for web services at scale because it enables even load balancing, simple failure recovery, and frictionless horizontal scaling.
How helpful was this content?
Comments
Sign in to join the discussion
Saved on this device only
Sign in to sync progress across devices