Altamar Labs
Back to Blog
architecture· 3 min read

Stateless vs. stateful: when simplicity beats control

Two legitimate ways to solve the same messaging problem, and how to choose between them without falling into the dogma of 'always simple' or 'always robust'.

There's a question almost never made explicit when designing a messaging pipeline, and yet it determines much of the system's future complexity: who remembers the state, and for how long?

Two solutions to the same problem

Picture a concrete problem: an event happens on a device (a message, a sensor reading, a notification) and needs to reach another system to be processed. There are two legitimate ways to solve it.

The stateful version treats every event as part of an ongoing relationship. It authenticates the source with its own credentials, persists the event before attempting delivery, retries on failure, and exposes that history for audit. This is the right answer when the cost of losing an event is high, when there are multiple sources that need to be distinguished from one another, or when someone (a client, a regulator, a support team) will eventually ask "what happened to this specific event?"

The stateless version treats every event as a self-contained, disposable unit. No database, no sophisticated retries, no history. The event gets filtered, transformed, sent, and the system forgets it ever existed. This is the right answer when volume is low, when the occasional lost event is tolerable, and when the cost of operating and maintaining stateful infrastructure exceeds the value of the guarantee it provides.

The most common design mistake

The mistake isn't picking the wrong option, it's not choosing consciously. It's common to see stateful systems built out of inertia ("that's how serious things are done") for cases where nobody ever needed an audit trail, and stateless systems that grew organically until they were handling money or regulated data, with nobody stopping to ask whether it was still the right call.

The question worth asking before writing the first line of infrastructure isn't "how robust can I make this?" It's:

  • If this event gets lost, does anyone notice? When?
  • Is there more than one source that needs to be distinguished, with different permissions or formats?
  • Will someone, at some point, need to reconstruct what happened?

If all three answers are "no," building the stateful version is over-engineering: you're paying the cost of maintaining credentials, encryption, and persistence for a guarantee nobody will ever use. If any answer is "yes," the stateless version isn't simplicity, it's technical debt that hasn't come due yet.

An important nuance

These two versions aren't necessarily maturity stages of the same system. Often they're the same solution built twice, for two different contexts, and both are correct. The goal isn't for everything to end up stateful "because that's the professional thing to do." It's to explicitly recognize, for each integration, what guarantee it actually needs and design exactly for that, no more and no less.

The engineering skill here isn't knowing how to implement exponential-backoff retries or model an event table with TTL. Those are known techniques. The real skill is resisting the pressure to default to the more sophisticated version, and having the judgment to say "this doesn't need persistence" when it's true, which tends to be, almost always, the less intuitive call to make.