The Codebase Is the Prompt
Every inconsistency in your repository is a bug in the specification you are handing your AI agent.
For most of the last eight months, my prompts have been the same sentence with two package names swapped.
Ok, considering `blas/ext/base/gxmy` as the closest reference
implementation, we need to refactor everything in
`blas/ext/base/gxdy`.
The difference is mostly implementation, and some other things
you will figure out when comparing.
But as far as structure goes, `blas/ext/base/gxmy` is the way
to go.
I have typed some version of that dozens of times, mostly inside blas/ext/base. The package names can look like line noise if you don't know the naming scheme. The leading letter often identifies the data type, while the rest describes the operation. You don't need to remember the details. The important part is that related packages follow recognizable patterns.
It's not a clever prompt. There's no elaborate role, long checklist, or prompting trick. It points at a package that already exists and says: follow that pattern, with these differences.
And it works surprisingly often.
It took me a while to understand why. The answer turned out to be more interesting than the prompt itself. It works because of how stdlib is built.
Thousands of siblings
stdlib is a standard library for JavaScript and Node.js focused on numerical and scientific computing. It is assembled from thousands of small packages under lib/node_modules/@stdlib, each of which can be installed independently. They are not thousands of unique snowflakes. They are variations on a much smaller number of shapes.
A strided BLAS routine looks like other strided BLAS routines. A LAPACK auxiliary routine follows the same general package structure as its neighbors, with similar files, section comments, documentation, tests, and naming conventions.
The Zen of stdlib puts it plainly:
Value consistency above all else.
That principle was written for people. A contributor who implements one package can recognize the shape of many others. Consistency becomes a teaching tool and a promise that the next package won't require starting from scratch. Nobody wrote that principle with language models in mind. But a codebase where packages consistently resemble their neighbors is also one of the best things you can give an AI agent.
When I point an LLM at blas/ext/base/gxmy and ask for blas/ext/base/gxdy, I am not asking the model to invent a package. I am asking it to compare two related operations and apply an existing pattern. The specification is not in a document. It is in the sibling package, sitting on disk, already reviewed, already merged, and already known to work.
The codebase is the prompt.
A messy codebase is a messy prompt
This is where the idea becomes uncomfortable. If precedent is your specification, every inconsistency in the repository becomes a bug in that specification. It's not only a cosmetic issue. A stale package using an old benchmark style is also a wrong answer waiting to be copied.
One of the other stdlib maintainers put this to me recently, and it changed how I thought about the problem. When a convention changes, you have two options:
- You can add another instruction telling the model to remember the exception; or
- You can update the old packages, so the exception no longer exists.
The second option is better when it is practical. Telling the model to remember is only a patch. It asks the model to carry an inconsistency and to keep track of which version is current. Fixing the codebase removes that ambiguity. The model can simply look around.
The same is true for people. Every "ignore that file, it uses the old approach" adds a small cost for every future contributor. We have always known this. AI agents only make the cost appear faster because they will find the old example and treat it as evidence.
I saw this during a recent modernization effort. Benchmark names used to be built by using string concatenation, and we have since migrated them to use string interpolation with format(), a pure JavaScript implementation of an API similar to C's printf. While that migration was in process, the tree had a mixture of both conventions. An agent adding benchmarks to a new package copied whichever style its nearest neighbor happened to use, and it was right to. Nothing in the old file says it's the old way.
Do not put off to tomorrow what you can fix today, because tomorrow an agent may read every version and assume they are all equally valid.
After stdlib participated in the METR study, which reported that experienced developers were 19 percent slower on tasks where they could use AI, Philipp Burckhardt wrote a reflection identifying two sources of friction: gaps in project conventions, and the blank slate from which an agent begins each session.
I think that diagnosis is right. An agent may not reliably remember an instruction from a previous session, but it can inspect the code in front of it. Consistency makes the right convention visible wherever the agent happens to begin.
This is why much of my work this year has looked boring from the outside. Updating benchmark names. Modernizing tests. Cleaning up old conventions across hundreds of pull requests. I used to think of that work mainly as maintenance. I now also think of it as improving the examples that every future contributor, human or otherwise, will learn from.
What isn't in the package
Precedent is powerful, but it has a limit. Code shows you the what. It rarely shows you the why, and it doesn't always show you what to verify. A model can produce a correct implementation and still have no idea which checks to run before opening a pull request.
But that knowledge isn't only in people's heads. Much of it is already written down in the repository, just not inside the package you happen to be editing.
stdlib installs a pre-commit hook that runs more than a dozen checks before a commit is allowed. It validates formatting, lints JavaScript separately for source, tests, examples, and benchmarks, checks the package manifest, lints Markdown and TypeScript declarations, and verifies license headers.
At the time of this post, there are 126 custom lint rules written for this project alone, covering conventions a general linter has no reason to know: how a JSDoc block should be shaped, how doctest output should match, how a namespace should export its packages. There are style guides for every language in the tree, and a contributing guide describing what a reviewable pull request looks like.
None of that sits inside the package. All of it is machine readable.
That distinction matters more than I expected. An agent that doesn't know that these exist will guess, and its guesses will look plausible enough to survive a quick review. An agent that runs the hook is told exactly what is wrong, in the project's own words, without anyone writing a prompt about it.
So the most useful thing you can do for an agent is not to describe your conventions. It is to make them executable. A convention that lives in a reviewer's memory has to be repeated every time it's broken. A convention that lives in a lint rule enforces itself, for people and machines alike.
What tooling cannot decide is which existing package to imitate in the first place. Choosing the right reference is the one piece of guidance I had to write down for the model myself.
Finding the right sibling
The most useful thing I learned while writing that guidance is that the valuable part is not another list of style rules. It is teaching the model how to find the right precedent.
For a new package, the guidance is a simple ladder:
- Look for the same operation at a different precision.
- If that does not exist, find a routine with the same package shape and the same precision.
- Otherwise, find the closest operation in the same family.
Then, before editing anything, state the chosen reference and explain why it was selected.
That is the core of my repeated prompt, generalized. Instead of requiring me to know that blas/ext/base/gxmy is the right package to follow, the model has a process for finding the right package itself.
The final instruction matters more than it may appear. Asking the model to state its reference before writing code gives me a cheap place to stop it. If it chooses the wrong sibling, I find out in one sentence rather than after reviewing a four-hundred-line diff. Many of the poor outputs I have received began with the wrong reference. That mistake was visible before any code needed to be written.
What I would tell a maintainer
If you maintain a project and are thinking about how to prepare it for AI-assisted contributions, this is what I currently believe.
- Consistency is infrastructure. It helps people understand a project, and it gives machines reliable examples to follow.
- Fix the codebase instead of documenting every exception. When practical, migrate old conventions rather than asking every future contributor to remember which examples should be ignored.
- Write down the process, not only the style. A linter can enforce formatting. It can't explain which reference to choose, which checks prove the work, or which changes don't belong in the same pull request.
- Make the agent commit to a reference first. One sentence before any code can prevent an entire incorrect implementation.
- Keep a real reference for numerical work. Convention can make code look correct. Differential testing against trustworthy ground truth is what tells you whether it behaves correctly.
- Version AI instructions like code. They become outdated. Give them identifiers, an index, and a history so contributors can tell which guidance is current.
The accident that paid off
I came into this year thinking the interesting question was how to prompt well. I no longer think that is the main question. Prompting is a thin layer over a more basic question: does your project have anything coherent to point at?
stdlib is unusually good at this, although the benefit to AI was accidental. Years of insisting that related packages look and behave consistently, entirely for the benefit of human contributors, created a useful machine-readable specification as well. The best AI investment the project made happened long before anyone was thinking about AI. It was called "value consistency above all else."
That leads to something optimistic. Making a codebase legible to a machine is not separate from making it legible to a person. It is the same work: consistent structure, current conventions, documented processes, and real verification.
We have always known these things matter. We have not always treated them as urgent. Now something is reading all of it and using what it finds to shape the next contribution.
Better make it good.
Karan Anand is a core contributor of stdlib, a JavaScript library for numerical and scientific computing.
stdlib is an open source software project dedicated to providing a comprehensive suite of robust, high-performance libraries to accelerate your project's development and give you peace of mind knowing that you're depending on expertly crafted, high-quality software.
If you've enjoyed this post, give us a star 🌟 on GitHub and consider supporting the project. Your contributions and continued support help ensure the project's long-term success and are greatly appreciated!