From Stateful to Stateless

Complete System Design Notes — Points 1 to 33

ScalabilityDistributed SystemsWeb ArchitectureInterview Prep

Contents

1. The Core Problem

The central architectural question is:

Can any request be handled by any application server?

If yes → stateless architecture. If no → state is tied to a particular server.

2. Stateful Architecture

                    User
                      |
                      v
                +-----------+
                |    LB     |
                +-----------+
                      |
             +--------+--------+
             |                 |
             v                 v
        +---------+       +---------+
        | Server A|       | Server B|
        +---------+       +---------+
        | Session |       | Session |
        | Cart    |       | Cart    |
        | Files   |       | Files   |
        +---------+       +---------+

User ---> Server A
User ---> Server B  X

Server A knows something Server B does not. The load balancer may therefore need sticky sessions.

3. Stateless Architecture

                       +-------------+
                       |    Client   |
                       +-------------+
                              |
                              v
                       +-------------+
                       | LoadBalancer|
                       +-------------+
                         /     |     \
                        v      v      v
                    +-----+ +-----+ +-----+
                    | App | | App | | App |
                    |  A  | |  B  | |  C  |
                    +-----+ +-----+ +-----+
                       \       |       /
                        \      |      /
                         v     v     v
                    +-------------------+
                    | External State    |
                    | Redis / DB / S3    |
                    | Kafka              |
                    +-------------------+

Now App A, B and C are interchangeable clones. Any instance can process any request.

  • Horizontal scalability
  • Easier autoscaling
  • Simpler failover
  • Easier deployments
  • Better fault isolation
  • Easier container orchestration

4. What Exactly Is State?

State is information that must survive from one request/process execution to another in order to maintain application behavior.

STATE
 |
 +-- HTTP Session
 +-- Authentication
 +-- Shopping Cart
 +-- Uploaded Files
 +-- Generated Files
 +-- Local Cache
 +-- Local Database
 +-- In-memory objects
 +-- WebSocket connections
 +-- Background jobs
 +-- Locks
 +-- Temporary processing data
 +-- Rate-limit counters
 +-- Feature flags
 +-- Configuration

Not all state must disappear. The goal is to remove instance-local state from horizontally scaled application servers.

5. HTTP Sessions

HTTP is stateless. Each request is independent:

Request 1: GET /login
Request 2: GET /account
Request 3: GET /portfolio

Applications use cookies/tokens to create continuity across requests.

Browser
   |
   | session_id
   v
App Server
   |
   +--> Find session
   +--> Identify user
   +--> Load preferences/cart/etc.

If session data lives in one server's memory, the user becomes tied to that server.

6. Cookie Sessions

Instead of storing only a session ID, the cookie can contain small session data, usually serialized and protected.

Application data
       |
       v
Serialize
       |
       v
Encrypt / Sign
       |
       v
Cookie
       |
       v
Browser

Good for: small session data such as user ID, token, and a few preferences.

Problem: cookies are sent with requests, so large session data creates network overhead.

5 KB cookie × 100 requests = 500 KB transferred per user session

Also remember:

Encoding ≠ Encryption ≠ Signing

Use appropriate cookie protections such as Secure, HttpOnly and SameSite.

7. Centralized Session Store

                 Browser
                    |
                    | session_id
                    v
             +-------------+
             | Load Balancer|
             +-------------+
               /    |    \
              v     v     v
           App A  App B  App C
              \     |     /
               \    |    /
                  Redis
                     |
          user/session information

The cookie carries a small session ID; Redis holds the actual session.

  • Small cookie
  • Large session data possible
  • Any app instance can serve the user

Trade-off: Redis becomes critical infrastructure and must be highly available, monitored and capacity-planned.

Statelessness often means relocating state to a purpose-built state store.

8. Sticky Sessions

User
 |
 v
Load Balancer
 |
 +----> Server A
          |
          +--- Local Session

User X ---> Server A
User X ---> Server A
User X ---> Server A

The load balancer keeps a user attached to a particular server.

Problems:

  • Poor load distribution
  • Harder autoscaling
  • Bad failure behavior
  • Harder server replacement
  • Session may be lost when a server crashes

Sticky sessions preserve statefulness; they do not eliminate it.

9. Session Strategy Comparison

ApproachStateless AppScalabilityComplexityMain Problem
Cookie sessionYesHighLowCookie size / bandwidth
Redis/session storeYesHighMediumRedis becomes critical dependency
Sticky sessionsNoMediumLowPoor failover / rebalancing

Typical choice: small immutable information → signed/encrypted cookie/token; larger mutable session information → Redis/distributed store.

10. JWT and Stateless Authentication

Login
  |
  v
Authentication Service
  |
  v
JWT
  |
  v
Client
  |
  | Authorization: Bearer JWT
  v
Any App Server
  |
  +--> Validate signature
  +--> Read claims
  +--> Process request

No session lookup is necessarily required.

JWT introduces trade-offs around token revocation, token size, key rotation, expiration, compromised tokens and refresh-token management.

JWT is a mechanism for carrying claims; it is not automatically the best authentication architecture.

11. Files Are Another Hidden Source of State

User
 |
 v
Server A
 |
 +--> /uploads/user123.pdf

Later:

User
 |
 v
Server B
 |
 +--> File doesn't exist!

Local files make application instances stateful because Server A's filesystem is not Server B's filesystem.

12. Local Disk Creates Stateful Servers

             Load Balancer
                  |
          +-------+-------+
          |               |
          v               v
       Server A        Server B
       /data           /data
       file.pdf       EMPTY

Persistent user data on local disk prevents requests from being freely routed to any instance.

13. Solution — Object Storage

                 +-------------+
                 | App Server A |
                 +-------------+
                        |
                        v
                 +-------------+
                 | Object Store|
                 +-------------+
                        |
                 +------+------+
                 |             |
             document.pdf   image.jpg

Use durable object storage such as S3-compatible storage for user-generated files and large system-generated artifacts.

The application stores metadata and access information; it does not make the application instance the owner of the file.

14. Better Upload Architecture

                1. Request upload URL
Client ------------------------> API
                                  |
                                  v
                           Generate pre-signed URL
                                  |
                <-----------------+
                2. URL
Client ------------------------> Object Storage
                3. Upload

For large uploads, direct client-to-object-storage transfer reduces application-server bandwidth and improves scalability.

For processing:

Object Storage
      |
      | Event
      v
 Queue / Kafka
      |
      v
Document Processing Workers

15. User-Generated vs System-Generated Files

User-generated

  • Profile photos
  • PDF/KYC documents
  • Videos
  • Customer uploads

→ Object Storage

System-generated

  • Reports
  • Statements
  • Exports
  • Invoices
Request
   |
   v
API
   |
   v
Job Queue
   |
   v
Worker
   |
   v
Generate file
   |
   v
Object Storage
   |
   v
Download URL

Avoid making an API server synchronously generate very large files.

16. Temporary Files

Temporary files can also cause hidden coupling:

Server A
 |
 +-- /tmp/report.csv

Another request
 |
 v
Server B
 |
 +-- /tmp/report.csv --> NOT FOUND

Use object storage or ephemeral local storage only when losing the data is safe.

Distinguish ephemeral state from durable state.

17. Local Cache Is Also State

Server A: user123 -> balance = ₹10,000
Server B: user123 -> balance = ₹8,000

Caching is not inherently bad. The key question is whether the cache is the authoritative source of truth.

Database = Source of Truth
        |
        v
Redis / local cache = Derived data

Cache miss
   |
   v
Database
   |
   v
Rebuild cache
A cache should generally be disposable and reconstructible.

18. In-Memory State

Server A:
Map<UserId, Session>
       |
       +-- Session X

Server B:
       |
       +-- No Session X

Local in-memory maps, session objects and mutable process variables break horizontal scaling when they affect correctness.

Move critical state to Redis, a database or another distributed state store.

19. Background Jobs

Do not keep critical job state only in application memory.

API
 |
 v
Queue
 |
 +--> Worker A
 +--> Worker B
 +--> Worker C

Job state can live in a database, Redis, queue, or workflow engine depending on requirements.

This allows workers to be added, removed or restarted without losing the job's authoritative state.

20. Locks Are State Too

Server A holds:
Lock(Order123)

Server A crashes
       |
       v
Who releases the lock?

Distributed systems may use database locks, Redis-based coordination or dedicated coordination systems.

However, consider whether distributed locks can be replaced with:

  • Idempotency
  • Optimistic concurrency
  • Database constraints

These approaches can often be safer and simpler.

21. WebSockets and Stateful Connections

Client
  |
  | WebSocket
  v
Server A

Server A dies
       |
       X
Connection lost

WebSocket connections are inherently connection-stateful. At scale, events may need Pub/Sub, Kafka or another messaging mechanism.

User A connected to Server B

Server C receives event
        |
        v
      Pub/Sub
        |
        v
    Server B
        |
        v
      User A

22. Rate Limiting Is State

Suppose a user is allowed 100 requests/minute. Local counters can be inconsistent:

Server A: 95 requests
Server B: 95 requests

User can effectively make ~190 requests.

Use a distributed counter/store or gateway-level distributed rate limiting.

Requests
   |
   v
 Redis / Distributed Counter
   |
   +--> request count
   +--> TTL/window
   +--> decision

23. Configuration Is Also a Form of State

Server A: featureX = ON
Server B: featureX = OFF

Different configuration across instances can create inconsistent behavior.

Configuration / Feature Flag Service
              |
       +------+------+ 
       |      |      |
       v      v      v
       A      B      C

Use centralized configuration or feature-flag mechanisms where consistency matters.

24. Deployment Becomes Much Easier

Stateful instances are difficult to terminate because users may depend on local state.

Stateless deployment:

Old: A B C
New: D E

Shift traffic to D/E

Terminate A/B/C

This enables rolling deployments, blue/green deployments, canary releases and rapid rollback.

25. Autoscaling

Traffic:
100 RPS → 200 RPS → 500 RPS

Autoscaler:
3 instances → 8 instances

Because stateless instances are interchangeable, autoscaling can add capacity without needing to migrate user state between servers.

Horizontal scaling works best when compute instances are disposable.

26. Failure Handling

Stateless

Request
   |
   v
Server A
   X CRASH
   |
   v
Retry / new request
   |
   v
Server B
   |
   v
Success

Stateful

Request
   |
   v
Server A
   |
   +-- Local Session
   |
   X CRASH
   |
   v
Session LOST

Statelessness improves failure recovery and fault tolerance because another instance can take over.

27. The Stateless Frontend Architecture

                         INTERNET
                            |
                            v
                     +-------------+
                     | CDN / WAF   |
                     +-------------+
                            |
                            v
                     +-------------+
                     | LoadBalancer|
                     +-------------+
                            |
              +-------------+-------------+
              |             |             |
              v             v             v
          +-------+     +-------+     +-------+
          | App A |     | App B |     | App C |
          +-------+     +-------+     +-------+
              |             |             |
              +-------------+-------------+
                            |
              +-------------+-------------+
              |             |             |
              v             v             v
          +-------+      +-------+     +-------+
          | Redis |      |  DB   |     |  S3   |
          +-------+      +-------+     +-------+
                            |
                            v
                       +---------+
                       | Kafka   |
                       +---------+

The application tier becomes disposable while the state layer provides the required durability and coordination.

28. But Don't Make Everything Stateless

Statelessness has costs: network calls, latency, infrastructure complexity, operational cost and consistency concerns.

Local memory
     |
     | very fast
     v
Application

versus

Application
     |
     | network
     v
Redis
Keep instance-local state only when losing it is acceptable and it does not affect correctness or scalability.

29. State Classification Framework

                    STATE
                      |
       +--------------+--------------+
       |              |              |
   Durable         Temporary       Derived
       |              |              |
      DB          ephemeral       Cache
       |
       v
  Must survive
  failure?

Ask these questions

  1. Does this data need to survive server failure?
  2. Can another instance reconstruct it?
  3. Must every request see exactly the same value?
  4. Can the client safely carry it?
  5. Is the data large?

30. Architecture Decision Tree

             Do I need state?
                    |
              +-----+-----+
              |           |
             NO          YES
              |           |
          Stateless       |
                          v
                  Must survive failure?
                       |
                 +-----+-----+
                 |           |
                NO          YES
                 |           |
              Local       External
              memory      storage
                 |           |
              Cache?      +---+---+
                          |       |
                         DB     Object
                                Store

31. Interview Mental Model

When asked, "How would you make this application horizontally scalable?", do not immediately answer "add more servers."

Ask: Can I add server N without changing system behavior?

Inspect these sources of hidden state:

  1. Sessions
  2. Authentication
  3. Local files
  4. Local cache
  5. Local database
  6. In-memory maps
  7. Locks
  8. Background jobs
  9. WebSockets
  10. Rate limits
  11. Configuration
  12. Temporary files

For every one ask:

"What happens if this server disappears right now?"

This single question exposes many hidden scalability problems.

32. The Golden Rule

                    SERVER
                      |
        +-------------+-------------+
        |             |             |
      CPU           Memory         Disk
        |             |             |
      GOOD          CAREFUL         BAD
    ephemeral      derived       persistent
    computation     cache          state

A scalable application server should ideally behave like:

CREATE
   |
   v
USE
   |
   v
DESTROY

Critical information should not be trapped inside an application instance.

33. Final Architecture Cheat Sheet

State TypeTypical Location
User sessionRedis / token / cookie
AuthenticationToken + authentication service
User profileDatabase
Shopping cartRedis / Database
Uploaded filesObject Storage
Generated reportsObject Storage
Temporary processingEphemeral disk
CacheRedis / local cache
JobsQueue + Database
Job statusDatabase / Redis
Rate limitsRedis / API gateway
LocksDatabase / distributed coordination
WebSocket eventsPub/Sub / Kafka
ConfigurationConfiguration service
Feature flagsFeature flag service
Audit trailDurable database/storage
Golden takeaway: Don't eliminate state from the system; eliminate unnecessary state from the application instances.

One Diagram to Remember

                 ┌──────────────────┐
                 │      CLIENT      │
                 └────────┬─────────┘
                          │
                          ▼
                 ┌──────────────────┐
                 │ CDN / WAF / LB   │
                 └────────┬─────────┘
                          │
             ┌────────────┼────────────┐
             │            │            │
             ▼            ▼            ▼
        ┌────────┐   ┌────────┐   ┌────────┐
        │ App A  │   │ App B  │   │ App C  │
        │STATELESS│  │STATELESS│  │STATELESS│
        └────┬───┘   └────┬───┘   └────┬───┘
             │            │            │
             └────────────┼────────────┘
                          │
           ┌──────────────┼──────────────┐
           │              │              │
           ▼              ▼              ▼
       ┌────────┐    ┌─────────┐    ┌──────────┐
       │ Redis  │    │Database │    │Object    │
       │        │    │         │    │Storage   │
       └────────┘    └─────────┘    └──────────┘
                          │
                          ▼
                    DURABLE STATE