LangGraph — step by step
A reading path, not a reference. Work down it in order; each step names what you should be able to do before moving on. Tick the box when you can explain it out loud without notes.
The deep-dive notes live in LangGraph, in depth. Verified against LangGraph 1.3.x / LangChain 1.3.x, 2026-08.
Before anything else: the front door is
create_agentfromlangchain.agents.create_react_agentis the pre-1.0 name — most tutorials and Stack Overflow answers still use it, and saying it out loud dates your knowledge to before October 2025.
Step 1 — What it is, and when not to use it
State machine runtime, not a chain library. A node never calls the next node — it returns an update and the runtime decides. That indirection is the whole design, and everything else is downstream of it.
Be able to say: why a chain cannot loop, branch, pause or resume, and the three conditions that justify adopting LangGraph at all — durability, human-in-the-loop, genuine branching.
Step 2 — State and reducers
from typing import Annotated
from operator import add
class State(TypedDict):
question: str
logs: Annotated[list[str], add]The single concept people miss. Without a reducer an update replaces the key; with one it merges. That is also what makes a key safe for two parallel branches to write.
Be able to say: what add_messages does that add does not (replaces by
message id, so a turn can be revised rather than duplicated).
Step 3 — Nodes, edges, routing
Normal edges, conditional edges, and Command for update-and-route in one
return. Loops are just a backward edge — an agent is a graph with a cycle and
a model in the router.
Be able to write from memory: the smallest complete graph, StateGraph →
add_node → add_edge(START, ...) → compile().
The gotcha: own your termination condition. The runtime’s recursion limit raises rather than finishing gracefully, so put a step counter in state.
Step 4 — The prebuilt agent
from langchain.agents import create_agent
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search],
)It returns a compiled graph, so everything from step 3 applies to it. Middleware is the 1.0 replacement for subclassing — summarisation, guardrails, approval and retry each become an independent unit.
Be able to say: where approval logic belongs (middleware, running on every
tool call — not prompt text), and when you would drop to StateGraph.
Step 5 — Persistence
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "conv-42"}}One mechanism, four payoffs: conversation memory, crash resumption,
human-in-the-loop, time travel. PostgresSaver in production —
AsyncPostgresSaver in an async app, or you block the event loop.
Be able to say: how you debug a bad run — read the state history, fork from the checkpoint before it went wrong with one value changed.
Step 6 — Human in the loop
from langgraph.types import interrupt, Command
decision = interrupt(
{"action": "refund", "amount": 500}
)
# ... later, possibly another process:
graph.invoke(Command(resume=True), config)The pause is durable — a row in Postgres, not a suspended coroutine — so approval can happen days later from another machine.
The two rules: resuming replays the node from its start, so side effects
go after the interrupt; and never wrap interrupt() in a bare except,
because it works by raising and a broad catch silently disables the gate.
Step 7 — Streaming
updates for step progress, messages for tokens — different questions, and
a long run needs both.
Be able to say: streaming changes perceived latency, not actual, and the underrated half is that visible progress lets a user abort a run that has misunderstood them.
Step 8 — Composition
A compiled graph is callable as a node. Three legitimate reasons to split: context isolation, genuine parallelism, privilege separation — otherwise one agent with more tools.
Be able to say: five agents at 90% each is about 59% end to end, and a supervisor routing on a state field needs no model at all.
Step 9 — Production
Durability modes, determinism, the three testing layers, and the failure catalogue.
Be able to say: why a node must be safe to run twice, and why now() or
random() inside one makes a resumed run diverge from the one you were
debugging.
The 60-second answer
If you get one question about LangGraph and no follow-up:
“It’s a state machine runtime rather than a chain library. You declare state, nodes and edges, and the runtime owns execution — which is what makes checkpointing, resumption and human-in-the-loop possible, because it can pause between any two nodes. In practice I reach for
create_agentfirst since it’s a compiled graph anyway, and drop to an explicitStateGraphwhen the flow has stages rather than a loop. The thing I’d emphasise is that most production agents are a graph with explicit edges and one routing decision — the free-running loop is the exception, not the default.”
Where this connects
Official documentation
These notes are written from the docs, not copied from them — go to the source when you need an exact signature.
- LangGraph docs — graph API
- Persistence — checkpointers and stores
- Interrupts — human-in-the-loop
- Streaming — the stream modes
- create_agent — the prebuilt agent