Designing for Failure

What a design owes once calls can time out, repeat or half-succeed — including idempotency as a property chosen up front rather than retrofitted.

Designing for Failure

A function call either returns or throws. A remote call has a third outcome — you do not know — and an interface designed without a name for it will be wrong in a way no amount of error handling fixes.

Q · What does an interface have to look like once the call can time out, be retried, or half-succeed?
Idempotency by Design

Idempotency is a property of a signature, not a feature you add later. `createPayment(commandId, amount)` has the id in it because the failure model put it there — and no discipline around `createPayment(amount)` can substitute.

Q · What has to be in this operation's signature for repeating it to be safe?
Partial Failure

A local operation is all-or-nothing because the language and the transaction say so. A distributed one is not, and an interface that returns one boolean for five sub-operations is lying about what happened.

Q · This operation does four things across three systems. What should it return when the second one fails?
Retries Are a Property of the Operation

Retry safety is not something a caller can decide. It is a fact about the operation, and it has to be stated in the interface — otherwise every caller is guessing, and some of them will guess wrong.

Q · What must be true about this operation before anyone is allowed to call it twice?
Concurrency by Design

Every piece of shared mutable state is a permanent tax on reasoning. Before reaching for a lock, ask whether ownership can be local, the data immutable, or the operation atomic — those remove the problem instead of managing it.

Q · Can this design avoid needing a lock at all, and what does it cost if it cannot?
The Thread-Safety Contract

Whether a type may be used concurrently is part of its interface. Leaving it unsaid does not make it safe — it makes every caller guess, and the guesses are wrong at different times.

Q · May two callers use this object at the same time, and where does the answer live?
What Changes at the Network Boundary

A function call becomes a message that may be lost, delayed, duplicated or half-processed. "We can split it later" underestimates this, because the cost is not the transport — it is every interface that was designed as if calls always return.

Q · What exactly changes about an interface when the call it makes stops being local?