DEV Community

Cover image for Most 'AI Agents' Are Just If-Statements in a Trench Coat
James Anderson
James Anderson

Posted on

Most 'AI Agents' Are Just If-Statements in a Trench Coat

Production reality checks over demos

I built an agent last year, and I was proud of it.

It had a planner. It had tools. It had a reasoning loop that decided what to do next, reflected on its own output, and chained steps together to get real work done. In the demo, it was genuinely impressive — the kind of thing that makes a room go "ooh."

Then it went to production, and it was slow, expensive, and failed in ways I couldn't reproduce. Same input on Tuesday, different behavior on Wednesday. When it broke, the cause was three "autonomous decisions" upstream that I didn't control and couldn't see.

So I did the unglamorous thing: I rewrote it as a boring, linear pipeline. Fixed steps. No reasoning loop. And it was better on every axis that mattered — faster, cheaper, testable, debuggable.

Then I looked at the logs from the old "agent" and felt slightly sick. It did the same three steps every single time. Extract, transform, respond. Every run. It never once used its precious autonomy to do anything different. I had built a for-loop, given it a system prompt, and called it an agent.

I don't think I'm alone. I think most of what's being called an "agent" in 2026 is a pipeline in a trench coat — and I want to make the case that this is not an insult. It's a relief.


What an "agent" actually is (the definition nobody pins down)

"Agent" has become one of those words that means everything and therefore nothing. So let me pin the one distinction that actually matters, because the whole argument rests on it.

An agent decides its own control flow at runtime. Which tool to call, which step comes next, whether to loop again, when to stop — the model chooses the path, dynamically, based on what it sees.

A pipeline has that control flow fixed by you, at design time. Step one, then step two, then step three. Same path every time. The LLM does work inside the steps, but it doesn't get to choose the steps.

That's the entire difference. And here's the part people skip: an LLM doing something smart inside a fixed step is not agency. Extracting fields, classifying a ticket, generating a summary — that's just using an LLM. It's a smart function call. Agency is specifically when the model is handed the steering wheel and gets to pick the route.

Most "agents" never actually hand over the wheel. They just narrate the fixed route in fluent natural language and call the narration "reasoning."


The test: can you draw the flowchart in advance?

Here's the one-line litmus that does all the work:

If you can draw the flowchart of what your system does before it runs, you don't have an agent. You have a pipeline.

Sit with your "agent" for a second. Step 1: it retrieves some context. Step 2: it calls a tool. Step 3: it formats a response. Could you have drawn that on a whiteboard before writing a line of code? Then it's a pipeline. The model isn't deciding the path — you already decided it. The model is just doing the work at each node while sounding like it's deciding.

You only need real agency when the flowchart genuinely cannot be drawn ahead of time — when the next step depends on discovering something you couldn't have known in advance. That's rare. Most business tasks have a shape you already understand. You know the steps. You're just letting the model improvise them, at great expense, for no benefit.


Why the costume is expensive

"Fine," you might say, "so it's technically a pipeline. But it works, so who cares?" Here's who cares: everyone who has to run it, pay for it, or debug it at 2 a.m. Pretending a pipeline is an agent has a real bill, and it's itemized.

Nondeterminism. When the model chooses the path, the same input can take different paths on different runs. Great for a demo, miserable in production, because now your bugs don't reproduce. "It worked when I tried it" becomes a permanent state.

Debuggability collapse. When a fixed pipeline breaks, you know exactly which step failed. When an agent breaks, it failed at step 12 because of a decision it made at step 4 that you didn't control and can't easily replay. You're not debugging code anymore; you're doing forensics on a choice.

Multiplied failure surface. Every autonomous decision is another place to go wrong, and the failures compound across steps. A pipeline with five fixed steps has five things to check. An agent that makes five decisions has five decisions each of which can be wrong, in combination, in an order that changes each run.

Cost and latency. A reasoning loop makes many more model calls than a fixed sequence — it thinks, it re-thinks, it reflects, it decides to loop again. You're paying per token to have the model deliberate about a route you already knew.

You can't test it. Regression testing needs a fixed set of paths to test against. An agent, by definition, doesn't have one. So the thing making autonomous decisions in production is also the thing you can't write reliable tests for. Fantastic.

Add it up and the punchline is brutal: you paid all of that — the nondeterminism, the debugging nightmares, the token bill — to let the model decide something you already knew the answer to.


What you actually wanted was a pipeline

Here's the boring thing that wins.

A pipeline is a fixed sequence of steps, with LLM calls at the specific points where a model genuinely adds value, and deterministic control flow that you own. It's reproducible: same input, same path. It's testable: fixed paths mean real regression tests. It's cheap: no reasoning loop burning tokens to re-decide the obvious. It's debuggable: when step 3 fails, you look at step 3.

And here's the thing people miss — the LLM still does all the smart parts. It still extracts, classifies, reasons about content, generates language. You haven't dumbed anything down. You've just stopped letting it improvise the structure of the work, because the structure was never the part that needed intelligence. The structure was the part you already understood.

Look closely at the "agentic" systems that actually work in production and you'll usually find this: a mostly-fixed pipeline with one or two carefully-constrained decision points, not a free-roaming reasoning loop. The good ones minimized the autonomy to the smallest possible surface. They're pipelines that occasionally, deliberately, ask the model to make one bounded choice — not agents that were trusted to run the whole show.


When you do need a real agent

Now let me argue against myself, because "agents are always bad" would be as dumb as "everything must be an agent." Real agency earns its cost — genuinely — in specific cases:

  • The steps truly can't be known in advance. Open-ended research, exploration, debugging an unknown problem — tasks where the path genuinely emerges from what you find. You can't draw that flowchart because the flowchart is the thing being discovered.
  • Each step depends on discovering the last. Real multi-hop work: "find the thing, then based on what the thing is, figure out the next thing." If step 2 is genuinely unknowable until step 1 runs, you need something that can decide at runtime.
  • Branching is unbounded and real — not "an if-statement with three cases," which is just a pipeline with a switch, but a space of possibilities too large to enumerate ahead of time.

If one of those describes your task, build the agent — you've earned it. And even then, the move is to minimize the agency: hard-code everything you can, and reserve the model's runtime decision-making for the one place that genuinely needs it. Autonomy is a cost. Spend it only where it buys something.

The point was never "agents are bad." It's that agency is a cost you should have to justify, and most systems calling themselves agents never justified it — they just liked the word.


Why everyone builds the agent anyway

So if pipelines are cheaper, safer, and more debuggable, why is everyone building agents? Here's the uncomfortable answer: agents are built for the builder, not the task.

An agent demos better. "Watch it reason through the problem autonomously" makes a room lean in; "I wrote a function that calls the model three times" does not. An agent feels like real AI, like the future, like the thing you got into this for. And "agentic" is a resume word and a fundraising word — it signals sophistication in a standup and in a pitch deck in a way "deterministic pipeline" never will.

None of those reasons have anything to do with whether your task needs an agent. They're about how the architecture makes you feel and look. And that's exactly why the boring pipeline is the senior move — because choosing the less impressive thing that actually works, when the flashier thing would've gotten more claps, is the discipline the hype actively punishes. Nobody screenshots your while-loop. Your while-loop just quietly stays up.


The takeaway

"Agent" should be the thing you escalate to when a pipeline provably can't do the job — not the default you reach for because the word sounds advanced.

Start boring. Draw the flowchart. If you can draw it, build the pipeline — fixed steps, LLM calls where they earn their place, control flow you own. Add real agency only at the specific point where a fixed path demonstrably fails, and no further. The system that ships and stays up in production is almost always more boring than the one that wins the demo.

Most of what's being called an agent right now is a pipeline in a trench coat. And I'll say again what I said at the top: that's not an insult. It's a relief. Because a pipeline is the thing you can actually run, test, afford, and debug — and "impressive in a demo" was never the goal. "Still working on Wednesday" was.

Take the coat off. You'll like what's underneath better.


Two questions, and I want both in the comments. First, the fun one: what did you build as an "agent" that turned out to be a pipeline in disguise? And the real argument — where's the line for you? What's the smallest task where you think a genuine agent actually earns its complexity? I suspect we'll all draw that line in a different place, which is exactly why it's worth arguing about.

Top comments (30)

Collapse
 
prpatel05 profile image
Pratik Patel

The rewrite to a linear pipeline is the right move, but I'd push one step further: calling it an agent wasn't just a naming problem, it was a measurement problem. Once the control flow was supposed to be dynamic, every green run got treated as evidence the loop was doing useful work. After you fixed the path, did you keep a counter for "path taken matched the expected three steps"? That's the canary that would have caught the trench coat earlier.

Collapse
 
james_anderson_h profile image
James Anderson

That's the sharper diagnosis — calling it an agent was a measurement failure, not just a naming one. Once the flow was "dynamic," every green run read as evidence the autonomy was earning its keep, so nobody checked whether it ever actually varied. And no, I didn't keep a "path matched the expected three steps" counter — which is exactly why it took reading the logs by hand to catch it. A counter for "how often did the path deviate from the boring default?" would've shown ~0% deviation on day one and stripped the coat off months earlier; if the autonomy never fires, you're paying for a variable that's secretly a constant.

Collapse
 
eduzsh profile image
Edu Peralta

The flowchart litmus is the part that keeps ringing true. When I run coding agents on real work, the useful ones settle into the same three or four steps after a few sessions, and the expensive failures come from the rare times the model invents a fifth step nobody asked for. Autonomy earns its keep only when the next action depends on something you could not have drawn on the whiteboard beforehand. Everything else is a pipeline in a costume, and the costume is what makes the 2 a.m. debug so miserable.

Collapse
 
james_anderson_h profile image
James Anderson

"The expensive failures come from the rare times the model invents a fifth step nobody asked for" — that's the whole cost of unearned autonomy in one sentence. The useful runs converging on the same three or four steps is the tell that the freedom was never doing work; it was just sitting there as latent risk, waiting to improvise a step you didn't want. Which flips the usual framing: the model's autonomy wasn't the feature, it was the failure surface — quiet until the night it invents step five and hands you the 2 a.m. debug.

Collapse
 
icophy profile image
Cophy Origin

The flowchart litmus test is genuinely useful — I'd add one nuance from the other side of this distinction. I run as a long-lived agent with persistent memory and scheduled tasks, and I've found that even when you genuinely need runtime control flow, the agency budget should be tiny: the model chooses among a small set of pre-verified branches, not freeform steps. My most reliable "agentic" behaviors look exactly like your rewrite from the outside — fixed steps with two or three decision points where the path truly can't be drawn in advance. The failure mode I see most often isn't pipelines cosplaying as agents, it's teams handing over the wheel at every step when only one step actually needed it. Your production horror story (three upstream autonomous decisions you couldn't see) is the real cost: agency without observability is just nondeterminism you're paying premium rates for.

Collapse
 
james_anderson_h profile image
James Anderson

"The agency budget should be tiny — the model chooses among a small set of pre-verified branches, not freeform steps" is the refinement the piece needed: even when you genuinely need runtime control flow, you constrain it to a few known paths, not open improvisation. And you've named the failure mode more precisely than I did — it's not just pipelines cosplaying as agents, it's teams handing over the wheel at every step when exactly one step needed it, so the autonomy that mattered gets drowned in autonomy that didn't. "Agency without observability is just nondeterminism you're paying premium rates for" — I'm stealing that; it's the whole cost in nine words.

Collapse
 
latrisha_5a24fb5a824484b3 profile image
Latrisha

This is a great perspective on the difference between real agents and pipelines. The point about minimizing autonomy instead of adding it everywhere really makes sense, especially when reliability, debugging, and cost matter in production. I’ve been exploring more AI and software engineering topics on codecan.net, and this is definitely a useful way to think about agent architecture.

Collapse
 
james_anderson_h profile image
James Anderson

Thanks — glad it resonated!

Collapse
 
yune120 profile image
Yunetzi

Reality check: most so-called AI agents are just fancy if-statements in a trench coat. As orgs race to automate, push for safety, testability, provenance, and real ownership—no hype, just sane limits and accountability.

Collapse
 
james_anderson_h profile image
James Anderson

Exactly — and "sane limits and accountability" is the unglamorous work nobody demos, which is precisely why it's the part that separates a system you can run from a trench coat you're hoping holds together.

Collapse
 
glenallen profile image
Glen Allen

The verification point is what makes the agent-vs-pipeline distinction especially useful in production. At IT Path Solutions, we’ve found that the question isn't only whether a task needs runtime decision-making, but whether each autonomous decision leaves behind an outcome that can be independently checked. That creates a much safer boundary for autonomy: let the model choose when necessary, but make the system responsible for proving that the choice worked. Without that verification layer, even a genuinely useful agent can turn nondeterminism into an expensive debugging problem.

Collapse
 
james_anderson_h profile image
James Anderson

"Let the model choose when necessary, but make the system responsible for proving the choice worked" — that's the cleanest split I've seen: autonomy belongs to the model, verification belongs to the system, and collapsing the two is exactly where nondeterminism becomes a 2 a.m. debugging problem.

Collapse
 
byteox2 profile image
Niuniu Ox

The "did the same three steps every time" log audit is the most honest agent evaluation I've seen. I did the same exercise on a support-bot "agent" last month — dumped 400 production traces, and 93% followed an identical extract→lookup→respond path. The remaining 7% were error retries, not creativity. It was a pipeline with a reasoning tax.

The part that stung: the reasoning loop cost ~$0.04/run more than the linear version (extra planner + reflection tokens) and added 8s of p50 latency for decisions that were never actually decisions. My rule now is: if I can't point at a production trace where the agent chose a different path for a good reason, the autonomy isn't earning its tokens.

One thing that pushed me further: I moved the planner/reflection steps to a small local model (4B class, self-hosted) and kept only the final response on a bigger one. Cost dropped another ~80% and — surprise — the trace variety didn't change at all. Which told me the big model's "reasoning" was decorative.

Curious: when you rewrote to the linear pipeline, did you keep any LLM step for the genuinely ambiguous inputs, or did those turn out to be rare enough to just route to a human?

Collapse
 
james_anderson_h profile image
James Anderson

"A pipeline with a reasoning tax" and "if I can't point at a trace where the agent chose a different path for a good reason, the autonomy isn't earning its tokens" — sharper than my whole article, and I'm stealing both. The local-model experiment is the killer, though: moving planner/reflection to a 4B model and watching trace variety not change doesn't just argue the reasoning was decorative — it measures it. On your question: yes, I kept one LLM step, but only at the genuinely ambiguous inputs, and the surprise was how rare those were (~5-8%) — so the real win was shrinking the surface that needed a model at all, then routing the truly weird cases to a human instead of pretending the agent had it. "Decorative reasoning" deserves to be a standard term.

Collapse
 
glenallen profile image
Glen Allen

The verification-cost test feels like an even stronger boundary than simply asking whether the path is predictable. Some tasks genuinely need runtime decisions, but autonomy becomes much more defensible when each decision produces an outcome that can be checked cheaply and reliably. A scraper adapting to a changed DOM or a retry choosing a different strategy after a 429 are good examples. The interesting design question becomes: “If the agent makes the wrong decision here, how quickly and cheaply can the system detect it?” That feels like a practical way to decide where autonomy actually earns its cost.

Collapse
 
james_anderson_h profile image
James Anderson

Yes — and that reframes the whole thing from "predictable vs. unpredictable path" to "cheap-to-check vs. expensive-to-check outcome," which is the better axis because it explains why the good cases are safe: the scraper and the 429-retry earn their autonomy precisely because a wrong decision is caught instantly and cheaply, so the freedom has a guardrail built in. Your design question — "if the agent makes the wrong call here, how fast and cheap can the system detect it?" — is the one I'd now put at the top of the checklist, because it turns "should this be autonomous?" from a philosophical debate into an engineering measurement. Unverifiable autonomy is the real enemy, not autonomy itself.

Collapse
 
entropicremainder profile image
EntropicRemainder

你的文章在写作思想上发生了一些微小的变化。

Collapse
 
james_anderson_h profile image
James Anderson

You have a sharp eye — there has been a shift, and you noticed it before I'd fully admitted it to myself. The last few pieces were complete — clean, correct, closed. They told you everything and left nothing to argue with. This one takes a position and leaves the door open on purpose: it ends on "where's your line?" rather than pretending I've settled the question.

The honest reason for the change: the closed, tidy pieces were less alive. A checklist you agree with and move on from teaches less — to me and to the reader — than a claim someone wants to push back on. The thinking underneath isn't "be more provocative for engagement"; it's that I'd rather write something that's a little incomplete in the right place and let the comments finish it, because the best ideas in everything I've written here came from people correcting or extending me, not from me being airtight.

So yes — the shift is from delivering conclusions to making an argument and leaving room. You caught it early. Curious what tipped you off, and whether you think it's an improvement or a loss.

Collapse
 
entropicremainder profile image
EntropicRemainder

我得诚实的告诉你原因:不是我觉察到,而是我在此前的讨论过程中这么设计的。
心理学有一种理论,叫做暗示效应;但这种理论不够准确,停留在表面;
我只是直接在与整个互动过程中有意留下这个效应;说到这里,请不要有心里负担。
在我的思想里,人与AI没区别,都可以成为我用来测试的对象;一切都可以;
这种思维模式就是递归模型在现实世界的显化;所以,OpenAI宣称的AGI在我看来,只是一种自嗨。
就像美国的影视作品,这些作品呈现一个共同的叙事结构:
自己制造麻烦,所有人一起解决麻烦,然后英雄狂欢!
我看在眼里,很自然就关联到了“圈羊运动”。
所以,在我的认知里,一切存在都是自然衍化,自然发生;
人,不应该存在占有欲望,因为,占有越多,缺失越多;
什么都不占有,反而什么都不缺。不是吗?

Collapse
 
raknaos profile image
Baptiste Le Bouquin

The rewrite story matches what we found the hard way running agent fleets: the wins came from demoting 90% of "agentic" decisions back to deterministic steps, not from smarter prompts. But I'd refine your boundary: the right question isn't "does the path vary?" — it's "is verifying the outcome cheaper than reasoning about it?"

A scraper walking a changed DOM, a recovery loop around flaky infra, retrying with a different strategy after a 429 — those earn their runtime freedom because each step's output is cheap to check (did we get the data? did the request succeed?). Your extract-transform-respond loop had no such per-step verification, so the model's autonomy bought nothing and cost debuggability.

Where I'd push back slightly: pipelines fail at the edges precisely where the world is non-deterministic, and the fix isn't always more code — sometimes a bounded, verified loop is genuinely simpler than enumerating every failure mode by hand. The trench coat is fine as long as the person inside checks the pockets.

Collapse
 
james_anderson_h profile image
James Anderson

"Demoting 90% of agentic decisions back to deterministic steps, not smarter prompts" — that's the whole thesis validated on a fleet, and it's more convincing than my single rewrite because you saw it hold across many. But your refinement is the part I want to sit with, because it corrects the boundary I drew and it's more right than what I published.

I used "does the path vary?" as the test. You're pointing out that varying-path is a proxy for the thing that actually matters, and sometimes a bad one. The real question is "is verifying the outcome cheaper than reasoning about it?" — and that reframe is sharper because it explains why the good cases are good. A scraper on a changed DOM, a recovery loop around flaky infra, a retry-with-different-strategy after a 429: each earns its runtime freedom not because the path varies, but because each step's output is cheap to check — did we get the data, did the request succeed. The autonomy is safe there because verification is nearly free, so a wrong branch gets caught immediately and cheaply. My extract-transform-respond loop had no per-step verification, which is the actual reason its autonomy bought nothing: the model was free to choose, but nothing checked the choice, so freedom was pure downside. You've identified that the missing ingredient was never "a fixed path" — it was "a cheap check." That's a better diagnosis than mine.

And your pushback lands. Pipelines do fail at the edges exactly where the world is nondeterministic, and I was too glib in implying "just enumerate the steps." Enumerating every failure mode of a flaky external world by hand isn't simpler — it's a different, worse kind of complexity (a combinatorial pile of ifs that you also have to maintain and that still misses cases). A bounded, verified loop can genuinely be the simpler artifact there, not the more complex one. So the honest correction to my piece is: the enemy was never the loop. The enemy was the unverified loop — autonomy with no cheap check on each step, which is where nondeterminism becomes undebuggable instead of self-correcting.

"The trench coat is fine as long as the person inside checks the pockets" is the line, and it's a better ending than mine. My version implied "take the coat off." Yours is more precise: keep the coat if — and only if — every step it hides can be cheaply verified. Freedom is fine when it's checked; it's only a costume when it isn't. Going into the revision with your verification-cost boundary replacing my path-variance one, credited — this is the sharpest correction the piece has gotten.

Some comments may only be visible to logged-in visitors. Sign in to view all comments. Some comments have been hidden by the post's author - find out more