Own the Call Path: Why I Didn't Adopt LangChain

A three-lab verification gate was quietly running on two. No error, normal latency, plausible output. Why that incident decided the framework question.

Own the Call Path: Why I Didn't Adopt LangChain

Someone asked me last week whether I should be using LangChain. It is a fair question. I run about ninety Cloudflare Workers, most of them talking to language models, and I have written my own provider abstraction, my own fallback logic, my own retrieval layer. That is either principled engineering or a spectacular case of not-invented-here — and from the outside, those look identical.

As designed, and as it actually ran: a three-lab gate operating on two, with no error raised.
As designed, and as it actually ran: a three-lab gate operating on two, with no error raised.

So here is the actual reasoning, including the part where LangChain is right and I am giving something up.

What LangChain is now, fairly

It is not the 2023 version people still argue about. LangChain 1.0 and LangGraph 1.0 shipped together in October 2025, and agents now run on the LangGraph runtime — which resolved most of the "black box" criticism, because you can stay at the high-level API or drop down to the graph. LCEL made composition readable. There are over a thousand integrations. LangSmith, the observability layer, is genuinely well-regarded by teams running it at scale.

It is a mature library with real production deployments. My reasons for not adopting it are about my situation, not its quality.

One: I had already built the eighty percent

Strip LangChain down to what people actually use and you get provider abstraction, retry and fallback, output parsing, and a retriever interface.

My callChain does provider abstraction with cross-provider fallback and validation-retry — try zai, fall through to OpenAI, fall through to Meta, and re-prompt once on a schema violation. That is more than the framework gives me natively. getSecret() is my credential layer, resolving KV then vault. corpusGround() is my retriever, reaching a sibling worker over a service binding. The bounded reformat retry is my output parser.

Adopting LangChain would mean swapping working code for equivalent code, plus a dependency tree. The migration is pure cost.

Two: the abstraction would have hidden the bug I most needed to see

This is the argument that actually decided it.

GLM-5.2 became a reasoning model. Given a small token budget it spends the entire allowance on hidden reasoning and returns an empty answer. My chat calls were set to 1,200 tokens. So GLM — the model I had deliberately chosen to compose, because it is a different lab from both of my judges — was silently returning nothing and falling through to the fallback. Every time.

Nothing errored. Latency looked fine. The output looked fine, because a capable fallback was answering. What was quietly broken was the architecture: I had designed a three-lab pipeline and was unknowingly running a two-lab one.

I found it because I own the call path. I could see which model actually answered. Behind a ChatOpenAI-style wrapper, that symptom is "provider returned empty" — and attributing it means reading framework source. The fix was a per-model token floor: 3,072 for glm-5.2, raising a ceiling only, never lowering a caller's request.

The most consistent criticism of LangChain in the field is that its abstractions make debugging harder. I did not want to test that on the layer where my correctness guarantees live.

Three: my verification gate has no equivalent in it

My inferred findings are not accepted because a model produced them. One lab composes, two independent labs judge, and a finding is accepted only if judge A says SUPPORTED and judge B does not say CONTRADICTED. A judge that fails to answer cannot supply consent — it fails closed. Withheld candidates render in the interface, so the gate is inspectable rather than silent.

That is not a LangChain concept. I would be writing it as custom code inside the framework, paying the abstraction cost without collecting the benefit.

Four: infrastructure facts do not abstract

My code encodes things that took incidents to learn.

Same-account worker-to-worker calls over a public workers.dev URL are blocked by Cloudflare with error 1042, so sibling workers must be reached over service bindings. A deploy that omits a binding drops it silently, so every deploy re-declares all of them. Access sits at the edge, so a front worker fetching a gated hostname receives a login page rather than the app. Workers cap at one megabyte uncompressed, and one of my bundles is already 195KB.

None of that is portable, and no framework carries it for me.

What the framework offers against what already existed — including the row where it wins outright.
What the framework offers against what already existed — including the row where it wins outright.

The honest gap: observability

LangSmith is the strongest thing in that ecosystem, and it targets a real hole in my estate.

Right now I cannot answer, systematically: how often does a model fall through to fallback? What is p95 on the judge calls? What did the model actually see on the run that produced a bad finding? I found the GLM bug by reasoning about a symptom, not by looking at a dashboard — which means I would not have found a subtler one.

That is a genuine deficiency and I am not going to dress it up. But I can close it without adopting the framework: I am already on Cloudflare, and AI Gateway puts logging, analytics, and caching in front of every provider call as a configuration change rather than a rewrite.

When I would reconsider

If I build something with real cycles — branching, retry loops, human approval mid-execution, resumable state — LangGraph is the right abstraction and hand-rolling a state machine would be the wrong call.

My pipelines are linear: parse, run deterministic rules, generate, judge, return. My decision queue looks like human-in-the-loop but is not; it is a separate turn, not a suspended graph. The day that changes, the calculus changes.

The counter-argument I take seriously

Custom orchestration is maintenance surface that only I understand. If any of this is ever diligenced, "standard LangChain stack" reads as more legible than "bespoke."

I do not think it outweighs the rest — the verification architecture is the differentiator, not a liability, and explicit code is better conformity evidence than a generic abstraction when someone asks which model contributed what. But it is a real cost and I would rather name it than pretend it away.

The test, stated so it transfers.
The test, stated so it transfers.

The principle

Adopt a framework when it removes work you understand. Refuse it when it hides work you need to see.

The GLM bug is the whole argument in one incident. A framework would have saved me some code and cost me the visibility that caught it. For a system whose entire value proposition is verified output, that is a bad trade.

Yours may differ. That is the point — this is a fit question, not a quality question, and anyone who answers it for you without asking what you have already built is selling something.

The silent demotion, in twenty seconds.

The silent demotion, in twenty seconds.