july 23, 2026 · 2 min read · distributed-systems, backend, durable-execution
Idempotency keys are not enough
The Idempotency-Key header protects an API boundary, not the workflow underneath. Three ways durable execution engines solve the rest.
If you are still solving retries with an Idempotency-Key header and a response cache, you are running the 2017 pattern Stripe formalised in a 2026 codebase.
Nobody notices because it still works for a single CRUD endpoint. The pattern breaks the moment a business action fans out across four services with three external side effects, any one of which can partially fail. The header protects the API boundary. It does nothing for the workflow underneath.
Temporal, Restate and DBOS solve this three different ways.
External orchestrator
Temporal, Inngest. You write business logic as a function. A separate cluster records every step to a log and, on failure, replays the function from the last checkpoint. Idempotency becomes a property of the runtime, not your code. Retry policies, timeouts and compensation are declarative. Mature and proven at scale, with extra operational surface to run.
Durable async/await
Restate. A lightweight runtime, deployed as a sidecar or a standalone server, journals every step your handler takes. Every side effect is wrapped in ctx.run and its result is recorded on success; on retry, the runtime returns the recorded result instead of re-executing, so the same email cannot be sent twice. The programming model is ordinary async functions and promises, so there is nothing exotic to learn.
Embedded, Postgres-backed
DBOS. No separate cluster, no sidecar. Workflow state lives in the same Postgres you already use for business data, in the same transaction. DBOS Transact ships SDKs for TypeScript, Java, Python and Go, and as of April 2026 has a Databricks Lakebase partnership behind it. The pitch is 10x less code than Temporal; the trade-off is that you trust Postgres for state, which most people already do.
Choosing
There is no best one. You are choosing between infrastructure weight, whether state lives inside or outside your database, and how much of your business logic gets pushed into the framework.
The header pattern is not dead. It is the right answer for a stateless CRUD endpoint and the wrong answer for a multi-step workflow, and standing up a durable engine in front of one has become cheap enough in the last year that it is now the default in new codebases.
Originally posted on LinkedIn.