AI coding agent repository memory belongs in version-controlled repository files, not in an earlier chat. Java and React teams can make important context durable with a simple ownership model: instructions explain how work should be performed, Architecture Decision Records (ADRs) explain why durable technical choices exist, and tests verify behavior and architecture that must remain true.
Coding agents can receive conversational context and, depending on the product, may use project-memory features. That can be useful, but it is not a replacement for reviewable engineering knowledge. Repository files travel with the codebase, can be changed through normal review, and remain available when a team changes contributors, sessions, or coding tools.
The aim is not to document every implementation detail. Preserve the information that cannot reliably be derived from nearby code: real validation commands, module boundaries, compatibility constraints, intentional trade-offs, and user-visible contracts. This gives an agent useful context while giving human reviewers a clear place to inspect and improve it.
Repository Memory Is Durable Engineering Context
Repository memory is the collection of maintained files that helps contributors understand how a codebase should change. It reduces reliance on remembered conversations, ticket comments, or assumptions made from a small part of the repository.
GitHub Copilot supports repository-wide custom instructions and path-specific instructions. GitHub recommends keeping project-specific information such as build, test, lint, runtime, and architectural guidance in version-controlled instruction files. That gives an agent concrete project context instead of requiring it to infer commands and conventions. GitHub documentation on custom instructions
Claude Code also distinguishes between conversational context and persistent project context, including repository files such as CLAUDE.md and scoped rules. Its documentation makes a useful boundary clear: project instructions provide context to the agent, but they are not hard enforcement. Claude Code memory documentation
That boundary leads to a practical division of responsibility. Put operating guidance in instructions. Put architectural rationale in ADRs. Put requirements that can be checked automatically into tests or other automated validation. Each artifact answers a different question, so each can stay short and purposeful.
Use Three Layers of Repository Memory
| Layer | Primary question | Typical contents | Risk when absent |
|---|---|---|---|
| Instructions | How should this change be made and validated? | Commands, tooling, conventions, module maps, local workflow | Contributors guess how the repository operates |
| ADRs | Why was this approach chosen? | Context, decision, status, consequences, links | Important trade-offs are rediscovered without their history |
| Tests | What behavior or boundary must remain true? | User behavior, contracts, dependency rules, architecture checks | Changes can appear plausible while violating an intended constraint |
Instructions define the operating rules
Repository instructions should make the opening work on a task predictable. They should identify where relevant code lives, how to run the narrowest useful validation, which broader commands are required before review, and where local rules differ.
For a Java service, useful instructions can cover the supported runtime, Maven or Gradle commands, module layout, migration conventions, and the location of architecture tests. For a React application, they can identify the package manager, test commands, component or routing conventions, and local rules for feature packages or a design system.
Write instructions as actionable repository facts. Generic statements such as “write clean code” are too vague to guide a change. A statement such as “run the affected JUnit test class and the architecture test suite before changing package dependencies” tells a contributor what to do and where the relevant guardrails are.
Scoped instructions are useful where rules genuinely vary. A root file can provide the repository map and common validation commands, while local files document conventions for a Java integration module, a React component package, generated code, or a migration area. This keeps broad context available without forcing every task to carry every subsystem’s details.
ADRs preserve architectural intent
ADRs record decisions whose rationale should survive the pull request that introduced them. The ADR approach described by Michael Nygard uses lightweight text records in source control to capture the context, decision, status, and consequences of architecture choices. Cognitect: Documenting Architecture Decisions
Create an ADR when a choice has meaningful trade-offs, affects more than one area of the system, constrains later work, or is likely to be challenged again. Examples can include a module boundary, a dependency direction, an API compatibility policy, or a rule about where persistence access belongs. Routine implementation details usually do not need an ADR.
Code may show what exists, and tests may show what must continue to work, but neither necessarily explains why one option was selected over another. Thoughtworks describes lightweight ADRs as an evolutionary architecture practice because they retain this architectural reasoning alongside the system as it changes. Thoughtworks Technology Radar on lightweight ADRs
A compact ADR can include a title and status, the context and constraints, a direct decision statement, and the consequences of that choice. Links to affected modules, related ADRs, migrations, or enforcing tests make the record easier to use during later changes. When a decision changes, retain the earlier record and link to the record that supersedes it rather than replacing its history.
Tests make important memory executable
Tests are the enforcement layer. They give contributors feedback when a change breaks a behavior or crosses a boundary that the team has decided to protect. They are especially valuable for agent-assisted work because a locally plausible implementation can still violate a repository-level contract.
Testing Library recommends tests that resemble how users interact with an application instead of tests tightly coupled to component implementation details. In React code, this makes behavior tests more resilient to internal refactoring while retaining confidence in user-facing outcomes. Testing Library guiding principles
A React behavior test can locate a meaningful control, perform an interaction, and verify a visible result or accessible state. That preserves a product contract while leaving room to reorganize components, hooks, or local state. Tests that depend on incidental markup structure, private state, or styling details are more likely to create churn when implementation changes without changing the intended user outcome.
For Java, ArchUnit supports architecture tests for package structures, layer isolation, and dependency rules through Java tests, including JUnit integration. ArchUnit documentation
If an accepted decision says one package must not depend on another, or that a layer must remain isolated, encode that condition in an architecture test where practical. Conventional unit tests may demonstrate a feature works while missing an unwanted dependency introduced during a refactor. Architecture tests provide a focused executable check for that different class of requirement.
Decide What Belongs Where
Repository memory becomes harder to trust when the same rule is copied into a README, ticket, chat, code comment, and test without clear ownership. Assign each kind of information to the artifact that can best maintain it.
- Use instructions for information needed to navigate, modify, validate, or review the repository.
- Use an ADR for a durable decision and its trade-offs.
- Use a test for a requirement that can be detected automatically and warrants enforcement.
- Use more than one layer only when a decision needs both explanation and enforcement, or when contributors also need workflow guidance.
Consider an API contract used by a Java backend and a React client. Instructions can identify the contract-test command and the locations of the participating modules. An ADR can explain the compatibility decision and its consequences. Tests can verify the response and the client behavior that depends on it. The same concern appears in three places, but each entry has a different job.
This model also improves review. A reviewer can ask separate, concrete questions: Are the operating instructions still accurate? Does the decision record explain the trade-off? Does an automated check protect the requirement? That is more useful than treating repository documentation as a single large, passive body of text.
A Practical Starting Structure
Start small enough that the owners of the code can keep the files current. The exact filenames will depend on the coding tools in use, but the structure can remain consistent across a combined Java and React repository.
- Root instructions: repository purpose, supported tools, primary validation commands, a module map, and contribution expectations.
- Scoped instructions: local guidance for backend modules, frontend packages, migrations, generated code, or integration-test areas.
- ADR directory: lightweight decision records with stable identifiers, statuses, and links to related records.
- Java architecture tests: a dedicated location for rules derived from accepted architectural decisions.
- React behavior tests: feature-level tests that protect visible workflows and accessible outcomes.
- Aligned automation: continuous integration should run the validation that instructions tell contributors to run.
A root instruction file should be a map, not a handbook. Keep it concise and point to local rules and ADRs when a task requires more context. This reduces the chance that important guidance is buried in a long general document or that unrelated material distracts from a focused change.
Build the System Through Real Changes
The most practical way to establish repository memory is to improve context around a recurring type of change. Choose a common Java endpoint change, a React feature update, or a dependency-boundary issue that reviewers repeatedly explain.
First, inventory the validation commands maintainers actually use for that work. Record the relevant commands, supported tooling, and code locations in repository instructions. Next, identify one recurring architecture rule or trade-off. Create or update an ADR that describes the context, chosen direction, and consequences without inventing rationale that the team cannot verify.
Then decide whether the rule can be checked automatically. A Java dependency rule may fit an ArchUnit test. A React workflow may fit a Testing Library test that exercises meaningful user behavior. Link the ADR to the test when the test enforces one of its consequences, and add local instruction guidance only where contributors need to know how to run that validation.
Finally, use the structure during a small change and remove anything vague, outdated, or duplicated. Repository memory is valuable when it stays close to the work. It should be reviewed and maintained through the same ownership practices as the code it describes.
Common Failure Modes
Assuming instructions guarantee compliance
Instructions provide context, but they are not enforcement. Claude Code explicitly distinguishes project instructions from hard programmatic controls. Important build, behavior, or architecture requirements need automated checks, review gates, or other enforcement mechanisms. Claude Code memory documentation
Using ADRs as a substitute for tests
An ADR can explain that a boundary matters, but it cannot detect a violation. When a consequence can be expressed as a dependency rule or behavioral contract, an automated test gives the decision operational force.
Using tests as the only explanation
Tests can reveal what is protected, but they may not explain the trade-offs behind an architecture or compatibility policy. An ADR provides that missing rationale and gives later contributors a place to evaluate whether the decision should remain in force.
Writing broad guidance that no one can maintain
Instructions become less useful when they attempt to explain every subsystem. Keep general guidance short, scope detailed rules near the relevant code, and remove information that is no longer actionable. A smaller, maintained set of files is more useful than a comprehensive but stale handbook.
FAQ
Should every AI-assisted change create an ADR?
No. ADRs are for consequential decisions with durable trade-offs. A routine bug fix or isolated component adjustment normally needs accurate instructions and appropriate tests, not a new architecture record.
Can a README replace repository instructions?
A README can support onboarding, but repository instruction files are intended for concise, task-oriented guidance that contributors and coding agents need while modifying code. Avoid maintaining the same detailed procedure in both places unless there is a clear owner and reason to do so.
Are well-covered tests enough?
No. Tests protect behavior and enforceable constraints, but they do not always preserve the reason behind an architecture choice. ADRs retain that rationale, while instructions tell contributors how to work within the repository.
What should a team do first?
Add a short, accurate instruction file with the real build, test, lint, and validation commands. Then select one frequently discussed architectural rule, record its rationale in an ADR, and add an automated check when the rule can be tested.
Sources
- GitHub: Adding repository custom instructions for GitHub Copilot
- Claude Code: How Claude remembers your project
- Cognitect: Documenting Architecture Decisions
- ArchUnit: Unit test your Java architecture
- Testing Library: Guiding Principles
- Thoughtworks Technology Radar: Lightweight Architecture Decision Records
Editorial note: AI assisted with research and drafting. Sources were selected for verification.
Full-Stack Developer & Solutions Architect · Casablanca, Morocco
8+ years building Java/Spring Boot/Angular enterprise solutions. Former Senior Software Engineer at NTT Data and Satec. Authorized Google Workspace and Microsoft 365 Partner for Morocco.