Lesson 1 of 6

Your pipeline can't go backward

Here's a question worth sitting with before you write a single line of agent code: when your system discovers something mid-run that changes what it should do next, where does that discovery go? In a chain — a fixed sequence of steps wired together once, at definition time — the answer is nowhere. The chain doesn't have a slot for "wait, go back and try that differently." It has a slot for step 4, which runs after step 3, which runs after step 2, forever, no matter what step 2 finds out.

Most retrieval-then-generate pipelines are fine with that constraint. Retrieve, generate, done — there's no reason to revisit an earlier step, because nothing about the task is conditional on what happens later. But an agent that calls a tool, checks whether the call succeeded, and needs to retry with different arguments if it didn't — that task is conditional by nature. So is a drafting loop that critiques its own output and sends it back for revision. So is anything that keeps taking actions until some goal-completion condition, unknowable in advance, finally becomes true.

LangGraph exists because "conditional by nature" describes most of what agents actually do, and a linear chain has no mechanism for it. This lesson is about the model LangGraph uses instead — nodes, edges, and a shared state object — and why that model, unlike a chain, has a natural place for a decision made in step 4 to change what step 2 does next.

Chains vs. Graphs

Why a fixed sequence stops working

A chain — LCEL, a simple prompt pipeline, any sequence of steps stitched together — is compiled once and runs the same shape every time. Step order is a property of how you wrote the code, not something the running system can revise. That's a feature, not an oversight: fixed sequences are easy to reason about, easy to test, and easy to trace when something goes wrong, because there's exactly one path through the code and it's the same path every time.

The trouble starts when a step's outcome needs to change what happens later in a way you didn't hardcode. A tool call fails and the right move is to retry with adjusted arguments — but which arguments depends on how it failed, information that doesn't exist until the call has already happened. A generated draft doesn't pass a quality check and needs another pass — but "another pass" means re-entering a step that already ran, with new information the first pass didn't have. Neither of these is exotic; they're the normal shape of what an agent does once it's allowed to act, observe the result, and decide again.

A chain can't express either case, because expressing them requires a path back to an earlier step, chosen at runtime, based on data the runtime itself produced. That's not a limitation you work around with cleverer prompting — it's a structural gap between what a fixed sequence can represent and what agentic behavior actually requires.

The catch: Not every agentic-sounding task needs this. If your task really is retrieve-then-generate, or classify-then-route-once, a chain is simpler, easier to debug, and has none of the overhead a graph framework adds. The gap only matters once you actually need to revisit a step based on what a later step discovered — don't reach for a graph because the word "agent" is in the requirements doc.

Nodes, Edges, State

A shared object that flows through the graph

LangGraph closes that gap with three primitives. A node is a function that receives the current state and returns a partial update to it — one narrow job, read what you need, do one thing, hand back a delta. An edge decides which node runs next. And state is the single object that flows through the whole graph, the shared context every node reads from and writes to, defined up front as a schema so every node agrees on its shape before the graph ever runs.

Notice what's different from a chain here: in a chain, "what happens next" is baked into the code's structure — function A calls function B calls function C. In a graph, "what happens next" is a first-class question the edges answer, separately from what any individual node does. That separation is what makes the earlier retry case expressible at all — the edge leaving the tool-call node can ask "did this succeed?" and route to either the next step or back to a retry, and neither the tool-call node nor the step after it needs to know that decision is even being made.

You build the graph by registering nodes and wiring edges between them, then compile it into something runnable. Once compiled, the graph's structure — not any single function's internals — is where your control flow actually lives. That's the shift worth internalizing before the next lesson, which is entirely about what edges can do once they're allowed to point backward.

Reducers

How concurrent writes get merged

Shared, mutable state raises an obvious question: what happens when two nodes both want to update it? A reducer is the answer — a function attached to a state key that says how a new value should combine with the value already there. The default behavior is the boring one: overwrite, last write wins, exactly what a chain would do anyway since nothing in a chain ever writes concurrently in the first place.

But you can attach a different reducer to a key that needs different behavior — a list of messages, for instance, where each node's output should append to the running history rather than replace it outright. Chat state specifically uses a reducer built for exactly this, matching updates to existing messages by identity so that an edit to an earlier message updates it in place instead of appending a duplicate. The point isn't the specific reducer — it's that state merging is a declared policy per key, not an accident of execution order.

This is what makes parallel branches safe to write at all. Without a merge policy, two nodes contributing to the same list would simply clobber each other, and the only fix would be forcing everything through a strict, one-at-a-time sequence — which is just a chain wearing a graph's syntax. Reducers are what let a graph actually behave like a graph: multiple things happening, writing to shared state, without stepping on each other.

Under the hood: LangGraph's execution engine is a message-passing system modeled on Google's Pregel — it runs in discrete steps, activating whichever nodes have pending input each round, and halts once nothing is left to activate. That's worth knowing conceptually because it's why cycles and parallel branches fall out of the same mechanism rather than being bolted on as separate features — a detail the next lesson leans on directly.

Structure carries the control flow now

The shift this lesson asked you to make is small to state and large to internalize: control flow moves out of function-call order and into the graph's structure — its edges — with a shared state object, merged through declared reducers, as the medium everything flows through. That's the entire reason a graph can express what a chain can't: a decision made in one node can change where the graph goes next, because "where next" was never fixed at definition time to begin with.

Everything else in this course builds on that one structural fact. Persistence works because state is a well-defined object that can be snapshotted. Human-in-the-loop works because execution can pause at any node without losing that snapshot. Multi-agent patterns work because a "sub-agent" is just another node, wired in with the same edges you'd use for anything else.

In the next lesson, we'll pick up the piece of this model we've only gestured at so far — edges that point backward. That's the entire mechanism behind cycles, and it's what lets a graph do the thing this lesson opened with: let a decision made in step 4 send execution back to step 2.

Introduction
0:00
10:00