Do You Need an AI Agent Framework? LangChain.js vs Plain TypeScript
A framework is a purchase, not a default. How to decide between LangChain.js-style frameworks and a plain TypeScript loop, and how to migrate later.
On this page

Every few weeks someone asks me which agent framework they should use, and the honest first answer is a question back: which feature are you buying? A framework is a purchase. It costs you a learning curve, a layer of abstraction between you and the model, and a dependency whose design decisions become yours. Sometimes that is a good deal. It is a bad default. This post is how I decide, what each side really gives you, and how to move from one to the other without a rewrite.
The short answer
You need an agent framework when you can name two things: the specific feature it gives you, and the week in your project when you would feel its absence. "Durable state, because our runs wait two days for a human approval and the server deploys every afternoon" is a reason. "It seems like the standard way" is not.
If you cannot name that feature and that week, start with a plain TypeScript loop. I wrote one in full in the 200-line TypeScript harness post, and it covers one agent with a handful of tools, turn and cost limits, a repeated-call guard and an approval gate. Most agents I have built never needed more. The ones that did were easy to migrate because I knew exactly which part had started to hurt.
A framework is a purchase, not a default
Treat the decision the way you would treat buying a database or a queue. You would not add a message broker to a project because message brokers are popular. You would add one when you had work that must outlive a request and survive a crash. Agent frameworks deserve the same discipline.
The price is not money. It is:
- Concepts. Chains, runnables, graphs, nodes, edges, state reducers, checkpointers, callbacks. Each one is reasonable. Together they are a vocabulary your whole team must learn before they can read the agent.
- Indirection. When the model calls the wrong tool, you want to see the exact messages it was sent. In a framework those messages are assembled for you, sometimes from prompt templates and memory components you did not write. Finding the line that caused the problem takes longer.
- Coupling. Your tools, prompts and state shapes start to take the framework's form. Leaving later costs more than not arriving.
- Upgrade churn. Fast-moving libraries change APIs. You will spend some weeks updating code that worked.
Against that price, put the feature. If the feature is worth more than the price this quarter, buy it.
What a framework actually gives you
Strip away the marketing and frameworks in the LangChain.js and LangGraph family offer four things worth paying for.
Durable state. A graph framework can checkpoint the state of a run after each step to a store such as a database. If the process dies, or the run pauses for hours waiting on a person, it can resume from the last checkpoint instead of starting over. This is the single most valuable thing a framework does, and the hardest to build well yourself. Writing state to a table is easy. Making resume correct, so a tool with side effects is not executed twice after a crash, is the hard part.
Graph routing. When work moves between several agents or stages, a graph makes the routes explicit: this node can hand off to these nodes, under these conditions. You can draw it, test each edge, and reason about loops. In a plain loop, multi-agent routing tends to become nested conditionals and ad hoc message passing.
An ecosystem of integrations. Document loaders, vector store adapters, retrievers, provider adapters, prebuilt tools. If your agent needs to read from a dozen sources, someone else has written and maintained most of the glue. That is real saved time, as long as the integrations you need are the ones that exist and are well kept.
Tracing. Many frameworks emit structured events for every model call and tool call, and plug into tracing products that show a run as a tree you can click through. When you have many runs a day and need to answer "why did this one go wrong?", a good trace view is worth a lot.
What a plain loop gives you
A plain loop gives you fewer features and more control.
Readability. The whole agent fits in one file you can read top to bottom. The messages the model receives are the messages in your array. There is nothing between you and the request.
Testability. A loop with the model behind one interface is easy to test with a stub that returns scripted tool calls. You can prove that the turn limit fires, that a blocked write stays blocked, that a malformed argument is rejected, all without an API key. Frameworks can be tested too, but you are often testing through their abstractions.
No abstraction to debug. When something breaks, the bug is in your code or in the model's output. It is never in a callback ordering you did not know about, or in a default that changed in a minor release.
Cost you can see. Limits are plain numbers in your code, checked where you can see them. If cost matters to you, and it should, read how to control the cost of an AI agent for the controls I put in every loop.
The trade-off is honest: you will build some things yourself. Retries, logging, a persistence story if you need one. For a single agent that runs for minutes, that list is short.
A decision walk-through
These are the questions I ask, in order. They are the same dimensions the Agent Framework Picker scores, so if you want a quick verdict you can answer them there. Here is the reasoning behind each.
1. How long does one run last?
If a run lasts seconds or minutes, it lives inside one process and one request. If it fails, you rerun it. A plain loop is ideal.
If a run lasts hours, you start to care about deploys and crashes in the middle. You may still get away with a loop plus a job queue that retries the whole run.
If a run lasts days, with waits in between, a loop held in memory is the wrong tool. You need state stored outside the process and a way to resume. This is the strongest single signal for a framework or a durable workflow runtime.
2. How many agents are there?
One agent calling tools: a loop. Two agents with a simple handoff, such as a planner that produces a list and a worker that executes it: still a loop, or two loops in sequence. Several agents sharing state and routing work between each other based on results: now routing is its own system, and a graph framework earns its keep.
Be suspicious of multi-agent designs that are really one agent with more tools. Every handoff between agents loses context, and one agent with good tools often beats three agents passing notes.
3. Must a run survive a restart?
Ask it plainly: if the server restarts halfway through, what happens? If "we rerun it" is acceptable, you do not need durable state. If the run has already sent an email, charged a card, or waited a day for approval, rerunning is not acceptable, and you need checkpoints plus idempotent tools. A framework gives you the checkpoints. You still need to make the tools idempotent.
4. What does your team already know?
This one cuts both ways. A team that already ships with a graph framework will move faster in it than in a hand-written loop nobody else has seen. A team with no agent experience will learn more, faster, by writing a loop, and will choose a framework better afterwards. If you already use an SDK for model calls and streaming, the natural next step is to keep that SDK and write your own loop on top, which is often the best middle ground.
5. Do you need to inspect every run?
If logs of tokens, tool calls and outcomes per run are enough, a loop with structured logging covers it. If you need to click into any of thousands of runs and see the full tree of steps, framework tracing integrations save you building a viewer. You can also send traces from a plain loop to a tracing tool yourself. It is more wiring, not a different capability.
Reading the answers
If most answers point to short runs, one agent and no restart requirement, write the loop. If durability or multi-agent routing is a day-one requirement, start with the framework and accept its cost on purpose. If you land in between, use an SDK for the plumbing and keep the loop yours. Notice that no single question decides it except restart survival for long runs. That one is close to decisive.
The migration path: from loop to framework
The best reason to start plain is that it keeps the later move cheap, if you shape the code for it from the start.
- Keep tools as plain data. Each tool is an object with a name, a description, a JSON schema for its arguments and an async handler. Every framework I have used can wrap a function with a schema as a tool. Your handlers move over unchanged.
- Keep the model behind one interface. One
complete()method that takes messages and tool definitions and returns text, tool calls and usage. When you migrate, this adapter is thrown away or becomes a thin shim. - Keep limits and approvals as functions. A budget check, a repeated-call guard, an approval callback. In a graph, these become guard conditions on edges or a node that interrupts for a human. The logic stays the same.
- Make side-effect tools idempotent now. Give each write an idempotency key derived from the run and the step. This costs little in a loop and is required once you have resume, because resume means some steps can run twice.
- Log in a shape you can replay. Record each turn's messages, tool calls, results and tokens. That log is your test fixture when you migrate: replay old runs through the new graph and compare outcomes.
- Migrate the loop, not everything. Replace the while loop with a graph whose nodes call your existing model adapter and tools. Run both side by side on the same inputs for a while before switching over.
If you followed the first five steps, the migration is days, not weeks. If tools are tangled into the loop and limits are scattered through it, you will be rewriting.
Common failure modes
When you chose a framework
- Debugging the abstraction. The agent misbehaves and the first hour goes to finding out which component built the prompt. Fix: log the exact request sent to the model, every time, from day one.
- Unused features with real cost. Memory components, retrievers and chains added because the tutorial had them. Each one changes what the model sees. Fix: start from the smallest graph and add components only for a named need.
- Hidden context growth. Default memory that keeps appending history makes each turn more expensive than the last, and nobody notices until the bill arrives. Fix: know exactly what is retained between turns and cap it.
- Durability without idempotency. Checkpoints make resume possible. They do not make a double-charged card impossible. Fix: idempotent tools, always.
When you chose a plain loop
- Rebuilding a framework badly. Six months in, the loop has a homemade checkpoint table, a retry wrapper and a routing switch, with fewer tests than any framework. Fix: when you notice you are building durable state, stop and evaluate a framework or workflow runtime for real.
- No limits. A plain loop is only safer if you add the limits. A loop with no turn cap and no budget is the most expensive agent there is.
- Logs nobody can read. Plain console output from many concurrent runs is noise. Fix: structured logs with a run id on every line.
- Provider lock-in in the loop. Parsing one vendor's response shape all through the code makes switching painful. Fix: keep provider details inside the adapter.
My verdict
Start with a plain TypeScript loop unless durable state or multi-agent routing is a requirement you can point to today. Shape it for migration: tools as data, model behind an interface, limits as functions, idempotent writes, replayable logs. When the week comes that you can name the missing feature, buy the framework for that feature, on purpose, and move the loop over in days.
If you are unsure where you stand, spend two minutes with the Framework Picker, then read the harness post for the loop I start from.
FAQ
Is LangChain.js overkill for a simple agent?
Usually, yes. One agent calling a handful of tools for a few minutes is a loop over messages plus a tool registry and some limits. A framework adds concepts you must learn and debug through without solving a problem you have yet. It stops being overkill when you need durable state, multi-agent routing, or a large set of ready-made integrations.
What does a graph framework like LangGraph give me that a loop does not?
Mainly two things: state that is checkpointed between steps so a run can resume after a crash or a long human wait, and explicit routing between nodes so several agents or stages can hand work to each other in a way you can inspect. Many also integrate with tracing tools so you can see every step of every run.
Can I start with plain TypeScript and move to a framework later?
Yes, and it is the path I recommend when you are unsure. Keep tools as plain objects with a name, a schema and a handler, keep the model behind one small interface, and keep limits and approvals as separate functions. Those pieces map onto framework concepts, so migration means replacing the loop, not rewriting the tools.
What is the most common mistake teams make with agent frameworks?
Adopting one on day one for features they never use, then debugging the abstraction instead of the agent. The second most common is the opposite: refusing a framework long after they have rebuilt half of one by hand, usually checkpointing and retries, with fewer tests than the framework had.
Is there a middle ground between a framework and a hand-written loop?
Yes. An SDK that handles provider differences, streaming and tool-call parsing, while you keep your own loop and limits, covers many projects. You get less plumbing to maintain without handing over control of the run.
Does using a framework make an agent more reliable?
Not by itself. Reliability comes from limits, validation of tool arguments, approval gates and verification of the result. A framework can make some of those easier to wire up, and durable execution genuinely helps long runs survive failures, but a badly bounded agent is badly bounded in any framework.
Related reading
- Your AI Agent Needs No Framework: A 200-Line TypeScript Harness - the loop this post assumes you start from.
- How to Control the Cost of an AI Agent - the limits that belong in any agent, framework or not.
- TanStack AI vs Vercel AI SDK - the SDK middle ground, compared.
Comments
No comments yet. Be the first.
Leave a comment