Your Codebase Is an API for Agents Now
A few months ago I stopped writing code the way I used to. Most of the changes that land in my repositories now are typed by an agent, not by me. I describe the outcome, the agent reads the repo, makes a plan, edits the files, runs the tests. I review and steer.
That sounds like a productivity story. It isn't, or at least that's not the interesting part. The interesting part is that it quietly changed who the codebase is written for.
For thirty years, source code had one primary reader: a human engineer who would be around for a while. That human accumulated context. They remembered why the auth module was split in two. They knew the one config flag you must never touch on Fridays. When something was ambiguous, they asked the person at the next desk. The code could afford to be a little vague because there was a warm body holding the missing half.
The agent has none of that. It shows up to every session with amnesia. It rebuilds its entire understanding of your system from whatever is on disk, plus whatever you bothered to write down. It cannot lean over and ask a teammate. It cannot remember last Tuesday.
So the repository is no longer documentation about the system. It is the interface the agent programs against. And like any interface, it can be well-designed or badly designed — and you pay for the difference on every single call.
Ambiguity Used to Be a One-Time Cost
Here is the shift that took me a while to feel in my gut.
When a human maintains a codebase, ambiguity is amortized. The first time someone is confused about a weird folder named utils2, they spend an afternoon figuring it out, and then they know. The cost is paid once and stored in their head. The second year, that folder costs nothing.
When an agent maintains a codebase, there is no head to store it in. Every session starts cold. That utils2 folder confuses the agent today, and it will confuse a fresh session next week, and the week after. The cost of ambiguity is no longer paid once and remembered. It is paid every session, forever, by every agent that ever touches the file.
This inverts a lot of old intuitions. "We all know what this does, no need to write it down" was a reasonable thing to say on a stable human team. It is now a recurring tax. Tribal knowledge isn't a minor inefficiency anymore — it's the single most expensive thing in the repo, because the tribe forgets between every meeting.
The good news is that the fix is the same fix that always made codebases nice for humans. Clear names. Obvious boundaries. Behavior you can verify. The difference is only that the agentic era removes your ability to get away with skipping it. A messy repo that a senior human can still navigate by memory is a repo an agent will get lost in, repeatedly, at scale.
I'll give you the acceptance test I actually use now, before any change ships:
Would a fresh agent session, given only this repository, converge on the correct understanding and the correct next change?
If the answer depends on something living in my head, a Slack thread, or a teammate's memory, the repo has a bug — even if all the tests pass.
The Repo Exposes Three APIs
Once you treat the repository as an interface, it helps to notice that it actually exposes three distinct APIs to the agent. They fail in different ways, and you design them differently.
The directory layout is the navigation API.
The first thing an agent does is read paths. Before it reads a single function body, the folder structure has already told it a story about where things live, what owns what, and where it should go to make a change. A path like features/billing/engine/calculateInvoice.ts is doing real work: it says billing owns this, it's pure domain logic, it's probably unit-tested, and it shouldn't be importing React or touching the network. The agent can infer all of that without opening the file.
Now compare that to a file called src/components/Dashboard.tsx that fetches data, reads localStorage, computes pricing rules, renders charts, and checks user permissions. The path tells the agent nothing true. Worse, it actively lies — it says "this is a dashboard component" when it's really half the application. An agent asked to change the pricing rule has to read two thousand lines to discover that the rule even lives there. Then, under context pressure, it adds the next rule in the same file, and the file gets worse.
Vague top-level folders — lib, helpers, misc, common, new, v2 — are navigation-API endpoints that return garbage. They don't state ownership or allowed imports. They become dumping grounds precisely because their names give the agent no reason not to dump.
Contracts and import boundaries are the constraint API.
The navigation API tells the agent where to go. The constraint API tells it what it's allowed to do once it's there. What may import what. Which values are stable promises other code depends on. What shape data has when it crosses a boundary.
The deadly failure here is the implicit contract — the one that exists only in the collective memory of the team. The order of columns in a CSV that's documented only in the parsing code. The event name 'user.updated' typed as a bare string in eleven different files. The storage key that three features write and nobody owns. Humans survive these because someone remembers. An agent changing one of them has no way to know it just broke a producer in another service, because nothing in the repo told it the boundary existed.
The fix is to make the boundary a thing the agent can see and the machine can check: a typed contract, a schema, a constant it has to import instead of a string it can retype slightly wrong. I'm going to spend a whole piece on this, because it's the highest-leverage move available — contracts over tribal knowledge is the difference between an agent that edits safely and one that breaks things it can't see.
Verification commands are the validation API.
Finally, the agent needs to know whether it succeeded. Humans can squint at a diff and feel reasonably sure. An agent that's the only judge of its own work will overfit to its own assumptions and declare victory — it wrote the code believing it was right, so of course re-reading it confirms it's right.
What rescues you is a deterministic way to ask the repo "is this actually done?" One command — make verify, pnpm verify, whatever — that runs the types, the lint, the tests, the contract checks, the architecture rules, and comes back with a crisp pass or fail the agent didn't get to vote on. "Done" stops being a feeling and becomes an exit code. I'll come back to this too, because separating who writes the change from who certifies it turns out to be one of the load-bearing ideas of the whole era — that's letting the agent prove it's done.
This Is the Same Diagram I Already Use for Humans
The part that took me longest to accept is that none of this is exotic. I kept waiting for "architecture for AI agents" to require some new alien shape. It doesn't.
The structure that's good for an agent is the structure that was always good for a new engineer joining a large team — one with perfect recall of public APIs and zero memory of your hallway conversations. Pure logic separated from side effects so it can be tested without provisioning the world. Explicit module boundaries so a change stays local and reversible. Names that mean what they say. Verification you can run with one command. We always knew this was the good way to build. We just had the slack to skip it, because a senior human could hold the missing structure in their head and keep the thing running anyway.
The agent removes the slack. It can't hold the missing structure. So the messiness that a strong team quietly absorbed now shows up as a bug, every session, at the speed the agent works.
I find that clarifying rather than depressing. It means I don't have to learn a separate discipline called "writing code for AI." I have to do the thing I already knew was right and stopped having an excuse to avoid. The maintainer changed, and the maintainer is unforgiving about exactly the corners I used to cut.
Which is the whole reframe. Stop thinking of the repository as a record of what you built. Start thinking of it as the API surface that the thing now building it has to program against. The cleaner that surface, the smarter your agent appears to be — not because the model got better, but because you stopped making it re-derive your mess on every call.
The model is rented and improving on its own schedule. The interface is yours. That's the part you get to design — and increasingly, it's the part that decides how far the agent gets.
This is the first of four pieces on building repositories that AI agents can safely maintain. Next: why every boundary has to become a contract. It pairs with the harness essays — the model isn't the product, the harness is — and with when software becomes disposable, which is the same shift seen from the product side.