Less framework, more code — and somehow less code.
Spoiler: I deleted LangChain from a production project last month and the codebase got shorter.
The Setup
LangChain has 47 ways to do one thing and the docs keep deprecating themselves. Once you write a non-trivial agent, you're reading the source anyway. So skip the middle step — write the source yourself, in 200 lines, with types you understand.
{`pip install litellm pydantic
# That's it. That's the dependency list.`}The Money Pattern
A typed client, a chain runner, and a tool dispatcher. Three primitives, zero magic. You can read the whole thing in a coffee break and your IDE actually knows what's going on.
{`from pydantic import BaseModel
from litellm import completion
class Step(BaseModel):
role: str
content: str
class Chain:
def __init__(self, model: str, system: str):
self.model = model
self.history: list[Step] = [Step(role="system", content=system)]
def run(self, user: str) -> str:
self.history.append(Step(role="user", content=user))
res = completion(
model=self.model,
messages=[s.model_dump() for s in self.history],
)
out = res.choices[0].message.content
self.history.append(Step(role="assistant", content=out))
return out
bot = Chain("anthropic/claude-sonnet-4-5", "You triage hail claims.")
print(bot.run("Claim 4821: roof, two skylights cracked."))
print(bot.run("What did I just tell you?"))`}The Catch
You lose the integrations. No prebuilt 47-class retriever, no off-the-shelf SQL agent, no vector store adapter zoo. For 80% of real products, those abstractions were costing you more than they saved. For the other 20%, fine, use the framework — just know which one you are.
The Verdict
Frameworks are great until they aren't, and LangChain hit "aren't" two years ago. Write the 200 lines. Own the abstraction. Sleep better.