From microservices to AI agents: what a decade of backend taught me
For ten years I built microservices: Java and Spring Boot, queues and contracts, pipelines that turned a green build into a production deploy without a human holding their breath. Lately I've been building something that feels different — agentic systems powered by the Claude Agent SDK — and I keep noticing it isn't different at all. The discipline transfers almost one-to-one.
An agent is a distributed system wearing a trench coat
A microservice talks to other services over an unreliable network. An agent talks to tools, models, and other agents over an equally unreliable boundary. The failure modes rhyme: timeouts, partial results, retries that quietly double-execute, state that drifts out of sync. If you've designed idempotent handlers and sensible backoff before, you already know how to keep an agent loop from melting down.
The hard part of agents was never the prompt. It's the same thing it always was: what happens when the third call in a chain fails.
Clean architecture still pays the rent
When I wrap a model call, I keep the same boundaries I'd keep around any external dependency — a port the domain owns, an adapter that knows the vendor's quirks:
public interface SummarizerPort {
Summary summarize(Document document);
}
@Component
class ClaudeSummarizer implements SummarizerPort {
private final AnthropicClient client;
ClaudeSummarizer(AnthropicClient client) {
this.client = client;
}
@Override
public Summary summarize(Document document) {
var response = client.messages().create(request(document));
return Summary.from(response);
}
}The domain never imports the SDK. I can swap models, stub the port in a test, or put a cache in front of it — none of that leaks into business logic. This is the same move that kept my services testable for a decade; the only new thing is what sits on the far side of the adapter.
Tests are how you sleep at night
The reflex I'm most grateful for is the testing one. Non-determinism isn't an excuse to skip tests — it's a reason to test the scaffolding hard: the parsing, the routing, the fallback when a tool returns nothing useful.
- Unit-test the deterministic edges. Tool input/output schemas, retries, guards.
- Contract-test the boundary. Record real responses, replay them, assert behavior.
- Evaluate the fuzzy middle. Treat quality like a metric you watch over time.
CI/CD closes the loop: an agent change ships through the same gates as any other code, because to the pipeline it is any other code.
The takeaway
The instinct to reach for "AI engineering" as a brand-new discipline is understandable, but most of what makes agents reliable is the boring stuff we already know. Clean boundaries. Honest tests. Pipelines that catch mistakes before users do. A decade of backend scar tissue turns out to be the best preparation for this work I could have asked for.
Comments
Comments are powered by GitHub Discussions and aren't configured yet.