Clean Architecture in the Age of AI
The Dependency Rule was always a discipline for humans. When an AI agent is the one writing the code, it becomes something more useful: a guardrail.

Clean Architecture is not a new idea. Robert C. Martin formalized it over a decade ago, and it was really just a clearer restatement of ideas that had been circulating for years under other names — Hexagonal Architecture, Ports and Adapters, Onion Architecture. The core claim is simple: draw your dependencies so they always point inward, toward business logic, and never outward, toward frameworks, databases, or UI. Keep that rule and the system stays malleable. Break it and every change starts requiring you to touch everything.
For most of my career, that rule was a discipline I imposed on human developers — including myself. Code reviews caught the controller that reached straight into a repository. Architecture standards caught the domain object that imported a framework type. It worked, but it worked because someone was watching.
Now the person writing a meaningful share of the code is an AI agent, and the watching has to happen differently. That changes what Clean Architecture is for. It stops being a style guide and starts being the thing that makes autonomous change survivable.
The circles, briefly
If you haven’t touched this pattern before, the shape is four concentric rings.
At the center sit Entities — the enterprise business rules. In a lending platform, that’s the definition of what a loan actually is, independent of any specific application. Around that sit Use Cases — the application-specific rules, the things this particular system does with those entities: originate a loan, approve an exception, calculate a payoff. Around that sit Interface Adapters — controllers, presenters, gateways, the translation layer between the use cases and the outside world. And at the outer edge sit Frameworks and Drivers — the web framework, the database, the UI toolkit, the specific vendor SDKs.
The Dependency Rule governs all of it: source code dependencies can only point inward. An entity must never know a use case exists. A use case must never import a web framework. The outer rings depend on the inner ones; the inner rings depend on nothing outside themselves.
That asymmetry is the entire value of the pattern. It means the parts of the system that encode what your business actually does are insulated from the parts that change because a vendor shipped a new SDK version.
The Dependency Rule becomes a literal guardrail
Here’s what’s different when an agent is doing the implementation. When I write code, my adherence to the Dependency Rule is a judgment call, applied consistently or not depending on how careful I’m being that day. When an agent writes code, the rule can be closer to a fact about the repository — enforced by where files live, what a linter allows to import what, what a story’s acceptance criteria explicitly forbid.
That matters because an agent that’s asked to “add a payoff quote to the loan use case” will, if left to its own judgment, sometimes take the shortest path — and the shortest path is often a direct call from application logic straight into a payment gateway’s SDK. It isn’t being sloppy. It’s optimizing for the instruction it was given, which said nothing about boundaries. The fix isn’t a smarter agent. It’s a repository structure and a set of stated constraints that make the shortest path and the correct path the same path.
This is the same lesson SOLID teaches about dependency inversion, but Clean Architecture gives it a physical location. A principle is something an agent has to remember to apply. A folder structure with an enforced import boundary is something an agent has to actively violate to get wrong — and a violation like that is exactly the kind of thing a lint rule or a CI check can catch before it ever reaches a human review.
Entities and use cases: the layer agents should touch carefully
The innermost rings are where your actual domain knowledge lives — the rules that took years of production incidents, edge cases, and client escalations to get right. This is not the layer where you want an agent making confident, silent judgment calls about what “should” happen when a loan is past due or a shipment is short.
That doesn’t mean agents shouldn’t touch this code. It means changes here deserve the tightest acceptance criteria and the most explicit review, because a subtly wrong business rule is far more expensive than a subtly wrong adapter. In backlog terms, a story that touches a use case should spell out the rule in enough detail that “the agent’s interpretation” isn’t a variable — the story states the rule, and the agent implements it. If the story can’t state the rule precisely, that’s a sign the rule wasn’t well understood before AI was involved at all, and no amount of agent capability fixes that.
Interface adapters: where agents do their best, safest work
The adapter layer is the opposite case, and it’s where I’ve found agents genuinely productive with minimal supervision. Controllers, presenters, gateway implementations, DTOs, mapping code — this layer’s whole job is translation between two well-defined shapes. It has a small blast radius by design: if a gateway implementation is wrong, it fails at the boundary, loudly, rather than corrupting a business rule three layers upstream.
This is also where swapping things happens constantly — a new model provider, a new persistence gateway, a new external API version — which makes it exactly the kind of well-bounded, well-specified, repetitive work an agent can churn through quickly. The Interface Segregation and Dependency Inversion principles from SOLID describe why this layer is safe to hand off: adapters depend on abstractions defined by the use cases they serve, not the other way around, so a bad adapter can be replaced without the inner rings ever knowing it happened.
Frameworks and drivers: the volatile ring you should expect to keep rewriting
The outermost ring is supposed to be the most disposable part of the system, and in an AI-accelerated codebase, it earns that reputation fast. Framework upgrades, model provider migrations, hosting changes, build tooling swaps — none of that should require touching a use case, and if it does, that’s a signal the Dependency Rule was violated somewhere upstream.
I’d extend this ring, in agentic systems specifically, to include the model and tool layer itself: which LLM provider answers a call, which vector store backs retrieval, which specific tool implementation a “search documents” capability resolves to. Those are framework-and-driver decisions. They should be swappable without an agent — or a human — needing to touch the use case that says “answer the customer’s question using internal documentation.”
Testing the core without the world
One of Clean Architecture’s oldest promises is that you can test business rules without a database, a web server, or a UI. That promise gets more valuable, not less, once an agent is generating a meaningful share of your test suite.
A use case with no framework dependencies can be tested with plain inputs and outputs — which means an agent can write and run that test in seconds, get a clear pass/fail signal, and iterate without spinning up infrastructure. A use case tangled up with a specific ORM or a specific HTTP client needs a much heavier harness before an agent — or anyone — gets a signal back. Fast feedback loops are what make iterative, low-supervision development safe. Clean Architecture is one of the more reliable ways to buy that speed structurally, instead of hoping the test suite stays fast by accident.
Encoding the boundary where the agent can see it
None of this works if the architecture only exists in a senior developer’s head. An agent starting a session cold has no access to tribal knowledge — it has whatever’s written down. That’s the same lesson that shaped how this site’s own backlog works: a story is either specific enough to be checkable, or it isn’t, and if it isn’t, the agent fills the gap with its best guess instead of your intent.
The practical version of this for Clean Architecture is unglamorous: state the layering in your project’s persistent context file, name the folders after the rings, and write backlog stories that say which ring they touch and what they’re allowed to depend on. “Add a payoff quote” is ambiguous. “Add a payoff quote use case that depends only on the existing LoanRepository interface, with a new gateway implementation in the adapters layer” is a contract. The first invites a shortcut through the boundary. The second makes the boundary the path of least resistance.
The lesson survives the technology cycle
I’ve watched several generations of “the old way of designing software is obsolete now” claims — service-oriented architecture was going to replace careful domain modeling, microservices were going to replace careful boundaries, low-code was going to replace careful data ownership. Every time, the underlying problem — where should this piece of logic live, and what is it allowed to know about — turned out to be the same problem wearing a new technology’s clothes.
AI agents are not an exception to that pattern. They’re an accelerant. An agent can produce a boundary violation in the time it used to take a developer to open the file. Clean Architecture doesn’t slow that down. It gives the acceleration somewhere safe to go — a structure where the inner rings stay stable while the outer rings, the ones designed to be replaced, absorb the churn AI is now capable of generating at speed.
That was always the point of the Dependency Rule. It just used to be enforced by people paying close attention. Now it’s worth enforcing in the repository itself, because the thing writing the code might not be one of them.