Skip to content
Writing
3 min readAI systems

LangChain in Production: What the Tutorials Leave Out

Where LangChain is worth using, where it gets in the way, and the defaults that stop our AI features failing silently: timeouts, validation, persistent state, tracing and pinned versions.

LangChain tutorials tend to stop at a tidy script that queries a PDF. You think you'll ship it by Friday, and a few weeks later you're working out why a chain returns an empty string with no error.

I've shipped LangChain-based features at WebNoodle and at Entropy Labs, and we still use it for Obelisk's agents. This post covers where I've found it worth the dependency and what I now do by default when I use it.

When to use it

LangChain is worth it when there's real orchestration: several model calls, tools, branching, approvals, state that has to survive a pause. The agent and middleware layer handles a lot of plumbing you'd otherwise write badly yourself, and the tracing integration is good.

For "summarize this text", a direct SDK call is shorter, easier to debug and one less dependency to upgrade. At Entropy we use the framework for agent workflows and the provider SDK for simple completions.

Timeouts and retries

A model call without a timeout will eventually hang a worker. Set one on every model you construct, and put retries on the calls that can safely be repeated:

model = init_chat_model(MODEL_NAME, timeout=30, max_retries=2)

Be careful what "retry" means once tools are involved. Retrying a timeout is fine. Retrying a tool call that sent an email means sending it twice. In agents, retries belong in middleware that knows which errors are transient; I wrote about that in agent middleware.

Checking prompts and results

The worst LangChain bug I shipped didn't raise anything. A prompt template rendered an empty message list, the model returned nothing, and the chain reported success. The war stories have the details. Since then, anything important gets checked on both sides: before the call, that the rendered prompt isn't empty, and after it, that the result is non-empty and parses into what you expected. Structured output with a schema does most of the second part for you.

Conversation state

The old in-memory conversation helpers were convenient and wrong for production. State vanished on restart, grew without limit and wasn't safe across concurrent requests. They've since moved to the legacy package in LangChain 1.0, which says enough.

Persist state somewhere that outlives a deploy. For agents that means a checkpointer backed by PostgreSQL, keyed by a thread ID, so a conversation can pause for a human approval and pick up hours later. For plain chat history, Redis with an expiry is fine. Either way, you get to decide how long history lives and when it's trimmed.

Tracing

Turn tracing on before the first user, and record every call with its latency, token counts, inputs and outputs. LangSmith is the easy path. We also send our own structured events with a request ID, so a trace, a log line and a user's complaint can be matched to each other. Alert on spend too, because a tool loop that nobody caps will find your budget before you do.

Versions

LangChain moves quickly: import paths change, classes move between packages and defaults shift. Pin exact versions, upgrade on purpose, read the release notes, and keep a small smoke test that runs a real chain end to end, so an upgrade breaks in CI instead of in production.

Next essayFree Web Dev Platforms Worth Your TimeA practical shortlist of free web development learning platforms and how to use them without getting stuck in tutorials.