A hand-rolled agent loop that works is not a reason to keep it
I run the AI platform at KnowBe4, and the first version of our agent coordinator was built the way most teams build one when the tooling isn't there yet: directly on lower-level orchestration primitives, with a hand-rolled loop deciding when to call a tool, when to hand off to a satellite agent over an HTTP protocol built for that purpose, and when a turn was actually finished. It worked. It also meant every property you'd want from an agent runtime, session state, a correct tool-use loop, streaming, retry and error handling on a long-running model call, was something my team owned, tested, and had to keep owning as the underlying model APIs evolved under it. We migrated that coordinator onto the Claude Agent SDK. This is the case for why, and the two enforcement mechanisms that kept the migration from quietly reverting itself.
The question isn't whether your loop works
A hand-rolled agent loop that passes its own tests is not evidence you should keep it. The question that actually decides build-vs-adopt for infrastructure like this is who is investing in it over time, and how that investment compares to what your own team can sustain. An agent loop touches a moving target: message formats, streaming semantics, tool-call retry behavior, context management, all of it shifts as the underlying model and its supporting APIs change. A vendor shipping an SDK for that surface is going to keep paying that cost as a matter of their own roadmap. A hand-rolled loop only keeps pace with however much of your own team's time you're willing to keep spending on it, indefinitely, for a piece of infrastructure that was never the actual product.
That's the frame I'd want any team to apply before defending a hand-rolled version of something a vendor is actively building: not "does ours work today," but "whose job is it to keep this correct in a year," and whether that answer is a name on your own team or a company that ships the fix before you'd have found the bug.
What we got back by adopting it
The Claude Agent SDK replaced the pieces of our loop that had nothing to do with what our product
actually needed to do. Session management, durable across a turn and pluggable into our own
storage rather than tied to whatever the SDK assumes by default. A tool-use loop that reasons
about stop_reason rather than something brittle. Streaming and error handling on the model call
itself, the part every hand-rolled loop reimplements slightly differently and slightly wrong the
first few times. None of that is where we wanted engineering time going. Our actual product is the
knowledge our sub-agents have, the tools we expose, and the guardrails around what those tools are
allowed to do. The SDK let us stop maintaining the scaffolding underneath that and put the time
into the scaffolding itself.
The zero-strands test
A decision to migrate is not the same thing as a migration that stays migrated. Six months in, it
is easy for a new contributor, or an agent doing the coding, to reach for a familiar pattern from
the old framework because it's still in muscle memory or still findable in an old branch. So the
migration shipped with an automated test, test_zero_strands, that fails the build if it detects
any import of the old hand-rolled framework anywhere in the coordinator. It doesn't check that the
new code is good. It checks that the old code is gone, permanently, and stays gone every time CI
runs. That's a small test to write and it is the single most effective thing we did to make the
migration durable rather than aspirational. A decision written down in a document can be
forgotten. A decision enforced by a failing build cannot.
Hub and spoke, never chain spokes
The old architecture let satellite agents call each other directly over their agent-to-agent protocol, which is a natural thing to reach for and a bad one to keep. A chain of spokes calling spokes means the dependency graph between agents grows however the code happens to grow, and nobody can look at it and say with confidence what depends on what. One of our architecture decision records, ADR-0036, states the rule we adopted instead as a hard constraint on any new agent work: one orchestrator delegates to focused sub-agents, and sub-agents do not chain to each other. A sub-agent starts with no memory of the parent conversation and returns its result to the hub, and the hub is the only place synthesis happens. That single rule keeps the whole system debuggable in a way a mesh of agent-to-agent calls never was. When something goes wrong, there is exactly one place to look for what called what.
The test outlives the memory
The measure of whether this migration actually stuck won't be the day it shipped. It'll be the
next pull request from someone, human or agent, who never knew the old framework was banned,
reaching for the pattern that used to work. test_zero_strands doesn't care whether they knew. It
just fails, and the fix is a two-line diff pointing at the SDK's own equivalent instead of a debate
about whether the rule still applies.
More in AI
All in AI →- I deleted five AI review agents and the reviews got betterA five-specialist review fan-out looked thorough. One session cost $5.88 and 905 seconds for zero findings, and that's what actually changed the design.AI
- A green test suite is not proof a non-programmer's edit was safeA change-tier system and a verifier that fails risky edits even when every test passes, because a green suite is not evidence the change was safe.AI
- Don't trust a model's arithmetic, only its tool callsWhy I moved every numeric answer out of model prose and into a calculator tool call, and what a data-quality investigation taught me about trusting the output.AI