LangGraph vs LangChain: When a Graph Beats a Chain (and When It Doesn't)

Quick answer
LangGraph and LangChain are not competitors — LangGraph is built by the LangChain team to solve the one problem chains can't: loops. Here's the real architectural difference, the same task written in both, and an honest decision framework for when a stateful graph earns its complexity.
- They're the same ecosystem, not competitors
- What LangChain gives you
- What LangGraph adds
- The core architectural difference
- The same task in both
13 min read · AI & Data
LangGraph vs LangChain is a false versus — LangGraph is built by the LangChain team, on top of the LangChain ecosystem, to solve the one problem chains structurally cannot: loops. If you're framing this as a migration decision or a rivalry, you've already misread the map. The real question is narrower and more useful: when does your workflow stop being a pipeline and start being a state machine?
I've watched teams get this wrong in both directions: bolting LangGraph onto a straight-line RAG pipeline and inheriting graph ceremony three LCEL pipes would have avoided, or duct-taping "agent loops" into chains with recursion and prayer, then wondering why nothing is debuggable. The distinction is architectural, not tribal, and once you see it the choice mostly makes itself.
They're the same ecosystem, not competitors
First, the relationship. LangChain is the original framework: model wrappers, integrations, prompt templates, output parsers, retrievers, and LCEL (LangChain Expression Language) for composing them. LangGraph is a separate library from the same team that reuses all of those components — your ChatAnthropic model, your tools, your retrievers plug straight in — but replaces the control flow layer.
The LangChain team is explicit about this: for anything agentic, their own guidance points you at LangGraph. The legacy LangChain AgentExecutor was replaced by LangGraph's prebuilt create_react_agent, itself since superseded by LangChain 1.x's create_agent — which runs on LangGraph under the hood. So the honest framing is:
- LangChain = the component library and integration layer, plus a linear composition syntax (LCEL)
- LangGraph = a stateful orchestration runtime that uses those components inside an explicit graph
You will very likely use both in the same file. The decision isn't "which framework" — it's "which control flow."
What LangChain gives you
LangChain's enduring value is the integration surface: hundreds of model providers, vector stores, document loaders, and retrievers behind uniform interfaces. Swap Pinecone for pgvector or Claude for GPT and the surrounding code barely moves.
On top of that sits LCEL, which composes components into chains with the pipe operator:
chain = prompt | model | parserAn LCEL chain is essentially a DAG that data flows through once, in one direction. You get batching, streaming, async, retries, and fallbacks for free, and the shape of the computation is fully known before any tokens are generated. RunnableBranch and RunnableParallel give you branches and fan-out, but there is no first-class way to go backwards — no cycles, no "loop until the answer is good enough," no durable state between steps beyond what you thread through by hand.
That's not a flaw. It's the design. A stateless one-way pipeline is the cheapest, most predictable thing you can run in production, and an enormous share of LLM workloads — RAG, summarization, extraction, classification — are exactly that shape.
What LangGraph adds
LangGraph models your application as an explicit state machine: a typed state schema, nodes (functions that read and update state), edges (fixed transitions), and conditional edges (a function inspects state and decides where to go next). Crucially, edges can point backwards — cycles are a first-class citizen, which is precisely what an agent loop is: call model → maybe call tools → feed results back → call model again.
On top of the graph model you get the features that matter in production:
- Checkpointing/persistence — every super-step can be snapshotted to a backend (in-memory, SQLite, Postgres). A crashed or interrupted run resumes from the last checkpoint instead of restarting, and each conversation thread gets durable state keyed by
thread_id. - Human-in-the-loop interrupts —
interrupt()pauses the graph mid-run (say, before an irreversible tool call), persists state, and waits for a human verdict. Approval can arrive seconds or days later; the graph resumes exactly where it stopped. - Streaming of intermediate state — not just final tokens, but state updates from every node as they happen, so your UI can show "searching → reading → drafting" instead of a spinner.
- Time travel — because every step is checkpointed, you can rewind to a prior state, fork it, and replay with modifications. For debugging non-deterministic agents this is worth more than it sounds.
If you've read my piece on AI agents vs agentic AI, the mapping is direct: LCEL is the level-1 workflow (hardcoded DAG, LLM fills in steps); LangGraph is the runtime for levels 2–3, where a model decides the next step and the loop needs supervision, budgets, and an audit trail.
The core architectural difference
Strip everything else away and it's this:
A chain is a stateless pipeline. A graph is a stateful machine with loops.
In a chain, control flow is fixed at authoring time and data flows through once. In a graph, control flow is decided at runtime by inspecting state, the same nodes can execute many times, and the state itself is a durable, inspectable artifact. Everything else — checkpointing, interrupts, multi-agent handoffs — falls out of that one design decision.
The same task in both
Concrete example: answer a question, using a web search tool when needed. First the LCEL version — a single tool-augmented call with a fixed shape:
1from langchain_anthropic import ChatAnthropic
2from langchain_core.prompts import ChatPromptTemplate
3from langchain_core.output_parsers import StrOutputParser
4
5model = ChatAnthropic(model="claude-sonnet-4-5")
6
7prompt = ChatPromptTemplate.from_messages([
8 ("system", "Answer using the provided search results."),
9 ("human", "Results:\n{results}\n\nQuestion: {question}"),
10])
11
12chain = (
13 {"results": lambda x: search(x["question"]), "question": lambda x: x["question"]}
14 | prompt
15 | model
16 | StrOutputParser()
17)
18
19answer = chain.invoke({"question": "What changed in Kubernetes 1.36?"})Clean, fast, trivially testable. But notice what's baked in: search runs exactly once, whether or not it was needed, and if the first results are garbage there is no "search again with a better query." The pipeline cannot reconsider.
Now LangGraph, where the model decides whether and how often to search:
1from langgraph.graph import StateGraph, MessagesState, START, END
2from langgraph.prebuilt import ToolNode, tools_condition
3from langgraph.checkpoint.postgres import PostgresSaver
4
5tools = [search]
6model_with_tools = model.bind_tools(tools)
7
8def agent(state: MessagesState):
9 return {"messages": [model_with_tools.invoke(state["messages"])]}
10
11builder = StateGraph(MessagesState)
12builder.add_node("agent", agent)
13builder.add_node("tools", ToolNode(tools))
14builder.add_edge(START, "agent")
15builder.add_conditional_edges("agent", tools_condition) # tool calls → "tools", else → END
16builder.add_edge("tools", "agent") # the cycle
17
18with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
19 checkpointer.setup() # first run only — creates the tables
20 graph = builder.compile(checkpointer=checkpointer)
21
22 config = {"configurable": {"thread_id": "user-42"}}
23 result = graph.invoke(
24 {"messages": [("user", "What changed in Kubernetes 1.36?")]},
25 config,
26 )Same model, same tool — different control flow. The tools → agent edge is the loop LCEL can't express: the model searches zero, one, or five times until it's satisfied. And because a Postgres checkpointer is attached, the run survives a pod restart, and invoking the same thread_id tomorrow continues the conversation with full state. That last part matters if you're running this on infrastructure you own — a durable graph pairs naturally with the kind of self-hosted stack I covered in deploying LLMs on Kubernetes.
The price is visible too: a state schema, a builder, a checkpointer, a thread model. That's real machinery, and for a task that never loops it's dead weight.
Kubernetes Production Readiness Checklist
The pre-launch checks we run before calling a cluster production-ready — probes, resources, RBAC, upgrades, and backups. Plain Markdown you can commit to your repo.
Free. Instant download. You'll also get the occasional deep-dive from the newsletter — unsubscribe anytime.
When LangChain alone is enough
Reach for plain LCEL when the shape of the computation is known before it runs:
- RAG pipelines. Retrieve → prompt → generate → parse is a straight line. This covers the classic retrieval patterns I walked through in how to build AI tools.
- Extraction, classification, summarization — single-pass transforms, often with structured output.
- Simple tool-calling where one round of tools is acceptable and a failed call can just fail the request.
- Anything you'd describe as "a function that happens to call an LLM." Stateless request/response services scale horizontally with no shared state to manage — operationally, that's a gift. Don't give it up for optionality you haven't needed yet.
A useful smell test: if you can draw your workflow left-to-right without any arrow pointing backwards, LangGraph is buying you nothing but indirection.
When LangGraph earns its complexity
The moment any of these appear in the requirements, the graph pays for itself:
- Cycles. Any genuine agent loop — act, observe, decide again. Also evaluator-optimizer patterns: generate, critique, regenerate until the critique passes.
- Multi-agent orchestration. A supervisor routing between specialist agents, or peer agents handing off to each other, is naturally a graph with the supervisor as a conditional-edge hub. Encoding this in chains means reimplementing a state machine badly, by hand.
- Retry and branch logic that depends on runtime results. "If the SQL query errored, route to the repair node, at most three times, then escalate" is three lines of conditional edge. In a chain it's exception-handling spaghetti.
- Long-running workflows that need to survive interruption. Anything spanning minutes to days — batch enrichment jobs, multi-step provisioning, research tasks — wants checkpointed resume, not restart-from-zero.
- Human approval gates. If a human must sign off before the agent executes a refund, merges a PR, or touches production,
interrupt()plus a durable checkpointer is the entire feature. Building "pause indefinitely and resume with full context" yourself is a distributed-systems project you did not budget for.
For where LangGraph sits against Temporal, Airflow, n8n, and the rest of the orchestration field, see my rundown of AI workflow orchestration tools — the short version is that LangGraph is application-level orchestration, not a scheduler replacement.
Head-to-head
| LangChain (LCEL) | LangGraph | |
|---|---|---|
| Mental model | Pipeline / DAG | State machine / graph |
| Control flow | Fixed at authoring time | Decided at runtime via conditional edges |
| Cycles | No | First-class |
| State | Ephemeral, per-invocation | Explicit schema, durable via checkpointers |
| Resume after crash | No — rerun | Yes — from last checkpoint |
| Human-in-the-loop | DIY around the chain | Built-in interrupt() |
| Streaming | Tokens | Tokens + per-node state updates |
| Multi-agent | Awkward | Native (supervisor, handoffs) |
| Boilerplate | Minimal | Moderate — schema, builder, threads |
| Best fit | RAG, transforms, single-shot tool use | Agents, multi-agent systems, long-running workflows |
Production concerns
Observability. Both integrate with LangSmith via a couple of environment variables, and with agents you want it from day one — a failing agent produces a plausible-looking wrong answer, not a stack trace, and the per-node trace with token counts and tool I/O is how you find where a 40-call run went sideways. The non-negotiable part is step-level tracing, not the vendor; OpenTelemetry-based alternatives work too.
Checkpointer backends. MemorySaver is for tests and notebooks — it dies with the process. SqliteSaver suits single-instance deployments; PostgresSaver is the production default, and it's what you want as soon as you run more than one replica, since any instance can resume any thread. Checkpoints accumulate fast — every super-step on every thread — so plan retention and cleanup like you would for any state store, and keep secrets out of graph state, because state is persisted verbatim.
Versioning graphs. A durable graph creates a problem chains never have: in-flight state written by version N of your graph may be resumed by version N+1. Renaming nodes or reshaping the state schema breaks pending threads mid-flight. Treat the state schema like a database schema — additive changes where possible, and either drain old threads before deploying breaking changes or version the graph and route old threads to the old definition. Teams discover this the week after their first human-approval workflow ships, when a deploy lands while approvals are pending.
The honest alternative: no framework
There's a third option this comparison usually omits: plain Python against the provider SDK. Anthropic's and OpenAI's tool-use loops are not complicated — a while loop that sends messages, executes tool calls, appends results, and stops when the model stops asking is maybe forty lines, and you understand every one of them. No framework abstractions between you and the API, no dependency churn, no debugging through someone else's call stack. Plenty of experienced teams ship exactly this.
What you give up is everything in the production section above: durable checkpoints, resumable interrupts, per-node streaming, and time-travel debugging all become your code. My rule of thumb: one agent, bounded task, no resume requirement — the SDK loop is defensible and arguably cleaner. The moment you need durable pause/resume or multi-agent coordination, you'll end up rebuilding a worse LangGraph, so use the real one.
Frequently Asked Questions
Is LangGraph replacing LangChain?
No. LangGraph replaced LangChain's agent runtime (the legacy AgentExecutor), not LangChain itself. The component layer — models, tools, retrievers, integrations — remains LangChain, and LangGraph consumes those components. The team's own guidance: LCEL for straightforward chains, LangGraph for anything agentic.
Can I use LangGraph without LangChain?
Mostly. LangGraph doesn't depend on the langchain package — though it does pull in the lightweight langchain-core base abstractions — and nodes are plain Python functions that can call any SDK directly. In practice most people use LangChain's model and tool abstractions inside their nodes because conveniences like bind_tools and ToolNode assume them, but it's a choice, not a requirement.
Do chains and graphs mix in one application?
Routinely, and it's often the right architecture. A LangGraph node is just a function, so a node can invoke an entire LCEL chain — a RAG chain as one node inside an agent graph is a common and sensible pattern. Use the graph for control flow, chains for the linear stretches inside it.
Is LangGraph overkill for a simple RAG chatbot?
For the RAG pipeline itself, yes — that's a straight line and LCEL handles it in a few lines. But if the chatbot needs durable multi-turn memory across sessions or restarts, LangGraph's thread-level persistence is the cheapest way to get it, even with a trivial graph of one or two nodes.
Does LangGraph require LangSmith?
No. LangSmith is optional and a separate commercial product (with a free tier). LangGraph runs fully open-source without it, and observability can come from OpenTelemetry-based alternatives. You do want some step-level tracing for any agent in production — which vendor provides it matters less.
The bottom line
Stop asking which library wins. Ask whether your workflow has arrows pointing backwards. If every step flows one way — retrieve, transform, generate — LCEL gives you the cheapest, most predictable production posture available, and adding a graph runtime is self-inflicted complexity. The moment you need cycles, durable state, human approval gates, or multiple agents coordinating, LangGraph is the same ecosystem with the control-flow layer your problem actually requires — and considerably better than the state machine you'd otherwise grow by accident inside a chain.
See also
- AI Agents vs Agentic AI — the autonomy spectrum these two libraries sit on
- Best AI Workflow Orchestration Tools — where LangGraph fits against Temporal, Airflow and friends
- How to Build AI Tools — the RAG patterns that belong in a chain
- Deploy LLMs on Kubernetes — the infrastructure underneath a self-hosted agent stack
Official References
- vLLM documentation — serving, batching and parallelism options
- Scheduling GPUs — device plugins and GPU resource requests
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


