We've been quietly saying "stateless" for modules. Now's the moment to look closely. Where does the user's identity actually live? How do you remember someone across requests when each one might hit a different server? This is the discipline that unlocks the whole pattern.
You build a login system. Alice signs in, your server creates a session, stores her info in memory, and gives her a session ID cookie. She makes a few more requests. Then she comes back later and gets "please log in again." What happened? Your code didn't change. Your servers are all running. The session cookie is right there in her browser. So why is she logged out?
Three out of four requests fail. Not because the code is wrong — the code on Server A worked perfectly. The problem is that the session lives only on Server A, and the load balancer is happily distributing Alice's requests across all three. To Server B, Alice looks like a stranger. To Server C, the same. This is the bug that bites every team the first time they go from 1 server to 2 — and the lesson it teaches is the most important pattern in horizontal scaling: statelessness.
State is anything a server remembers between requests. Variables in memory. Files on its local disk. Open database connections. Cached computations. Any of these create a dependency: "future requests must come back to this server because only it has this thing." And that dependency is what breaks horizontal scaling. If even one piece of important data lives only on one box, your "scalable" cluster turns into a fragile single-server system pretending otherwise.
this.cachedThingsMap.The pattern is now visible: state about a particular user, request, or action is the dangerous kind. Move it out of the server process and into a place every server can read — a database, a Redis cache, an object store. Once your servers don't remember anything specific about who's talking to them, you can spin up 5 more in 30 seconds and they all behave identically.
This is also the heart of the "12-factor app" principles that nearly every modern cloud platform expects: stateless processes, state in backing services. Why? Because it's what makes everything else — load balancing, auto-scaling, rolling deploys, failover, blue/green — actually work without users noticing.
When the question "where does the user's session live?" comes up, the industry has settled on three patterns. They're worth knowing because you'll see all three in real systems — sometimes in the same system. One is a beginner's mistake. One works but is fragile. One is the modern default. The next section turns this into a live comparison; first, a quick tour.
Store sessions in a JavaScript object or a server-side framework map. Works perfectly with one server. Breaks immediately when you go to two, because each session lives on whichever server happened to handle the login. The trap we opened the module with.
Configure the load balancer to "pin" each user to the same server. Now Alice always lands on Server A. The session-in-memory problem disappears — until Server A dies, and Alice's session dies with it. Works, but inherits the SPOF of vertical scaling.
Move sessions out of the server. Two flavors: store sessions in Redis and have every server read from it (server-side sessions), or encode the user's identity into a signed JWT token that any server can verify without lookup. Stateless servers, full horizontal scale.
The lab below puts all three side-by-side, running the same scenario. You'll see exactly where each one breaks and which one survives a server failure. Then we'll dig into the JWT-vs-Redis trade-off in §05.
Three columns, three strategies. Same scenario in each: a user logs in and makes more requests. The load balancer round-robins across servers (except in sticky mode). Hit Send next request repeatedly to walk through. Then hit Kill S1 and watch one of the three fall apart while another doesn't notice.
All three strategies will handle login (Request 1) successfully. The differences appear at Request 2 — when in-memory sessions discover their other-server problem. Then hit Kill S1 to see which strategy survives losing a server.
Option 3 — externalizing session state — has two distinct sub-patterns. Both work; both are modern; they trade off differently. Server-side sessions in Redis: server gives the client an opaque session ID, and looks up the real data in Redis on every request. JWT tokens: the server signs a token containing the user's identity, hands it to the client, and the client sends it back on every request — no lookup needed, the token itself is the proof.
sess_a7b2f9.The pragmatic rule of thumb: use Redis-backed sessions for normal user-facing web apps (you'll want revocation, and the lookup is cheap), and use JWTs for service-to-service auth, mobile APIs, and microservices (where avoiding a per-request lookup actually matters and revocation matters less). Many systems use both — JWT for API calls, Redis sessions for browser cookies.
One more thing worth mentioning: JWTs are signed, not encrypted. The contents are readable by anyone — they're just base64-encoded JSON. Don't put secrets in a JWT. The signature only guarantees "this was issued by me and hasn't been tampered with." If you need encryption, there's JWE for that — but most production systems just keep claims minimal and avoid the issue.
You'll see these in every architecture review involving login. Get fluent.
Test the statelessness intuition. Click an answer; the explanation arrives instantly.
You see the failure modes. Now onto Auth itself — how the identity in those tokens actually gets established.
This is one of those modules whose ideas show up in nearly every system you'll ever design.
Any data tied to one specific server box is a constraint that breaks horizontal scaling. Move user-related state out of process memory.
In-memory: don't. Sticky: works but fragile. External store or JWT: the modern default. Pick external for revocation; JWT for service-to-service.
Failures, rolling deploys, autoscaling — none of them feel scary when servers carry no per-user memory. The discipline pays back constantly.