Tech
Tech · ◉ Evergreen

Exactly once is a story you tell yourself

by Shreyansh Ojha·5 min·Working Theory

There’s a promise every distributed system badly wants to make and none can keep: this message will be delivered exactly one time. It sounds like the obvious default — of course an order confirmation should fire once, not zero times, not twice. But “exactly once” isn’t a setting you switch on. It’s a fantasy that the network, which can lose any packet at any moment, is fundamentally unable to honor. Once you accept that, a lot of confusing infrastructure snaps into focus.

Start with why the network can’t help you. Picture two parties who can only coordinate by sending messages across a channel that sometimes drops them. One sends a request; the other is supposed to act on it and reply. Now something goes silent. The sender is stuck with a question it cannot answer from where it stands: did my message never arrive, or did it arrive and the reply get lost on the way back? Those two worlds look identical from the sender’s chair, and no amount of extra acknowledgments closes the gap — each new “did you get my ack?” is just another message that can itself vanish. This is the Two Generals Problem, and it’s not an engineering shortcoming to be fixed with a cleverer protocol. It’s a proven impossibility. Perfect certainty about a remote event, over an unreliable channel, is not available at any price.

Given that, a sender facing silence has exactly two choices, and they define the two real delivery contracts. It can give up — assume the message is gone and move on. Or it can retry — send again, just in case the first one was lost. There is no third door.

Give up, and you have at-most-once delivery. Every message is sent zero or one times. You will never double-charge a card or send a duplicate email, because you never resend. The price is that some messages simply evaporate and no one notices until a customer asks where their receipt went. At-most-once is the right contract only when a lost message is cheaper than a repeated one — a metrics ping, a best-effort presence update, a cache-warm nudge. Losses you can shrug off.

Retry, and you have at-least-once delivery. Every message arrives one or more times. Nothing is silently lost — but the moment a retry lands on top of a first copy that did get through, the receiver sees the same event twice. This is the contract almost every serious system chooses, because for most work a duplicate is a problem you can solve while a silent loss is a problem you can’t even see. Webhooks, task queues, payment events — they nearly all promise at-least-once, and they’re telling you, in the fine print, that you will receive some messages more than once and it is your job to cope.

sender receiver message → ← ack (lost) sender can't tell: never arrived, or reply lost? give up → at-most-once 0 or 1 times · may lose, never duplicates retry → at-least-once 1 or more times · never lost, may duplicate build → exactly-once effect at-least-once delivery + a receiver that ignores a message it already handled the pipe never promises "once" — the front door does
The network offers only two honest contracts: maybe-lost, or maybe-duplicated. "Exactly once" is the third thing you assemble yourself, on the receiving side, out of retries and a memory of what you've already done. Original diagram · Working Theory

So where did “exactly once” go? It didn’t disappear — it moved. You can’t get exactly-once delivery, but you can get exactly-once effect, and the difference is the whole game. The recipe is at-least-once delivery — so nothing is ever lost — plus a receiver built to recognize a message it has already handled and do nothing the second time. Let the message arrive as many times as the flaky network needs it to; make the outcome fire once. “Exactly once” is not something the pipe delivers. It’s something the endpoint manufactures, by being willing to see a duplicate and refuse to act on it.

That places one non-negotiable demand on you as the builder: assume every message can be redelivered, and design the receiving end to survive it. Stamp each event with a stable identifier its sender will reuse on every retry, so the receiver can tell “a new thing happened” apart from “the same thing, said again.” Keep a memory of what you’ve already processed. Make the write safe to replay — reach a final state rather than blindly incrementing, so the tenth copy of “mark paid” leaves the same result as the first. This is the idempotent-handler discipline in its natural home: the delivery contract is why you need it. The messy edges are the real work — how long to remember a message ID before it’s safe to forget, and what “the same result” even means when the world has moved on between the first delivery and the fifth — but they’re edges you can actually engineer, unlike the certainty the network will never give you.

The mindset shift is the whole lesson. Junior systems are written as if messages arrive once, cleanly, and then spend their lives being surprised. Mature systems are written as if every message might arrive twice or vanish, and are calm when it does — because the guarantee they lean on isn’t a promise extracted from the network. It’s a property they built into their own front door.

The Two Generals / coordinated-attack problem (Gray, 1978); Fischer, Lynch & Paterson, impossibility of distributed consensus with one faulty process (1985); Akidau et al., streaming systems and the exactly-once-processing framing.

Liked this? Get the next one in Working Theory.

Going weekly in August (it's in beta now). One genuinely interesting read on building, the brain, and the science most people missed.

Got a reaction, a counter-example, or something I missed? Reply by email — I read everything.
◉ join in

Where have you hit this — in a product you use, or one you're building?

Threads open here soon. For now, the conversation lives two clicks away — discuss on GitHub, or just reply by email. I read and answer everything.