Agentics: Agent Optimized Codebases
Here’s a hopefully uncontroversial statement: ‘good’ code is contextual. O, sure, everyone knows how to spot bad code. If you have 12 layers of nested for-loops you are probably doing something wrong. But ‘good’ code can really vary based on a lot of different factors. Different languages have different variable naming standards, different projects have different approaches to documentation, what works for Google probably won’t work for a 2 person startup, and junior engineers and data engineers and researchers and staff software eng all have pretty different code standards.
So naturally it follows that code and codebases for agents will look a bit different than code and codebases written for humans. And in fact there is, in our experience, a lot of alpha in making some subtle changes to your codebase to make them more agent amenable. You get less slop, you can push agents to be more autonomous, and you avoid most long term codebase degradation that comes from having agents be the primary code contributors.
At Nori we call this AOC, i.e. Alexandria Ocasio- Cor — wait, no, sorry, AOC i.e. ‘agent optimized code.’
12 Grams of Carbon is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.
What makes a codebase ‘agent optimized’? Put yourself in the mind of the agent — if you were a PhD savant with anterograde amnesia and a penchant for verbosity, what would you need to get your work done?
Agent optimized codebases take advantage of agent training / post training patterns, while accounting for the fact that each agent session starts fresh with no previous memory and gets dumber the longer a session runs. Here are a list of ‘hacks’ that we’ve used over the last year to make our codebases more agent friendly. Skip to the bottom for the full list in shorthand.
Memory
Agents are a bit like the main character in Memento. They cannot remember things, so they need to have some way to recall what happened in previous sessions. The code is not enough. A ‘bug’ is when the code does not match the intent; that means you need to document ‘intent’ somewhere in the codebase.
It’s also not enough to just, like, have these intent files stored in some random s3 bucket or postgres table. The docs need to be in a place where the agent is likely to find it. There’s a reason the memento guy literally tattooed things on his body, it basically guaranteed that he would rediscover his previous notes!
We can’t tattoo things onto an agent, but the next best thing is the AGENTS.md config file — a bit of customizable text that gets prepended to every single agent session. You can tell the agent where and how to read and write memory. Our preferred memory format is to just tell the agent to read/write a docs.md file in each folder, that clearly states what is going on in that folder and how it relates to the rest of the system.
In-Distribution Tooling
Even though agents don’t have memory, they do have a bunch of information that is just wired into their weights. Any information that is baked into the model — that the model just implicitly knows without you having to explain anything — is called ‘in-distribution’. To a first approximation, anything that the agent is likely to see during training is ‘in-distribution’ and baked into the model weights. You don’t have to explain it. Anything that the agent hasn’t seen during training is ‘out of distribution.’
What sorts of things are in distribution?
- The actual coding harness! If you are using any Anthropic models, Claude Code is in distribution. The Anthropic models are explicitly trained to use Claude Code. The Anthropic models know Claude Code’s tools. Meanwhile, the GPT models know Codex, and the Cursor models know Cursor Agent.
- Certain widely used dev tools are definitely in distribution. All of the models are trained to do things with Github. You don’t have to explain how to use git or the gh command line tool. You also don’t really have to explain things like how to use github actions. You also don’t really have to explain how to use AWS, the agents just know because they have seen so many examples.
- Some programming languages are more agent-friendly than others. The agents are really good at Python, Javascript/Typescript, and Rust. They really aren’t that great at, like, Elixir or Forth. There are just way more lines of Python available on the Internet than there are lines of Forth!
In an ideal world, you would have a super detailed understanding of what each model provider is training on. In practice, you can get pretty damn close by just thinking about what the most common tools are. Like, just think about domain name registrars. Agents are going to be better at using AWS Route 53 than they are at using Namecheap, and the agents are going to be better at using Namecheap than they are using something obscure like NearlyFreeSpeech.net. Obviously!
We really really want to take advantage of agent training patterns. Using in-distribution tools will be better for the agent output, and will also save you a fair bit of cash. Generally, you can just ask an agent what sorts of tools it prefers. The first answers it gives are often the best ones.
This is also why we prefer a very simple ‘docs.md‘ memory structure. The agents are already trained to recognize, read, and write markdown!
Code Structure
Two really important things fall out of the agent’s short term memory and context rot.
- First, the agents are way better at writing code when they are dealing with small modules. An agent that only has to read 5 files to get the gist of what to do will perform better than one that has to read 500 files. The more they can ‘blackbox’ parts of the system, the better they are.
- Second, the agents are way better at writing code when they have a lot of really clear examples. If adding a new feature always follows the same pattern (add an endpoint in this folder, add a persistence class in that folder, etc.) the agent can easily fill in the gaps. Make your code boilerplate.
The most valuable system-design choice: design everything for single tenant deploys if possible. That is, each ‘buyer’ gets an entirely isolated universe — their own machines, their own databases, their own deployments. This works great for anything that is primarily used by an organization, including ~all SaaS. Doesn’t always work great for consumer software, though even there you could get away with ‘servers’ a la discord/slack.
The overarching goal is to avoid networking, concurrency, and distributed systems. The agents are pretty bad at reasoning about things like race conditions. If you can make everything run on a single ec2 instance, and then just scale up the number of ec2 instances, your agents will be much more effective and make far fewer mistakes. You can also use tools that are more in-distribution. For example, if you can design your application so it can run on a single ec2 box, you can just use sqlite as your database.
Other things that we’ve picked up over the months:
- Avoid dependency injection. The agents love using dependency injection because that is what a ‘good’ software engineer would do. But dependency injection (and, more generally, passing around functions) totally breaks code call sites. Agents depend on the code import graph to understand relevant context. Dependency injection breaks the graph.
- Use interfaces and plugin / registry patterns. If you can define a single plugin interface, and then write most of your code as plugins to that interface, your agent will have a great time adding new modularized features in the future. It’s a win-win for the agent: clear ability to blackbox context, while having a lot of great examples to go look at.
- Use really clearly named folders, with a really well thought out folder architecture. People love creating repos with a core folder that just has a bunch of random crap in it. Don’t do that. Think about the ‘swim lanes’ in your codebase, the paths where it is easy to quickly scaffold new behaviors. And then encode those in your skills and configs.
- Avoid having single really-long files. Claude Code and a few other harnesses will only show the first 2000 lines of a file. Any subsequent reads requires an additional tool call, which will sometimes trip up the models. Generally a good idea to split your files up.
Verification
I left this section last because it’s the hardest to get right, even though it is extremely important. Good agent-optimized codebases are agent-verifiable — that is, the codebase contains clear examples of how to test things end to end.
There’s an old story about a guy named Butler Lampson, one of the early folks at Xerox PARC. The story goes that Lampson wrote a highly complex, multi-thousand-line compiler or subsystem completely on a blackboard and paper over several weeks. When they finally pushed the thing to punch cards, it compiled on the first try and executed perfectly.
If you’re Butler Lampson, you don’t need to test your code. The rest of us mere mortals try to run the code before we push a PR, just to catch the obvious crap and avoid the inevitably embarrassing ‘did you even run this?’ from the code reviewer when we try to push slop.
By default, agents don’t test the code they write. Sure, they may write unit tests, but that is a far cry from hitting the binary start button and navigating through the code tree to test the brand new feature you just finished. And agents are definitely not as good at programming as Lampson.
So the solution is to give them tools to test-drive the features they build.
In some cases, this is easy. If you’re building a web app, just tell the agent to open up a scriptable browser (playwright, selenium) and click around. The more general (and complicated) case is spinning up an entire dev environment ‘clone’ of prod, telling an agent how to deploy to and manipulate that workspace, and then having the agent actually deploy against dev every time there is a relevant change.
This is another reason why microservices and multi-tenant architectures suck. Every k8s cluster that you add to the mix makes your system significantly harder to integration test.
Full list
The high level overview:
- Have your agents write and maintain docs.md files
- Use in-distribution languages (ideally ones with type checking) and tools
- Deploy single-tenant if you can.
- Use plugin / registry architectures.
- Avoid dependency injection.
- Make your folder names really descriptive. Make each folder ~single purpose.
- Make your codebase agent verifiable.
If you want to check if your codebase is up to stuff, you can try out our analyze-for-ai-compatibility skill, which is public alongside the rest of the skillsets at noriskillsets.dev.
Agentics is the study of how to use and reason about agents. If you are an expert in coding agents, or interested in learning more about agents, join our community slack. Check out our agent learning hub.
If you are trying to set up cloud agents, we can help! We build white-labeled custom cloud agents that work like Ramp Inspect for our customers. Check out noriagentic.com for more.
12 Grams of Carbon is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.
If you want to learn more about how this happens, check out my ML research paper review series or this article on deep learning.