An authoritative reference library and a gotchas file work well when one person maintains them. The problem starts when a second engineer shows up. Now there are two copies of the coding conventions, two gotchas files, and the same nasty bug gets rediscovered by each person's AI in turn. The fix is boring and effective: put the reference library in a git repo the whole team owns, point every engineer's AI at it, and treat updates to it like code.
I've run the single-owner version for years across my own stack. The team version is the same idea with one added constraint: the source of truth has to be shared, reviewed, and versioned, or it decays into five private forks that quietly disagree.
The Repo Structure
Make the reference library its own repository, not a folder buried in one service. Give it a flat, predictable layout so both humans and models can find things without a map:
rules/- coding conventions, one file per language or framework.java.md,typescript.md,sql.md. These are the "how we write code here" rules: naming, error handling, the builder pattern you standardized on, the ORM query style you actually use.patterns/- reference implementations. Not prose about patterns, the real thing: a canonical repository class, a canonical API controller, a canonical agent definition. The model copies these far more reliably than it follows a description.gotchas/- the accumulated "this looked fine and cost us a day" entries. One file per stack, or one big file if it's small. Each entry states the symptom, the cause, and the fix.stacks/- per-stack orientation docs that tie the above together.spring-boot.md,angular.md,native-apple.md. Each one is a short map: here's how this kind of project is laid out, here's what to read first, here are the traps.facts.md- canonical facts the AI otherwise guesses wrong. Product names, the domains they're served from, the version of the framework you're actually on, org-specific vocabulary.
Keep the files as plain Markdown. No custom format, no build step. The whole value is that a model can read them cold and a human can edit them in ten seconds.
How Each Person's Tooling References It
The repo is worthless if it lives on a shelf. Every engineer's AI has to load it automatically, without anyone remembering to paste it in. There are three ways to wire that up, in rough order of preference.
Git submodule. Add the reference repo as a submodule of each service repo, typically at .ai/reference or similar. The project's instruction file (the CLAUDE.md or equivalent) then points at it: "Before writing code, read .ai/reference/stacks/spring-boot.md and the relevant rules/ file." The AI follows the pointer and reads the current pinned commit. The submodule pin also means each service records exactly which version of the reference library it was built against, which matters more than it sounds like it should.
A synced path. If submodules are more ceremony than your team wants, clone the reference repo to a known location on every machine and have each project's instruction file reference an absolute or well-known relative path. I do a version of this in my own setup, where cross-project reference docs live in one place and every project's instructions point at that folder. It's simpler than a submodule but you give up the per-service version pin, so you're trusting everyone to pull.
An instruction file that points at the URL. The lightest option: the project instruction file names the repo and tells the AI to fetch the specific files it needs. This works when your tooling can pull from git on demand, and it keeps zero copies in the service repo. The tradeoff is you're depending on network access and the model actually following through on the fetch.
Whichever you pick, the instruction file is the load-bearing part. It's the thing that turns "we have a reference repo" into "the AI read it before it touched the code." Without that pointer, the repo is documentation nobody reads, which is the state you were trying to escape.
The Update Discipline
Here's the rule that makes the whole thing work, and the one teams skip: when you learn something, you update the reference repo. Not your local notes. The shared repo.
You spent a day chasing a build failure that turned out to be a framework quirk. The moment you understand it, you write the gotcha entry and open a pull request to the reference repo. You standardized a new way of handling a class of errors. You update rules/java.md and open a PR. You found that the model keeps generating a deprecated API call. You add a gotcha that names the deprecated call and the current replacement.
The test for whether something belongs in the repo is simple: would another engineer's AI benefit from knowing this, or would it make the same mistake? If yes, it goes in. This is the mechanism that stops five engineers from each teaching their AI the same lesson five times. One person pays the cost of learning it; everyone's AI inherits the answer.
I treat my gotchas file exactly this way in the single-owner case: every real production incident that a model could have avoided becomes an entry. At team scale the only change is that "write it down" becomes "open a PR," so the knowledge lands in the shared source instead of your private copy.
Review Keeps the Source Trustworthy
A shared reference repo is only useful if people trust it. The moment an engineer reads a rule, thinks "that's wrong, nobody does that anymore," and starts ignoring the repo, you're back to private forks. So review the PRs to it the way you review code.
Reviewing a reference PR is fast and worth it. The reviewer is checking a few things:
- Is this actually true, or is it one person's habit dressed up as a convention?
- Is it stated clearly enough that a model will follow it correctly? Vague rules produce vague adherence.
- Does it contradict something already in the repo? If so, resolve the contradiction now, because the AI will read both and pick one at random.
- Is it in the right file, so it gets loaded in the right context?
This review is also where a convention gets ratified. A rule that survived a PR review is one the team agreed to, which means engineers stop overriding it and let their AI follow it. That agreement is most of the value. The repo isn't just machine-readable documentation, it's the record of what the team decided.
Versioning Makes Changes Auditable
Because it's git, every change to the reference library is a commit with an author, a date, and a diff. That's not bureaucracy, it's the thing that lets you answer questions you will eventually need to answer.
When a service starts generating code in an old style, you can look at which commit of the reference repo it pinned and see it's three months behind. When a convention changes and something breaks, the diff tells you exactly what changed and when. When someone asks "why do we do it this way," git blame on the rule points at the PR and the discussion. A reference library that lives in a shared doc with no history gives you none of this. The same argument for pinning your model versions applies to the context you feed the model: if it can change silently, it will, and you'll spend the debugging time either way.
The Onboarding Payoff
The clearest return shows up when someone joins. A new engineer clones a service, their AI reads the instruction file, follows it into the reference repo, and immediately has the team's conventions, the reference implementations, the gotchas, and the canonical facts. On day one their AI writes code in your house style, avoids the traps the team already mapped, and uses the right product and version names. It inherits years of accumulated context that would otherwise take months to absorb by osmosis and code review comments.
That's the real shift. The reference repo turns the knowledge that usually lives in senior engineers' heads and old Slack threads into something a model can consume directly. The knowledge stops being tribal and starts being infrastructure. The cost is modest: one repo, a pointer in each project's instructions, and the discipline to open a PR when you learn something instead of just moving on.