Temporal persists workflow state in event history, turning brittle recovery code into durable execution with retries, timers, and signals. Read now.
Why state machines break under recovery
Long-running business processes—order fulfillment, account onboarding, multi-step approvals—naturally look like state machines. You encode stages, transitions, and side effects in application code or a table of statuses. That model works until failure becomes normal: a process dies mid-transition, a downstream API times out, a human never responds, or a deploy restarts the worker halfway through a saga.
The usual fix is a pile of recovery code: checkpoints, “resume from last known state,” manual reconciliation jobs, and careful idempotency so a retry does not double-charge or double-ship. That recovery layer is brittle because the true process state lives in memory, ad hoc database rows, and tribal knowledge about which steps are safe to re-run. When something fails in an unexpected order, operators end up reconstructing intent from logs instead of replaying a reliable history.
Event history as the source of truth
A Temporal-style workflow engine flips that model. Instead of treating in-memory progress as primary and persistence as an afterthought, it records every meaningful step of the workflow as an event history. The workflow function is written as ordinary sequential code—call activities, wait for timers, react to signals—while the engine persists decisions and outcomes so the run can be rebuilt after a crash.
On recovery, the engine replays the history to restore where the workflow left off, without re-executing side effects that already completed. Durability is not a separate recovery path you invent per feature; it is how execution works. Retries, timers, and signals become first-class primitives rather than custom loops and cron patches bolted onto a status column.
- Retries: activities fail, back off, and resume according to policy without the workflow author reimplementing backoff tables.
- Timers: “wait three days, then escalate” is a durable wait, not a fragile sleep or a one-off scheduler job.
- Signals: external events (approvals, cancellations, payment webhooks) re-enter the same durable run instead of forking a second state machine.
What you gain by replacing the state machine
You still have states and transitions, but they emerge from control flow in code rather than a hand-maintained graph that must stay in sync with every edge case. Business logic reads as a story of what should happen next; the engine owns crash recovery, redelivery, and long waits. That separation shrinks the surface area of custom recovery code and makes failure modes easier to reason about: if the history is complete, the workflow can continue; if an activity is not idempotent, you fix that at the activity boundary rather than inventing a global resume protocol.
Tradeoffs remain. You accept an event-history model, activity boundaries for side effects, and operational discipline around worker deployment and workflow versioning when you change long-lived code paths. Those costs are usually lower than maintaining bespoke saga orchestration across services, especially as processes span days and many external systems.
Practical guidance for the migration
Start with a process that already hurts: multi-step work with timers, human input, or flaky third parties. Model the happy path as a single workflow, push real I/O into activities with clear retry and timeout policies, and keep the workflow deterministic so replay stays safe. Use signals for anything that arrives from outside the activity call graph. Keep domain status fields if product or reporting needs them, but treat the engine’s history as authoritative for “where is this run, and what is safe to do next.”
Do not try to mirror every old status enum one-for-one on day one. Replace the recovery story first—durable execution with retries, timers, and signals—then simplify status tracking once operators trust that a failed worker is a pause, not a lost process. That is the core shift: less brittle recovery code, more durable execution of the work itself.