Durable Execution: The Pattern That's Quietly Fixing Distributed Systems



The Problem Everyone Has But Few Talk About

You have a workflow: charge a customer, provision a resource, send a confirmation email, update a CRM. Simple enough.

Now ask yourself: what happens when step 3 fails? What if the process crashes between step 2 and step 3? What if the network times out during the charge but the payment actually went through? What if this workflow takes 30 days because it’s waiting for human approval?

Welcome to the distributed systems reliability nightmare. Engineers have been solving this with increasingly complex combinations of message queues, retry logic, dead letter queues, saga patterns, and distributed locks — building elaborate scaffolding just to ensure that business logic runs to completion.

Durable Execution is the framework-level answer to this problem, and by 2026 it has become a production-proven pattern that serious engineering organizations are adopting to eliminate entire categories of reliability bugs.


What Is Durable Execution?

Server infrastructure Photo by Taylor Vick on Unsplash

Durable execution is a programming model where the runtime guarantees that your code runs to completion, even across failures, crashes, and infrastructure restarts — without the programmer needing to implement custom recovery logic.

The key insight: instead of your code managing state in ephemeral memory and fighting to persist it externally, the execution framework journals every step. When a crash occurs, the workflow resumes exactly where it left off, with the exact same local variable state, as if the crash never happened.

From the developer’s perspective, they write straightforward sequential code:

# This looks like regular Python — but it's durable
@workflow.defn
class OrderFulfillmentWorkflow:
    @workflow.run
    async def run(self, order: Order) -> str:
        # Each activity is automatically retried on failure
        payment_id = await workflow.execute_activity(
            charge_customer,
            order.payment,
            start_to_close_timeout=timedelta(seconds=30)
        )
        
        resource = await workflow.execute_activity(
            provision_resource,
            order.items,
            start_to_close_timeout=timedelta(minutes=10)
        )
        
        # This waits for a human approval signal — could be days
        approval = await workflow.wait_condition(
            lambda: self.approved,
            timeout=timedelta(days=7)
        )
        
        if approval:
            await workflow.execute_activity(send_confirmation, order)
        
        return resource.id

If the process crashes after charge_customer but before provision_resource, when it restarts it will skip the charge (already completed and journaled) and retry the provision. No duplicate charges. No lost orders. No manual reconciliation.


The Core Mechanism: Event Sourcing Under the Hood

The magic is implemented through deterministic replay with event sourcing:

  1. Every activity completion is appended to an immutable event log
  2. On restart, the workflow replays the event log to reconstruct state
  3. Activities whose results are in the log are skipped (not re-executed)
  4. Execution continues from the first unapplied step

This is why durable workflows must be deterministic — the replay must produce the same sequence of activities as the original execution. Random numbers, current timestamps, and external state must be obtained through the framework, not from system calls.

# WRONG — non-deterministic
import random
discount = random.random() * 0.1  # Different on replay!

# RIGHT — deterministic through workflow primitives  
discount = await workflow.execute_activity(
    calculate_discount,  # Activity result is journaled
    customer_id
)

The Landscape: Temporal, Restate, and Others

Temporal

Temporal is the most mature and widely-deployed durable execution platform in production. Originally developed at Uber (as Cadence), it’s now used at Stripe, Netflix, Coinbase, and hundreds of other companies.

Strengths:

  • Battle-tested at massive scale
  • Rich SDK ecosystem (Go, Java, Python, TypeScript, .NET, PHP)
  • Strong query and signal primitives for complex workflows
  • Excellent visibility tooling

Tradeoffs:

  • Requires a separate Temporal cluster (or Temporal Cloud)
  • Operational overhead is non-trivial for self-hosted
  • Can be verbose for simple use cases

Restate

Restate is a newer entrant (2023) that takes a different architectural approach — it acts as an HTTP proxy that makes any service handler durable, without requiring workflows to be a special type.

// Restate: any handler becomes durable
const orderService = restate.service({
  name: "OrderService",
  handlers: {
    // This handler is automatically durable
    placeOrder: async (ctx: Context, order: Order) => {
      const paymentId = await ctx.run("charge", () => 
        chargeCustomer(order.payment)
      );
      
      const resource = await ctx.serviceClient(ResourceService)
        .provision(order.items);  // Durable cross-service call
        
      return { paymentId, resourceId: resource.id };
    }
  }
});

Strengths:

  • Lower operational overhead
  • Works well with existing HTTP services
  • Great TypeScript/Java DX
  • Virtual objects enable stateful services without external state stores

Tradeoffs:

  • Smaller ecosystem than Temporal
  • Less battle-tested at extreme scale

Other Notable Options

  • AWS Step Functions — Managed, AWS-native, JSON-based DSL. High cost at scale but zero operational overhead.
  • Durable Task Framework (Azure) — Microsoft’s implementation, now open-sourced as Durabletask
  • Inngest — Developer-focused, serverless-first, great for event-driven workflows

When Durable Execution Shines

Long-Running Business Processes

Any workflow that spans minutes, hours, or days is a perfect candidate. Human-in-the-loop approvals, multi-day onboarding sequences, payment reconciliation jobs — these are exactly the cases where traditional retry logic fails.

Saga Orchestration

The saga pattern (compensating transactions for distributed systems) becomes dramatically simpler with durable execution. Each compensation step is just another activity — the framework handles ensuring compensation runs on failure.

Event-Driven Pipelines That Need Guarantees

Replace your Kafka consumer → DB update → outbox → another queue chain with a durable workflow that clearly expresses the business intent, with reliable exactly-once semantics.

AI Agent Loops

This is the emerging use case that’s driving renewed interest in durable execution in 2026. AI agents that might run for minutes, call external APIs, wait for tool results, and potentially run for hours need exactly the reliability guarantees durable execution provides.


Common Concerns

“It’s vendor lock-in.” Temporal and Restate are both open-source. Self-hosting is viable. The programming model does create some lock-in, but it’s architectural lock-in (like choosing microservices) rather than vendor lock-in.

“It adds latency.” The event journal means each activity completion involves a write to the persistence layer. Typical overhead is 5–20ms per activity. For workflows spanning seconds or more, this is negligible. For sub-100ms latency requirements, use a regular queue.

“Debugging is hard.” Actually the opposite — Temporal’s UI shows you the complete history of every workflow execution. Debugging a bug that happened 3 days ago means replaying the exact execution, including all input/output data.


Getting Started

  1. Try Temporal locallytemporal server start-dev gives you a full cluster in one command. Write a simple workflow in Python or TypeScript, trigger it, kill the process mid-execution, restart, and watch it resume.

  2. Identify your worst reliability problem — What’s the workflow in your system that most often requires manual reconciliation when it fails? That’s your first migration target.

  3. Start with the happy path — Implement the workflow without worrying about every error case. Let the framework handle retries. Add custom error handling only where business logic requires it.


Conclusion

Durable execution doesn’t eliminate distributed systems complexity — it moves it from application code into the framework layer, where it can be solved once, correctly, and reused everywhere.

The engineers who’ve adopted Temporal or Restate consistently report the same thing: whole categories of bugs simply stop happening. No more “the workflow got stuck halfway through.” No more manual database cleanup jobs. No more customers charged twice.

In a world where system complexity is only increasing, that kind of reliability simplification is worth a serious look.


Further reading: Temporal documentation, Restate documentation, “Designing Durable Workflows” (Temporal blog), Saga Pattern (Richardson)

이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)