Writing Agent Instructions That Do Not Rot as the Codebase Changes
Most CLAUDE.md files are accurate on day one and wrong by week six. The codebase moves, the instructions stay still, and your AI coding agent starts generating code that fits the document rather than the system.
This is not a discipline problem. Engineers are not lazy for failing to update agent instructions after every refactor. It is a structural problem: the instructions are written as prose descriptions of a system that is always in motion. Prose descriptions rot. The fix is not more diligence. It is choosing what to put in the instructions and how to source it.
What Actually Goes Stale
Not everything in an agent instruction file has the same half-life.
Structural conventions age slowly. If your team uses a layered architecture with domain, application, and infrastructure packages, that shape rarely changes in a year. Write it down explicitly. It will still be true at your next planning cycle.
Specific file paths and module names age fast. The moment someone renames UserRepository to AccountRepository during a bounded-context split, every agent instruction that references the old name starts lying. The agent does not know it is lying. It confidently generates code against a module that no longer exists, or worse, generates code that compiles but points at the wrong abstraction.
Dependency versions and API surface age fastest of all. Telling the agent to use a specific version of a library, or to call a specific method signature, is useful for about as long as that dependency stays frozen. In practice, that window is shorter than you think.
Your agent instruction file should contain almost no file paths, no method signatures, and no version pins. Write architecture, not inventory.
Source Instructions from Artifacts, Not Memory
The best-maintained agent instruction files are not maintained manually at all. They pull their content from sources that are already authoritative.
Your package.json or pyproject.toml is the authoritative list of runtime dependencies. Your openapi.yaml is the authoritative description of your API surface. Your CI configuration is the authoritative description of what a passing build requires. None of these need to be manually transcribed into a CLAUDE.md. They should be referenced, or better, included directly as grounding context when you invoke the agent.
Some teams solve this by writing a small script that assembles agent context at invocation time, pulling from project metadata, schema files, and lint configuration. The agent gets an accurate picture of the current system because the context is built from the current system. This is materially different from a static document that a human updates when they remember to.
This approach works especially well for dependency constraints. Rather than writing "use Zod for validation" and hoping that stays true, your context assembler can pull the installed version of Zod from the lockfile and inject it directly. The agent knows the exact version because the lockfile knows the exact version.
The Three Things Worth Writing in Prose
Prose instructions are indispensable for three things that cannot be expressed in schema files or configuration.
First, decision rationale. Your team chose event sourcing for the payment domain not because it was the fashionable choice, but because you needed a complete audit trail for PCI compliance. An agent that does not know this will cheerfully suggest optimizations that destroy the audit log. Write the rationale, not just the pattern.
Second, things the agent should not do. Negative constraints are almost never captured in tooling. "Never write raw SQL outside the repository layer" is enforced by convention and code review, not by your linter. These are worth writing explicitly because they represent intentional architectural fences, and the agent will breach them without guidance.
Third, team-specific vocabulary. If your team calls the caching layer "the warm tier" and that term appears nowhere in standard library documentation, the agent needs a mapping. Jargon is not decoration. It is shared context that compresses communication, and agents are not telepathic.
Keep each of these short. A rationale entry should be two to three sentences. A negative constraint should be one sentence. If you are writing paragraphs, you have the wrong audience.
Structural Guards That Prevent Drift
Even with lean prose and artifact-sourced context, instructions drift when no mechanism flags the drift.
The most reliable mechanism is a test that the agent itself can run. If your instructions say "all service classes must be registered in the container," write a test that asserts this. The test fails when a new service class gets added without registration. The agent, if it causes the failure, sees it in CI and knows its output was wrong. This converts a prose instruction into a falsifiable constraint, which is a much stronger property than a sentence in a document.
ThunderLang takes this further by letting you express what a change must satisfy as a declared intent, then verifying AI-written code against that intent before it lands. The getting-started guide shows how to attach those intent declarations to a pull request gate so that a stale instruction becomes a failing check, not a bug that ships.
Versioning your instruction file in the same commit as the code change it describes is another structural guard. When you rename a module, the commit that renames it should also update any agent instructions that referenced the old name. This is a social contract, but it is easier to enforce than "remember to update the docs," because the diff is right there and the reviewer can see both sides.
Some teams add a structured header to their instruction file that includes a last-reviewed date and the version of the codebase it was reviewed against. This sounds bureaucratic. It creates a visible signal: if the last-reviewed version is twelve major commits behind HEAD, the instructions are suspect. That signal prompts review rather than blind trust.
The File You Should Actually Ship
A defensible CLAUDE.md has the following shape.
A short preamble that states the system's purpose, its primary constraints (compliance, latency, whatever matters most), and the team's vocabulary. This is prose and it is irreplaceable.
A section that explicitly lists what the agent must not do, and why. Keep this to eight or fewer items. If you have more, you have an onboarding problem, not an agent-instruction problem.
A reference section that points to authoritative artifacts rather than duplicating them. "See openapi.yaml for the API surface" is more durable than pasting the schema.
An explicit last-reviewed marker with the Git SHA it was reviewed at. One line. It pays off every time someone opens the file six months later.
That is it. A file with this structure can be kept accurate with a fraction of the effort of a sprawling prose document, because most of what would have been prose is now sourced from artifacts the team already keeps current.
ThunderLang's intent-declaration model fits this structure naturally: the architectural constraints become declared intents, and every AI-written change is verified against them automatically rather than relying on the agent having read and remembered the prose.
The goal is not a perfect document. The goal is a document where staleness is visible before it causes damage, and where the parts most likely to change are sourced from things that change with them.
Gate your first AI change
ThunderLang lets you declare what a change must satisfy, then gates AI-written code against that intent with a verify-diff and durable proof artifacts. Try it here.
Instructions that rot silently are worse than no instructions at all: at least without them, the agent signals uncertainty instead of confidently generating against a lie.