The difference between an agent demo and an agent in production is rarely the reasoning. It is what happens when the process restarts mid-run, when a step needs a human's sign-off, or when someone asks "why did it do that on Tuesday?" LangGraph's answer to all three is the checkpointer, and it is the single strongest reason to use LangGraph for stateful workflows — including for teams that (reasonably) use plain SDK calls everywhere else.
What a checkpoint actually is
LangGraph models a workflow as a graph of nodes updating a shared, typed state. With a checkpointer configured, the runtime persists a snapshot of that state after each node executes — what has been computed, what messages accumulated, which node runs next. Snapshots group under a thread_id you choose: one thread per conversation, per document batch, per case being processed.
Three capabilities fall out of that mechanism, and they are exactly the three production asks:
Resumability. A deploy, crash, or timeout no longer destroys work in progress. Re-invoke the graph with the same thread id and it continues from the last completed node — it does not re-run the whole flow, and it does not re-charge you for the tokens the earlier steps already spent.
Interrupts for human-in-the-loop. You can declare that the graph should pause before a designated node — the one that sends the email, files the ticket, executes the trade. The run parks durably at the interrupt; a human reviews the proposed action (read straight from checkpointed state), edits it if needed, and resumes. The pause can last three seconds or three days; nothing is held in memory while it waits. This is the honest implementation of "agent with approval step" — not a while-loop with a confirm() bolted on.
Replay. Every historical checkpoint is inspectable: you can read the exact state at each step of a past run, or fork from a mid-run checkpoint and re-execute with a changed prompt or fixed tool. For debugging agent behavior, this beats log archaeology by a wide margin — you are not reconstructing what the model saw, you are looking at it.
Production setup: boring on purpose
- Use a database-backed checkpointer. The in-memory saver is for tests. In production, use the Postgres checkpointer (or SQLite for genuinely single-node tools) so state survives restarts and is shared across replicas. Run it in the same database your ops team already backs up and monitors — checkpoints are application data.
- Choose thread ids deliberately. They are your unit of resumption and audit. Derive them from a real business key (case id, document batch id) rather than random UUIDs; "resume the workflow for case 4127" should be a lookup, not a search.
- Keep state lean. The state schema should hold references and decisions — document ids, tool results you actually need downstream, the running message list — not multi-megabyte blobs. Store large artifacts in object storage and checkpoint the pointer. Fat state makes every step slower and every checkpoint a liability.
- Treat checkpoints as data with a lifecycle. They may contain user content, so retention rules apply: decide how long finished threads live, delete them on schedule, and exclude anything you would not put in an application table. And version your state schema — a deploy that changes state shape must either migrate live threads or drain them first. Teams forget this until the first mid-run deploy corrupts a resumed workflow.
The failure modes we actually see
The checkpointing bugs in the wild are rarely in LangGraph; they are in what teams assume around it.
Non-idempotent nodes. Resumption re-runs the node that was interrupted mid-flight. If that node had already sent the email before crashing, resuming sends it twice. Side-effecting nodes need idempotency keys or an outbox pattern, the same as any distributed system — checkpointing does not exempt you from exactly-once discipline.
Interrupts without an owner. A paused run is a work item. If no queue, notification, or dashboard surfaces "12 threads waiting for approval", they wait forever and the feature quietly becomes a graveyard. Build the review surface at the same time as the interrupt.
Replaying against a moved world. Forking a two-week-old checkpoint re-executes with today's tools, today's data, and today's model version. That is usually what you want for debugging, but it means replay is a diagnostic, not a deterministic re-run — treat divergence as information, not a bug in the checkpointer.
When it earns its keep
Checkpointing carries real weight — a database dependency, schema-versioning discipline, idempotency care. A stateless request/response chain does not need any of it, and adding LangGraph there is indirection without payoff. The test is simple: does the workflow outlive a request, involve a human pause, or need auditable step-by-step history? If yes, checkpointed LangGraph is the most engineering-shaped answer in the ecosystem right now. If no, keep it simple — and spend the saved complexity on your evals.