The Evolution Of Modern Software Architectures

I've spent 25 years decomposing monoliths into microservices, and now I'm watching agentic AI ask the same architectural questions all over again: where do boundaries live, and who owns the decision? Turns out the discipline transfers more than the hype admits. Here's what actually changes in your architecture, and wh...

Someone asked me last month if agentic AI is "the new microservices." I laughed, then thought about it a second longer than I should have, because there's a kernel of truth buried in there worth unpacking. I've spent about a decade breaking monoliths into services: lending platforms, disclosure systems, hospital backends running on Kinesis and Istio. Different domains, same pattern. Pull a chunk of business logic out, give it its own database, wrap it in an API, put it behind a gateway, repeat. That work taught me a lot about boundaries, ownership, and what happens when two teams both think they own the "customer" concept (spoiler: nothing good). Now I'm building agent workflows for ITJobOpportunities, and on a side project called Agent UI Editor, a visual canvas for wiring up LLM agents. I keep noticing the same architectural instincts kicking in, just applied to a different kind of "service." An agent calling a tool feels a lot like a microservice calling another microservice over REST. The failure modes rhyme. The debugging headaches rhyme even harder. But it's not the same thing. Here's where the analogy holds, and where it falls apart. Microservices Solved a People Problem, Not Just a Tech Problem This is the part people forget. Microservices weren't primarily a technical innovation. Sure, we got service discovery, container orchestration, event buses, real engineering. But the actual reason companies split monoliths was organizational: you can't have 40 engineers pushing to the same deployable without stepping on each other constantly. On one FinTech program I worked, we had a lending monolith that took 45 minutes to build and deploy. Every deploy was a group chat full of "wait, don't push yet." Breaking it into services, loan origination, credit decisioning, notifications, the usual suspects, meant each team could ship on its own clock. That was the win. Not "microservices are inherently better," just teams stopped blocking each other. Agentic AI doesn't really have that problem to solve. Nobody's blocking anyone's deploy pipeline because of an LLM call. If you're adopting agents to fix a team-coordination issue, it won't fix that. Different tool, different job. What an "Agent" Actually Is, Architecturally Strip away the hype and an agent is a loop: prompt goes in, the model decides on an action (call a tool, ask a clarifying question, or answer), the action executes, the result feeds the next prompt, repeat until done or until you hit a max-iteration guard. It's a state machine with a probabilistic router sitting where you'd normally put an if/else block. Here's roughly the shape of it, using LangChain4j, which I've been running on Agent UI Editor: public interface JobSearchAgent { @UserMessage("{{query}}") String search(@V("query") String query); } AiServices.builder(JobSearchAgent.class) .chatLanguageModel(model) .tools(new JobSearchTool(), new ResumeParserTool()) .build(); Compare that to a Spring Boot REST controller calling a downstream service: @PostMapping("/apply") public ApplicationResult apply(@RequestBody ApplicationRequest req) { Candidate candidate = candidateService.findOrCreate(req); Score score = relevanceClient.score(candidate, req.jobId()); return applicationService.submit(candidate, req.jobId(), score); } Structurally, both are orchestration code calling specialized workers and combining results. The difference is that the REST controller's flow is deterministic, you wrote the sequence yourself. The agent's flow is decided at runtime by the model, and that's the part that should make any architect a little nervous, in a good way. Where the Analogy Breaks Down A few spots where "agent equals microservice" stops being useful. This is where people get burned: Contracts are fuzzy. An OpenAPI spec tells you exactly what a service accepts and returns. A tool description tells the model what it might accept and return, and the model can still call it wrong, pass malformed input, or skip the call entirely. You need validation on the way in and the way out, always. I learned this early: a permissive schema on a resume-parsing tool led to roughly 1 in 20 calls coming back with a field the model just invented. Retries aren't idempotent by default. Retry a failed HTTP call and you usually get the same result. Retry an LLM call and you might get a completely different response, sometimes a different decision . That changes how you think about "safe to retry." Latency budgets are a different thing. A microservice call might take 50 to 300 milliseconds. An LLM call with tool use and a couple of round trips can take several seconds, more if you're chaining agents. On the candidate-to-job matching flows I've built at ITJobOpportunities, that's exactly why the transport is SSE first, with polling as a fallback, instead of a blocking request. Build agentic features assuming normal API latency, and the UX will feel broken. There's no service mesh for reasoning. A mesh will tell you which service is slow. Nothing tells you why an agent decided to call tool A three times before falling back to tool B. Observability here is still catching up, and it's honestly the part of this space that annoys me most right now. The Orchestration Patterns Actually Do Transfer This is where the microservices experience pays off directly, and it's the part I lean on most when designing agent workflows. Sequence, parallel, conditional branching, supervisor/delegate: these are the same coordination patterns we used for saga orchestration and choreography in event-driven systems. On Agent UI Editor, the canvas has node types for exactly that vocabulary, because it maps cleanly onto how people already think about workflow orchestration. If you've built a Kafka-based saga where one service kicks off three others and waits for all of them before moving on, you already understand a parallel agent node conceptually. You're just replacing a consumer with a model deciding which branch to take. A supervisor agent delegating to sub-agents is basically an orchestrator service calling worker services, except the routing logic is a model instead of a switch statement. I built roughly this pattern for the ATS candidate-assessment feature at ITJobOpportunities: one orchestrating call decides whether to run the skills-matching path, the career-gap-analysis path, or both, then merges the results into a client-ready summary. The routing decision is the genuinely new piece. Everything downstream of it is code I've written a hundred times in a hundred different Spring Boot services. A Real Example: Skills Extraction and Candidate-to-Job Fit Scoring Vague architecture talk is useless without a concrete case, so here's the one I think about most because I built it and I'm the one who gets paged when it misbehaves. One of the AI pipelines at ITJobOpportunities takes a candidate's resume and a job's skill requirements and produces a fit score, matched skills, missing skills, and a short summary. Under the hood, this is really two very different problems wearing one feature's clothing: extracting skills from unstructured text, and turning matched skills into a number a human trusts. The first problem needs a model. Resumes are messy. People write "Node" instead of "Node.js," bury a skill in a project bullet instead of a skills section, or list a technology they touched once on a hackathon team. Regex and keyword matching miss too much of that. So skill extraction goes through an LLM call, with a fallback provider if the primary one times out or errors. The second problem, scoring, does not need a model at all, and I fought the temptation to hand it one. A fit score has to be explainable and stable. If a candidate refreshes the page and gets a different number, or a recruiter asks "why did this person score 78" and I can't answer without re-running an LLM call and hoping it says the same thing twice, the feature is broken even if it looks fine in a d...