The LangGraph checkpointer is good. I want to say that first, because what follows reads like a complaint and it isn't one. Thread-level persistence, time travel, human-in-the-loop pauses: those work, and they work well.
There's one failure mode it doesn't cover, and it's the one you hit in production. The process dies after a side effect but before the checkpoint that records it.
What's the difference between checkpointing and durable execution?
A checkpoint is a snapshot of agent state, written once a step has finished. Durable execution keeps a journal of every step as it runs, and the runtime, not your code, decides what completed.
Here is where that matters. A graph node calls an external API. It files a Jira ticket, or it charges a card. The call succeeds. Then the pod catches a SIGKILL during a rolling deploy, before the checkpoint write lands.
A checkpointer restores the last checkpoint it has, which is the one from before the node ran. So the agent runs the node again, and now there are two tickets and two charges.
Durable execution sees an activity in the journal that started and never confirmed, and it can tell you that's what it is. The steps that did confirm don't run twice. The workflow resumes at the one that didn't.
That's the part of AI workflow orchestration nobody demos. Drawing the boxes and arrows is easy. The runtime underneath has to answer "what already happened?" at any point, and that is a harder thing to build than it looks.
Keeping LangGraph, adding durability
You don't have to pick one. LangGraph stays the brain, and each side-effecting step moves inside a durable activity.
# before: the node does the side effect directly
def create_ticket_node(state):
ticket = jira.create(state["summary"]) # unprotected side effect
return {"ticket_id": ticket.id}
# after: the node schedules a durable activity
def create_ticket_node(state):
ticket_id = yield ctx.call_activity(create_ticket, input=state["summary"])
return {"ticket_id": ticket_id}
Same graph, same prompts. The difference is that every step now gets journaled, retried with backoff, and resumed after a crash rather than replayed from scratch. That applies to your LLM calls too, so a crash doesn't re-spend the tokens you already burned on steps one through six.
Caveat where it's due, because durable execution asks for things too. Your workflow code has to be deterministic, which rules out random branching outside activities. And you need to think about versioning before you change a workflow that has instances in flight. So there's a cost. I'd pay it the moment an agent starts touching anything with an invoice attached.
We've written up running LangGraph in production with durable execution at more length, and there's a longer argument for why checkpoints aren't durable execution if you want to know more of the details.
Top comments (0)