LangChain's chains are great until you need state. LangGraph is what you reach for when "this happened then this happened" is no longer enough, and you need "this happened, so consider this, and then maybe loop back."
When to reach for LangGraph
Three signals: you need conditional routing between steps, you need to recover from failures partway through a workflow, or you need to model human-in-the-loop checkpoints.
If your workflow is linear with no branching, stick with chains. Adding LangGraph adds overhead.
Conditional routing
The core LangGraph primitive: nodes and edges. Nodes do work. Edges decide what runs next. The router edge is where the value shows up. You can express "if the agent's previous output included an error, retry; otherwise continue" without writing imperative glue code.
Checkpoints and recovery
LangGraph supports checkpointing state at every node. If a workflow fails at step 7, you resume at step 7, not step 1. For long workflows (data processing pipelines, multi-step research tasks) this is the difference between usable and not.
Long-running workflows
Some agent workflows take minutes. Some take hours. LangGraph's persistence layer handles this. The graph can pause, persist its state, and resume on a different worker. For human-in-the-loop steps (where you genuinely wait for a human to approve something) this is essential.
Anti-patterns
The biggest anti-pattern I've seen: building a giant LangGraph for a workflow that's actually linear. If your edges are all unconditional, you're paying graph overhead for chain semantics. Simplify.
The second: hiding tool-calling logic inside graph nodes. Use MCP. The graph should orchestrate, not integrate.