← All posts

LangChain is mostly glue. That's fine.

7 min read
LangChainPythonLLM

Everyone has an opinion on LangChain. Half the timeline says it’s essential. The other half says delete it and call the API yourself. I bounced between both for a while.

My current take is boring: it’s glue. Good glue when you need it. Annoying when you pretend it’s an architecture.

What I thought it was

I opened the docs expecting an “AI framework” that would hand me agents, memory, RAG, and a career. What I got was a pile of wrappers: chat models, prompt templates, output parsers, vector store adapters, tool schemas.

That felt like a letdown until I shipped something that talked to two providers and a local Ollama box. Then the wrappers earned rent.

The parts I actually reach for

Chat model interface. invoke / stream with roughly the same shape across providers. When product wants “try Claude for this path,” I change a constructor, not twelve call sites.

Prompt templates. String concat works until someone adds a variable and breaks the JSON example in the middle. Templates are dumb and I like dumb here.

Output parsers / structured output. Free-text is for humans. Programs want fields. Forcing a schema at the boundary saves the “wait, did it say yes or did it say maybe yes” debugging session.

Tool definitions. JSON schemas the model can request. Still your job to execute them. LangChain doesn’t run your database for you, which is good, because I don’t want a framework holding my prod credentials with opinions.

Document loaders + text splitters. For RAG prototypes. Not sacred. I’ve swapped them out when they fought a weird PDF.

The parts I ignore (for now)

Giant prebuilt chains with names that sound like product features. Deep agent abstractions I don’t understand well enough to debug. Anything that hides the messages array so thoroughly I can’t print it when things go weird.

If I can’t sketch the message flow on paper, I don’t want a helper doing it “for me.”

A minimal shape that stopped feeling cursed

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

class TicketRoute(BaseModel):
    team: str = Field(description="billing | eng | support")
    urgency: int = Field(ge=1, le=5)

llm = ChatOpenAI(model="gpt-4o-mini").with_structured_output(TicketRoute)

prompt = ChatPromptTemplate.from_messages([
    ("system", "Route the ticket. Be conservative on urgency."),
    ("human", "{ticket}"),
])

chain = prompt | llm
result = chain.invoke({"ticket": "Invoice double-charged, need refund today"})
print(result.team, result.urgency)

That’s most of my LangChain usage: prompt in, structured thing out. LCEL (|) is fine. I don’t build cathedrals with it.

When I skip LangChain entirely

One-off scripts. Tiny eval harnesses. Places where the official SDK is clearer and I’m not swapping providers. Calling HTTP directly is not a moral failure.

Also: if the team already standardized on another stack, I don’t drag LangChain in for vibes.

Agents made this confusing

“LangChain agents” and “LangGraph agents” and blog posts from 2023 all blur together. My rule of thumb now:

  • linear-ish steps, light branching → plain code or a small chain
  • real loops, tool retries, human approval → LangGraph (or your own state machine)
  • LangChain still supplies the model/tool adapters either way

I wrote more about the graph side here.

Honest summary

LangChain didn’t make me an AI engineer. It saved me from rewriting the same adapter layer and from pretending raw string prompts were an interface.

Use the 20% that removes friction. Delete the rest from your mental model. Frameworks are optional. Clear message flows and validated outputs aren’t.

got thoughts?

Let's talk

© 2026 Prashansa Soni