Lean in
I’ve been watching how the agents move through a codebase lately, and one thing keeps nagging at me.
Nobody tells an agent to “go edit the service layer.” They tell it to “fix checkout.” The unit it works on is a feature, not a layer. So if you know an AI is going to touch the code, I think the most useful thing you can decide up front is to put everything for one capability in one place. Feature-first.
An agent doesn’t have a head with limited room. It has a context window, and it reasons over what’s written in front of it. A task maps to a feature, so feature-first lines the shape of the code up with the shape of the work. The agent loads one folder and holds the whole thing (handler to response, logic to test) with nothing irrelevant in the way. That’s the whole trick really.
And it’s worth saying why this matters now and didn’t before. For us, reading and writing code was the expensive part, so we abstracted to spend less of it. For an agent writing is cheap, and the expensive thing is behavior it can’t see. The trade runs the other way now.
Layer-first organization fights it. All controllers in one folder, all services in another, all repositories in a third, and now a single feature (controller to service to repository, through a mapper, out as a DTO) is six files scattered across the tree. A human with “go to definition” hops through it in seconds. An agent has to reassemble the feature from fragments every single time, burning half its context rebuilding a flow that could have just lived in one folder.
The neat part is that most of what people prescribe for “AI-friendly code” falls out of this for free. Locality of behavior is just what a slice is. The “should we extract this into a shared helper” debate softens, because a little duplication across features is cheaper than the wrong abstraction, now more than ever. And blast radius gets visible: change a slice and you touched exactly one feature, not a shared service quietly feeding six others.
Underneath all of it is the thing agents are genuinely worst at: control flow you can’t see. A decorator that silently adds an auth check. A router that turns a folder into an endpoint with nothing in the code saying so. An ORM firing a query you never wrote. The agent reads the function body, sees no auth check, and tells you with great confidence that the endpoint is open, because what actually happens just isn’t in the tokens it’s looking at. The more behavior lives right there in the slice, the less of it is hiding in the framework’s machinery.

Strategic Conquest, somewhere in the 90s. You can only act on the part of the map that’s lit up, the rest is just dark you have to guess at. An agent reads your code a bit like this.
You can take this further than folders, letting features hook into each other instead of calling down through layers. Done carelessly that’s the exact thing I just warned about, behavior wired in from somewhere the code never mentions. The fix is to write the wiring down: give each feature a little contract (what it provides, what hooks attach to it, how to add one) sitting right next to the code. Then the hooks aren’t magic, they’re documented. I sketched out what that can look like in a feature-first structured architecture card.
One caveat keeps it from going silly: it’s about feature logic. The cross-cutting stuff (auth, the database connection, logging) still wants a stable shared core, and that’s fine, because a reliable shared thing is a trustworthy black box, not the magic kind. The skill is drawing the line.
So it’s not “abandon structure”, and not “the old wisdom was wrong” either. The old wisdom was just the right answer to a constraint the agent doesn’t share. Match the code to the agent’s unit of work, keep the behavior explicit and local, and stop paying for the cleverness it can’t see through anyway…