Skip to main content

Stateful vs Stateless Design Explained

· 4 min read
Free system design course

Where does a server keep what it knows about a client between one request and the next? The answer to that one question decides how easily the whole system scales.

Stateless: every request carries what it needs

A stateless server treats each incoming request as fully self-sufficient — everything needed to handle it (an auth token, the relevant IDs, the data itself) travels with the request, and the server keeps nothing about the client in its own memory afterward. This pairs naturally with horizontal scaling: because any stateless server instance can handle any request equally well, a load balancer can route traffic to whichever instance is least busy, with no need for "sticky" routing that pins a client to one specific server.

Stateful: the server remembers you

A stateful server holds context about a specific client in its own memory between requests — a session object, an in-progress multi-step operation, an open connection. This makes certain interactions simpler to reason about, but it means a client generally has to keep talking to that specific server instance for the interaction to keep working, which is exactly the sticky-routing requirement statelessness avoids.

System Design Lab

Statelessness doesn't mean state disappears — it means it moves

This is the part that trips people up: a stateless architecture doesn't mean the application has no state at all — a shopping cart, a user's session, a logged-in identity are all still real state that has to live somewhere. What changes is where. Instead of living in one server's process memory, it moves to a shared store every server instance can reach equally — a database, a distributed cache like Redis, or an encoded, signed token (a JWT) the client carries and presents on every request. Any server can then serve any request, because the state it needs isn't tied to which server happened to handle the client last.

The resistant case: WebSockets

Not everything can be made stateless cleanly. A WebSocket connection is a genuine counterexample: the open, persistent connection between a specific client and a specific server is the state — there's no way to externalize "this TCP connection is open" to a shared database the way you can externalize a session token. Real-time systems built on WebSockets typically need sticky routing (or a connection-aware layer that can route messages to whichever server actually holds the relevant socket) precisely because this one piece of state can't be moved out of the server holding the connection.

Why this matters in an interview

Defaulting to stateless design is usually the right instinct for anything expected to scale horizontally — it's what makes adding more servers behind a load balancer actually work without extra coordination. But naming where the externalized state goes (which store, and why it's fast enough not to become the new bottleneck) is what separates a real answer from just saying "make it stateless." And naming WebSockets — or any other case where state genuinely can't move — as the exception shows you understand the tradeoff isn't universal, just usually correct.

Go deeper

The full lesson goes deeper on session storage options and where sticky routing legitimately earns its place:

👉 Read the full Stateful vs Stateless Design lesson — part of the free System Design Lab course.