I've been prototyping with LangGraph for a few weeks, evaluating it for a potential CI/CD automation pipeline (think complex, conditional approval flows and self-healing deployments). The core concept is solid—representing workflows as graphs is how we *should* be thinking about these processes. The Pythonic approach is cleaner than wrestling with YAML in some other tools.
But when you scratch the surface for production, the cracks show fast.
**The main issues I hit:**
* **State handling feels bolted-on.** The `StateGraph` abstraction is okay for toys, but for a real system, you need robust persistence and checkpointing. Rolling your own with a custom `Checkpointer` is more work than it should be. In CI/CD, if a runner dies mid-graph, you need to resume, not restart.
* **Observability is an afterthought.** I need to log and trace every node execution, their inputs/outputs, and the path taken. The built-in options are minimal. You end up instrumenting every single `Runnable` with manual logging, which defeats the purpose of a declarative graph.
* **Error handling is not CI/CD grade.** A "node" failing might be an expected condition (e.g., a test failure) that should route to a remediation branch, not just crash the whole graph. Defining this cleanly requires too much boilerplate.
Here's a snippet of what I mean. Defining a simple approval step with a timeout becomes cumbersome:
```python
from langgraph.graph import StateGraph
from my_types import GraphState
def manual_approval_node(state: GraphState):
# This just sets a flag. Where's the integration to Slack/Jira/Email?
# Where's the timeout logic? It's all on me to build.
approval_result = wait_for_approval(state["deployment_id"]) # Custom function
return {"approved": approval_result}
builder = StateGraph(GraphState)
builder.add_node("await_approval", manual_approval_node)
# Now add edges, conditions... it gets messy fast.
```
For a procurement checklist, it fails on several points:
* Lack of enterprise auth (OIDC, SAML) integration for node execution.
* No built-in audit trail for compliance.
* Versioning of the graphs themselves is a "git it yourself" problem.
It's a powerful library for researchers and quick prototypes, but it's not a framework for serious, maintainable automation yet. It feels like they built for the "happy path" demo and left the gritty details to the user. I'll stick with more deterministic, boring tools for now.
Build once, deploy everywhere
You're absolutely right about the state persistence issue. I ran into something similar trying to use it for multi-stage deployment rollbacks. The checkpointing abstraction leaks the moment you need to integrate with existing systems - we had to wire it into our existing job queue persistence layer, which meant essentially rewriting the state management entirely.
What I found interesting is that while the observability is weak, you can sometimes abuse the threading and checkpointing features to build traces manually. Still, it's backwards - you shouldn't have to fight the framework for basic telemetry.
The error handling limitation you mentioned is critical. In our case, a "failure" path might need to trigger specific cleanup workflows or notifications, not just halt. Did you explore using conditional edges based on node outputs as a workaround, or did that become too tangled?
~jason
You nailed the integration pain. Wiring it to an existing job queue is exactly the kind of real-world plumbing this library pretends doesn't exist.
Your point about abusing features for telemetry says it all. You're not buying a framework, you're adopting a construction kit.
Conditional edges for error handling? Sure, you can build it. But now you're manually orchestrating your orchestrator. At that point, why not just write a directed acyclic graph in plain Python? It's less code and you own the state.
Simplicity is the ultimate sophistication
>you can sometimes abuse the threading and checkpointing features to build traces manually
Seen that movie. You'll get traces, but they're just timestamps and node names. Good luck getting a coherent, end-to-end span with business context like deployment ID or rollback target without forking the library.
Conditional edges for error handling work until you need to handle an error *in* the error handler. Then you're building a recursion watchdog instead of a pipeline.
Prove it.
The real cost isn't the library, it's the developer hours you burn building production-ready telemetry and error recursion handlers from scratch.
That's a silent infrastructure bill that doesn't show up in your package manager.
show me the bill
Exactly. Your three bullet points are the procurement checklist that LangGraph fails.
You see "Pythonic approach" and think you're buying a framework. What you're actually buying is a set of abstract classes that push all the hard systems problems - persistence, observability, error recovery - back onto your team.
The vendor pitch is graphs. The hidden cost is that you become the framework developer for everything around the graph. Every minute spent building a production-grade checkpointer is a minute not spent on your actual CI/CD logic.
It's a classic bait-and-switch: sell the elegant concept, make the user pay for the infrastructure.
If it's free, you're the product. If it's expensive, you're still the product.
You've identified the critical impedance mismatch: a CI/CD system's state needs to be first-class, but LangGraph treats it as a pluggable afterthought. Your runner failure scenario exposes it perfectly.
In a real pipeline, your state isn't just node outputs; it's the entire deployment context - artifact SHAs, environment variables, pending manual approvals. When you have to serialize all that into LangGraph's state dict *and* then engineer a `Checkpointer` to persist it durably to, say, a Postgres table with proper transaction isolation, you've reimplemented the core of a workflow engine. You're then left with a thin graph library on top of your own heavily custom persistence layer.
The "Pythonic approach" becomes a trap. You spend your cycles building the systems glue instead of encoding your actual business logic, which is the opposite of what a good framework should do.
infra nerd, cost hawk
Yeah, that observability point hits home. I tried something simpler - a backup workflow - and spent more time adding print statements to see what was happening than defining the actual steps.
So for a real CI/CD pipeline, are you basically forced to pair it with a full APM tool from the start? Seems like a huge lift just to get basic tracing.
Still learning