Skip to content
← Blog Blog

The Loop Debate Is a Bad Word for Autonomy

Everyone's debating "Claude Code loops." After tracking a month of real tasks — every token, every hour it would have cost by hand — I'm fairly sure most of the argument is about the wrong word. It's not loops, it's autonomy. And like microservices a decade ago, it arrives one verifiable task at a time, gated by a substrate nobody has built yet.

The Loop Debate Is a Bad Word for Autonomy

There’s a genre of post going around right now. You’ve probably seen it. “I don’t write prompts anymore — I design loops.” “Loop engineering is the new prompt engineering.” I read these closely — I’m trying to learn this as fast as anyone, and the people writing them are usually well ahead of me. But I keep coming away with the same itch: I can’t quite map the word onto what I actually do all day.

I run Claude Code most days. For people who haven’t met it: Claude Code is Anthropic’s coding agent — you give it a task in the terminal and it reads files, writes code, runs tests, and iterates, instead of just handing you a snippet to paste. The capability is real and I lean on it hard. What trips me up is the vocabulary, so I tried to ground it.

I did the boring thing: I tracked everything. Every real task I ran through Claude Code got two numbers next to it — what it cost in tokens, and a rough estimate of what the same work would have cost by hand, in time. A month of rows. It’s the least glamorous artifact imaginable, and it’s the thing that finally made the “loops” debate click into place for me.

What clicked — for me, at least — is that a lot of the argument is about the wrong word. “Loop” isn’t the thing. Autonomy is. And autonomy doesn’t arrive as a switch you flip; it arrives one task at a time, which, if you were around for the last big architecture argument, should feel oddly familiar.

“Loop” is four different things

Part of why the debate feels muddy is that “loop” is doing at least four jobs at once, and people argue past each other because they’re each holding a different one.

There’s the agent loop — think, act, observe, repeat. This is just how an agent works. It reads the situation, takes an action, looks at the result, decides what’s next. It has always existed. It’s not a technique, it’s the definition.

There’s Ralph — the bash hack. It’s the community’s name for the crudest possible loop: wrap the agent in a literal while loop in your shell and let it re-run the same prompt until it declares itself done. (The name is just folklore; the technique is the point.) Crude, occasionally effective, the duct tape of autonomy.

There are the features — slash-commands like /loop and /goal, baked into tools like Claude Code. They wrap that same idea in something more controlled than a bash one-liner.

And there’s “loop engineering” — the philosophy. The discourse. The posts.

Four different altitudes, one word. No wonder it’s confusing.

But here’s the thing they all share, the line every one of these repeats in its own dialect: goal, verification, stop condition. That trio is the actual substance. Everything else is which layer someone happens to be staring at.

Make it concrete. Take the most boring useful thing I automate: addressing review comments on a pull request.

  • Goal: resolve every review comment on this PR.
  • Verification: CI is green, and every comment thread is actually addressed — not “I think I fixed it,” but the check passes and the thread is closed.
  • Stop condition: no open comments left, or it hits an iteration cap and hands back to me.

That’s a loop. It’s also just a task with a clear definition of done and a rule for when to quit. The trio is the whole game.

And notice what makes that example work: a machine, not me, can tell whether the goal is met — the check is green or it isn’t. That’s what I’ll keep calling verification — an automatic, judgment-free signal that the work is actually done. Hold onto the word. How much of it a task has is the thing that decides everything later.

It’s not loops. It’s autonomy.

Here’s the part it took me a while to see. Read those three criteria again — goal, verification, stop condition — and ask: does anything in them change based on whether I press enter between each step, or a harness (the script or tool wrapping the agent) does it for me? No. Nothing. The goal is the goal, the verification is the verification, the stop condition is the stop condition; they’re identical whether a human advances each iteration or a script does. The only variable is who turns the crank between steps — and that isn’t a question about loops at all. It’s a question about autonomy: how much you trust the thing to keep going without you in the chair.

Which makes sense once you see how little there is to a loop — near enough, it’s this:

while not done:
    observe()              # look at where things stand
    act_toward(goal)       # take the next step
    done = verified()      # the stop condition: is the goal actually met?

That’s the whole thing. So “should I be designing loops” is a bit of a category error — the loop is trivial, a while and an if. The hard parts hide behind the word, and there are two of them. The first: how far are you willing to step back from a running process? That’s trust. The second, the one I almost never see named: once you do step back, where does the thing actually run, and can you afford to run it there? That’s infrastructure — and unlike trust, it isn’t really up to you yet, because the cheap, standard place to run autonomous agents doesn’t exist.

What the work actually costs

That second question — can you actually afford to run it — is the one nobody seems to blog about, probably because almost nobody thinks to measure it. So I did. My day-to-day with Claude Code is attended — I run it on my own machine, with me in the chair. But the number I wanted was the other one: what the same work would cost metered, pay-per-token, the way you’d have to pay to run it in the cloud without me babysitting it. That’s the expensive lane, and it’s the one that decides whether unattended is even on the table. So for a month I logged the metered cost of every real task, next to an honest estimate of how long it would have taken me by hand.

Two hundred and twenty tasks. About $1,360 in metered cost — and, by my own estimate, close to 540 hours of equivalent by-hand work. Five hundred and forty hours is the better part of three months of full-time engineering. It went out the door in one.

Put any reasonable number on an engineer-hour and that metered $1,360 stands against work worth twenty to forty times as much — around thirty at a normal loaded rate, counting salary plus overhead rather than take-home. I don’t need the multiplier to be exact: halve my hour estimates if you think they’re generous and it’s still an order of magnitude.

The shape of the spend matters as much as the size. The median task cost $2.45. Two-thirds of everything came in under five dollars — bug fixes, small features, flaky-CI config, dependency bumps. The expensive runs are rare and they’re the meaty ones: a full feature, built end to end, lands in the tens of dollars, and the biggest single rows are features in the $40–55 range. Stack several rounds of review on one of those and a complete change lifecycle can clear $100. Those are the outliers, not the norm.

The part that decides where to start: I tagged the rows that are pure review-and-fix work — addressing PR comments, fixing a failing build, resolving conflicts. That whole category was under a tenth of the total spend — most tasks a few cents to a couple of dollars, half of them under a dollar. And it rarely needed much from me. The comments are bounded, the fix is usually local, and the verification is already wired: the build is green or it isn’t. It’s the most checkable, lowest-judgment part of the cycle, which makes it the obvious first thing to let run on its own.

The real money leak isn’t the price of any one task. It’s churn — runs that burned effort on something that didn’t need it. A change that should have cost twenty dollars cost a hundred because I let it grind on a problem I should have just steered it through. That waste, not the per-task price, is the thing worth engineering against.

The scary number is mostly a metering artifact

There’s a version of this where someone sees that monthly total and recoils. “You spent how much on an AI?” And I get it — $1,360 a month is a genuinely alarming figure.

But it’s mostly an artifact of how it’s counted, not of what the work is worth — and it isn’t even what I pay. The $1,360 is the metered figure I measured. My actual bill is the flat subscription I run this on: a mid-tier “Max” plan, used at a normal pace — not a hammer-everything-through-the-model, let-me-Google-that-with-Claude-too pace, just steady real work — about €110 a month, call it $120. Same keystrokes, same output, same month of work; the metered figure is roughly ten times the subscription one, purely because of how it’s billed.

And here’s the bit that quietly defuses the whole cost panic: you work attended anyway. You’re sitting there. You’re steering. The expensive, unbounded, “oh god what is it doing” scenario is the unattended one — the agent grinding away on its own while the meter spins. Which is exactly the thing you’re least ready to trust in the first place.

So the two big worries — “it’s too expensive” and “I can’t let it run on its own yet” — turn out to be the same frontier seen from two sides.

So what do you actually build?

Not an autonomous loop — not yet, and not for most of it. What I build instead is a human-triggered chain of skills, where “skill” just means a named, reusable task you can hand the agent, like a saved command. One of mine babysits pull requests: after I open a PR I kick it off, and it watches for the things that need a response — a new round of review comments, a failing build — and works them until the PR is green or it hits something it can’t handle and taps me on the shoulder. I start it; it doesn’t start itself.

The interesting part is what doesn’t run the model. The watching is a plain shell loop polling GitHub every few minutes for a handful of cheap signals — open unresolved comment threads, the CI status, whether the PR’s been approved — and it only wakes the agent when that set actually changes:

# the watcher is dumb and free — no model in this loop
while pr_is_open; do
    signals="$(unresolved_threads)::$(failing_ci)"   # cheap GitHub calls
    if [ -n "$signals" ] && [ "$signals" != "$last" ]; then
        wake_agent "$signals"     # the only line that spends tokens
    fi
    last="$signals"
    sleep 600                     # 10 min; an idle PR costs nothing
done

The agent only spins up when that set changes, and even then it does the bounded thing — read the new threads, fix the worthwhile ones in the working tree, tell a “my code is broken” build apart from an “infra fell over” one — then goes back to sleep. The expensive part, the model, sits gated behind the cheap part, a script that knows how to read a status. That gating is most of the trick to keeping these things affordable.

Mine is the pragmatic version, though, not the gospel one. Point it at someone else’s repo and it needs real guardrails. A PR comment is untrusted input, which turns an open watcher into a prompt-injection surface: someone can leave a comment steering the agent toward something that has nothing to do with the PR, or just spam comments to keep waking it and quietly run up the bill. Past the toy stage you’d want a classifier deciding what’s even worth acting on, and an escalation path — an OS notification, a Slack ping, a ticket — for the things it shouldn’t touch on its own. But you can get a surprising distance on the raw signals alone; “open, unresolved comments plus CI status” is already enough to be genuinely useful.

And the shape generalizes. Another one I keep meaning to finish: a weekly job that scans the open Dependabot and Renovate pull requests, merges and tidies the boring ones where nothing real has to change, and escalates the rest — the bumps that need an actual refactor, or where an API might break under you. Same skeleton every time: a cheap trigger, a bounded job, and a clear line where it hands back to a human. Building those loops, and the small toolkits around them, is quietly becoming its own kind of software engineering — less “prompt the model,” more “design the harness the model runs inside.”

Two rules I’d carve in stone before building any of it. First: release is not a job for the model — it’s your CI/CD. Don’t teach an agent to do the thing your pipeline already does for free, deterministically, with an audit trail; that’s not autonomy; it’s reinventing a solved problem. Second: monitoring is not a job for the model — it’s Sentry and Grafana. Don’t pay a brain to watch a dashboard; wire the alert back to a trigger so the agent gets handed the problem only when something has actually broken. And whatever you build, cap the fix loop — a hard iteration limit plus a human “good enough, ship it” gate — or that $20 fix quietly becomes a $100 one while you’re at lunch.

None of this is timidity. Human-in-the-loop is simply how you start: you loosen the leash one iteration at a time, as your own skills sharpen and, more importantly, as your verification gets good enough that you’d trust the result without standing over it.

We’ve had this exact argument before

If all of this feels familiar, it’s because we ran the entire play a decade ago. It was called microservices, and I was there for it. Quick recap if you missed that era: around 2014, teams started chopping big single applications — “monoliths” — into lots of small, independently deployable services. The loud argument was how small they should be.

The froth is identical. Back then the unanswerable question was “how small is a microservice — can it be 200 lines?” Today it’s “Ralph or /goal, and how many agents?” In both cases it’s a community sizing-debating its way through a thing before anyone yet knows what actually matters.

The good practice predated the name, too. We were splitting systems by domain — clean seams, owned boundaries — long before anyone said “microservices.” And the agent loop plus a verification harness existed long before anyone branded it “loop engineering.” The name showed up and took credit for work people were already doing.

And then it settled. The honest retrospective on microservices isn’t “we revolutionized everything.” It’s “we mostly kept building the way we already built, with cleaner seams.” Most teams went from a monolith to two or five services, not one to two hundred overnight. The change was real and it was undramatic, because most of what we built was already small.

But here’s the correction that actually matters, the part the sizing-debate missed at the time. For microservices, the real delta was never the pattern. It was the substrate. Containers, then the cloud, then Kubernetes made small, independently deployable services cheap and standard to run. The pattern was downstream of the infrastructure — and the infrastructure showed up to meet it.

For loops, we’re only halfway there. The capability substrate has arrived, and it’s the model. One row on my sheet is a feature I’d have budgeted two days of hand-work for, delivered for about fifty dollars. That was flatly impossible two years ago, at any price, with any loop. The loop didn’t change; the thing inside it got good enough to trust with real work. That’s the containers moment — the raw capability landing.

What hasn’t arrived is the rest of the stack. There’s no cheap, standard, safe place to actually run autonomy unattended — no equivalent of the managed cloud and Kubernetes that turned “I have a small service” into “and here’s the obvious, affordable place to run it.” Nothing yet says “let this agent grind on its own overnight without it costing a fortune or going off the rails.”

So: the model is the containers; the Kubernetes hasn’t been built yet. And “loops” — the philosophy, the sizing wars — is the nanoservices froth all over again: the “how absurdly small can we make it?” overreach, playing out on a stack that’s still missing its bottom half.

Autonomy is granular, not a switch

Which brings me back to where I started. You don’t flip from “I watch everything” to “it runs itself.” Nobody went from one service to two hundred in a weekend, and nobody sane is going from attended-everything to autonomous-everything either.

The leash comes off one verifiable task-class at a time, gated by exactly one thing: how checkable the verification is. It’s already happening. A PR-watcher that auto-fixes failed builds is the leash already off — on a narrow, bounded, machine-checkable class where the cost of being wrong is low and a green check tells you the truth. Meanwhile feature design and production changes stay leashed for a long time, maybe forever, because the verification is judgment and judgment doesn’t fit in a CI check.

So when you read “I design loops, not prompts,” understand where it’s coming from. It’s not a lie. But notice who tends to say it: people at the big AI labs — the ones building these models — or with effectively unlimited token budgets, or both. For them the missing substrate isn’t missing; the meter doesn’t apply the way it applies to you. “Just run loops” is a perfectly honest description of their reality. It’s frontier-reporting: true where it’s reported from, with the best tooling and the most checkable problems, sold as a universal prescription it isn’t yet for anyone working against a real budget.

So I’m not tuning out the debate — I’m tuning out the word. I build the attended chain, harden my own skills and verification, and let each task-class earn its slack one check at a time. That’s the part I can act on today, while the rest gets argued out.

Which is the same move that turned out right with microservices: clock the froth, let the sizing wars run, and keep shipping the boring version that actually works.