LangChain vs. LangGraph: What Actually Changes When You Move from Chains to Graphs
You've built a few LLM apps with LangChain. Retrieve some docs, summarize them, answer a question. Works fine. Then someone mentions LangGraph, and you're wondering if you should migrate. Let's cut through the confusion.
The difference isn't philosophical. It's structural. LangChain works in straight lines. LangGraph works in loops and branches. That's it. Everything else follows from this.
Table of Contents
- The Core Structural Difference
- When a Chain Is Actually Enough
- State Management: The Real Problem
- Building the Same Thing in Both Frameworks
- Production Tradeoffs Nobody Talks About
- When You Actually Need to Switch
The Core Structural Difference
LangChain uses chains. A chain is a directed acyclic graph, or DAG. That means data flows in one direction, no loops, no going back. Think of it like a factory assembly line. Part goes in, moves through stations, comes out finished.
Analogy: LangChain is a water slide. You start at the top, gravity pulls you down through each section, and you end up in the pool. You can't climb back up mid-slide.
LangGraph uses state graphs. These allow cycles. You can revisit previous nodes. You can branch based on conditions and loop until something is true. It's a state machine that holds context across iterations.
This matters when your workflow needs to loop. When an agent needs to try something, check the result, and try again if it failed. When you need memory that persists across multiple decision points.
When a Chain Is Actually Enough
Most LLM apps don't need loops. If your workflow is retrieve, process, respond, a chain works fine. You don't need the complexity of state graphs.
Use LangChain when:
- Your steps always happen in the same order
- You don't need to retry or backtrack
- State is just passed along the chain, not modified conditionally
- You're building RAG pipelines, summarization, or simple Q&A
Real example: Document analysis tool. User uploads PDF, you chunk it, embed it, store vectors, run a query, return results. Linear. No branching logic. LangChain handles this perfectly.
| Workflow Type | Best Framework | Why |
|---|---|---|
| RAG pipeline | LangChain | Straight line: retrieve, rank, generate |
| Document summarization | LangChain | Sequential processing, no loops |
| Multi-step agent with retries | LangGraph | Needs loops and conditional branching |
| Conversational memory | LangGraph | State persists and changes over turns |
| Tool-calling agent | LangGraph | May need to try tools multiple times |
State Management: The Real Problem
This is where chains break down. In LangChain, state flows through the chain. Each component gets input, produces output, passes it forward. If you need to track complex state across multiple interactions, you're fighting the framework.
LangGraph has explicit state management. You define a state schema. Nodes read from state and write to state. The graph maintains this state across all node executions.
Consider a coding agent. It needs to:
- Read the task
- Write code
- Run tests
- If tests fail, revise code and go back to step 3
- Return final code when tests pass
In LangChain, you'd hack this with custom logic and external state tracking. In LangGraph, you define the loop in the graph structure. The state holds the code, test results, and iteration count.
# LangGraph state example
class AgentState(TypedDict):
task: str
code: str
test_results: dict
attempts: int
finished: bool
The graph nodes modify this state. The routing logic checks test_results and decides whether to loop back or finish.
Building the Same Thing in Both Frameworks
Let's compare code for a simple research agent that searches the web and summarizes findings.
LangChain version:
from langchain import PromptTemplate, LLMChain
from langchain.agents import initialize_agent, Tool
search_tool = Tool(name="Search", func=search_function)
chain = initialize_agent(
tools=[search_tool],
llm=llm,
agent="zero-shot-react-description"
)
result = chain.run("Research topic")
It works, but if you need the agent to search, evaluate results, and search again with refined queries, you're manually managing that logic outside the framework.
LangGraph version:
from langgraph.graph import StateGraph, END
workflow = StateGraph(ResearchState)
workflow.add_node("search", search_node)
workflow.add_node("evaluate", evaluate_node)
workflow.add_node("refine", refine_query_node)
workflow.add_edge("search", "evaluate")
workflow.add_conditional_edges(
"evaluate",
route_decision,
{"continue": "refine", "finish": END}
)
workflow.add_edge("refine", "search")
The loop is explicit. The state tracks queries, results, and quality scores. The route_decision function decides whether to keep searching or finish.
Production Tradeoffs Nobody Talks About
Debugging: LangChain chains are easier to debug initially because they're linear. You can print output at each step. LangGraph requires LangSmith or similar tooling to trace state changes across graph executions.
Performance: Chains are faster for simple workflows. Graphs add overhead with state management and routing logic. If you don't need the flexibility, you're paying a cost for nothing.
Team learning curve: New developers understand chains faster. Graphs require understanding state machines and conditional routing. Budget time for onboarding.
Testing: Testing chains means testing each component and the sequence. Testing graphs means testing state transitions and edge conditions. More complex, but more powerful.
| Aspect | LangChain | LangGraph |
|---|---|---|
| Learning curve | Easier | Steeper |
| Debugging simplicity | Print statements work | Needs tracing tools |
| Loop support | Manual workarounds | Native |
| State management | Implicit, passed along | Explicit, typed |
| Performance overhead | Lower | Higher |
| Flexibility | Limited to DAG | Full state machine |
When You Actually Need to Switch
You need LangGraph when:
-
Your agent needs to retry actions. Code generation with test validation. API calls with fallback strategies. Research with iterative refinement.
-
State evolves based on conditions. Not just accumulating data, but changing strategy based on intermediate results.
-
You have multiple agent personas or modes. A graph can route between different specialized nodes based on task type.
-
Human-in-the-loop workflows. User feedback can route back to earlier nodes for revision.
-
You're hitting the limits of chain composition. When you're writing custom logic to manage branching and loops, you're reimplementing a state graph.
You don't need LangGraph when your workflow is fundamentally linear, even if it has multiple steps. Adding graph complexity to a simple pipeline is overengineering.
The Honest Conclusion
Start with LangChain. Build the simple version. If you never need loops or complex state management, stay there. Your future self will thank you for avoiding unnecessary complexity.
Move to LangGraph when you can't implement your logic cleanly in a chain. When you're writing workarounds for state tracking or retry logic, that's the signal.
Both frameworks come from the same team. LangSmith works with both for tracing and debugging. You're not locked in. You can migrate incrementally, moving only the parts that need graph structure.
The real question isn't which framework is better. It's whether your problem needs a straight line or a state machine. Answer that honestly, and the choice becomes obvious.