I started noticing a pattern while working with AI coding agents. The agent could write code surprisingly quickly, but the quality of the change depended heavily on how well it understood the repository. That led me to look at something that had existed in our repositories long before AI coding agents became common: engineering context.

Things like:

  • why a service is structured in a particular way
  • which layer should own a database operation
  • how services communicate
  • which queues or events are involved
  • how to run a particular test
  • which constraints a developer needs to know before changing something
  • what authentication method should be strictly followed while exposing APIs

Some of this knowledge was in documentation | Some was in tests | Some was in architecture decisions done when developers were not in the meeting | lot of it was simply in people’s heads | and at last only one of the technical expert knew all the gotchas….. on how something works end to end :)

The problem became more obvious when I started using AI coding agents. An experienced developer can often fill in missing context from his working with service experience.

An agent cannot.

If the repository doesn’t contain an important constraint, the agent has to discover it by exploring the codebase—or make an assumption. That made me look at AGENTS.md and folder-level instructions differently.

But I didn’t want to introduce another rule saying:

“Developers must keep AGENTS.md updated.”

I had seen enough engineering processes to know what happens next. It becomes another checkbox which developers want to always bypass for a urgent PR which need to merged and deployed to production.

So I changed the problem —> Instead of asking developers to remember documentation, I made engineering context part of the GitHub pull-request workflow.


The problem I was trying to solve

The problem wasn’t that developers didn’t know documentation was useful. The problem was the timing. A developer makes a change today. The benefit of updating context might happen weeks or months later. For example, imagine a developer changes how an event is processed:

1
2
3
4
5
Service A
SQS
Service B

The code change is immediately visible. The feature is immediately testable.

But the knowledge that:

“Service B expects this event to be processed in this particular way”

may only become important when somebody changes the consumer six months later. The developer making today’s change has a very clear cost: Spend time updating context

The benefit is uncertain: Somebody might need this information later

That is why I didn’t want to rely on developer discipline. I wanted the engineering workflow itself to catch it.


I started with the PR

The change I made was deliberately simple. Every pull request now has an Engineering Context section. The developer has to make one decision:

1
2
3
4
Engineering Context

- [ ] No engineering context changed
- [ ] Engineering context updated

If there is no context change, the developer explains why.

If there is a context change, the developer points to what was updated.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Engineering Context

[x] Engineering context updated

Context updated:

- services/orders/AGENTS.md
- contracts/order-created.yaml

Why:

The order event now contains a new field and the consumer
behavior has changed.

Or, for a normal internal refactoring:

1
2
3
4
5
6
7
8
Engineering Context

[x] No engineering context changed

Reason:

This is an internal refactoring. API behavior, event contracts,
service boundaries and development workflow remain unchanged.

This small change was important.

I wasn’t asking:

“Did you update the documentation?”

I was asking:

“Did the engineering knowledge around this change affect others (your team + AI coding Agent)?”

This is simply becuase I dont want someone to change the application behaviour without proper documented context around it.


I defined what actually requires context review

I didn’t want every code change to trigger a documentation exercise.

So I defined the types of changes that should make a developer stop and think about context.

The areas I focused on were:

Service architecture

1
2
3
4
New service
New dependency
Changed service boundary
Changed data flow

Asynchronous processing

1
2
3
4
5
New queue
New event
Changed producer
Changed consumer
Changed retry behavior

API and contracts

1
2
3
4
5
API behavior
API contract
Event schema
Authentication
Authorization

Data

1
2
3
4
Database behavior
Schema changes
New data dependency
Changed data flow

Development and operations

1
2
3
4
5
Build process
Test process
Deployment assumptions
Required configuration
Operational constraints

A variable rename doesn’t require an AGENTS.md update. A new SQS consumer might.

That distinction keeps the process practical.


I also changed what I put in AGENTS.md

One thing became clear to me while doing this. AGENTS.md should not become another README. don’t want it to describe every directory or repeat what the code already tells me.

For example, this doesn’t provide much value:

1
2
3
4
5
The Orders service contains order-related code.

The service is written in Java.

Tests are located in the test directory.

An agent can discover most of that.

Instead, I used it for things that are easy to miss and important to follow.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
## Orders Service

## Important constraints

- Database writes must go through OrderRepository.
- Events are published only after the transaction commits.
- SQS consumers must be idempotent.
- Do not call the Payment service directly from the repository layer.

## Testing

make test-orders
make test-orders-api

## Contracts

API:
contracts/orders.yaml

Events:
contracts/order-events.yaml

These are not descriptions of the code. They are instructions and constraints for changing the code. That distinction helped me keep these files small and precise.


I didn’t put everything into one AGENTS.md

As the repository grows, a single context file can become a problem.

Imagine a root file containing information about:

1
2
3
4
5
6
Orders
Payments
Reporting
Infrastructure
Data pipelines
Frontend

Eventually it becomes a large document that nobody wants to maintain.

So I use context closer to the code it applies to.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.
├── AGENTS.md
├── services/
│   ├── orders/
│   │   └── AGENTS.md
│   │
│   └── payments/
│       └── AGENTS.md
└── infrastructure/
    └── AGENTS.md

The root context contains repository-wide rules. The service-level context contains rules specific to that service. That also means the developer working on Orders doesn’t have to understand the entire repository’s engineering rules just to make a change to Orders.


I made ownership part of the same model

There was another problem I wanted to avoid. I have experienced and discussed this widely across many new and experienced developers.

If we say:

“Keep the documentation updated.”

Who owns it? | The developer? | The architect? | The platform team? | A documentation team?

I didn’t want to create a separate documentation ownership model. Which is bound to fail!!

The team that owns the code should also own the context around that code. So I used GitHub CODEOWNERS.

For example:

1
2
3
/services/orders/    @orders-team
/services/payments/  @payments-team
/infrastructure/     @platform-team

Now the ownership model is simple:

1
2
3
Code ownership
      +
Engineering context ownership

When the service changes, the same people responsible for the service review whether its context is still accurate. Also they make sure a member from other team doesnt merge anything unknowingly without their approval.


Then I added the GitHub check

The PR template creates the human decision. The GitHub workflow creates the enforcement.

I don’t check whether every PR changed AGENTS.md. That would be too simplistic.

Instead, I look for changes that are likely to affect engineering context.

For example:

1
2
3
4
contracts/**
database/migrations/**
infrastructure/**
services/**/consumer/**

If a PR touches one of these areas, the context check becomes relevant.

This lets me distinguish between:

1
Small implementation change

and:

1
Potential system-level change

without making every PR heavier.


The important part: I don’t automatically fail the PR

Suppose a developer changes an SQS consumer. The workflow detects that it is a context-sensitive change.

But the developer says:

1
No engineering context changed.

I don’t automatically reject the PR.

I ask for the reason.

For example:

1
2
3
4
This fixes error handling for an existing message type.

Queue name, event schema, retry behavior and consumer contract
remain unchanged.

That’s a valid engineering decision.

The problem is:

1
2
3
4
No context change.

Reason:
N/A

That isn’t useful.

So the workflow is enforcing a decision and explanation, not a Markdown-file modification.

That was important to me.


This also changed how I think about AI review

Once I had the basic PR workflow in place, there was an obvious next step. AI coding agents are already good at exploring repositories. I’ve seen this first-hand while working on API and contract testing. Instead of immediately asking an agent to write tests, I first asked it to understand the end-to-end data flow.

The system had a microservices/SOA architecture with:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
API
Service
Database
SQS
Consumer
Data pipeline
Downstream processing

The agent analysed the repository and created an end-to-end data-flow diagram. I then asked it to analyse the existing tests. It identified that the existing test coverage was low. That was useful because the agent wasn’t simply generating code. It was first building a model of the system.

That same capability can be used during a PR.


I use AI as another context reviewer

For a context-sensitive PR, an AI reviewer can look at:

1
2
3
4
5
6
7
Changed code
    +
Changed contracts
    +
Existing AGENTS.md
    +
Tests

and ask:

Does the PR appear to introduce engineering knowledge that is not reflected in the repository context?

For example, imagine the code changes from:

1
orders-created

to:

1
orders-created-v2

but AGENTS.md still says:

1
Orders are published to orders-created.

The useful result isn’t:

“AGENTS.md was not modified.”

The useful result is:

“The documented queue/event name appears inconsistent with the current implementation.”

That is what I want automation to catch.

Context drift, not Markdown changes.


I also keep different types of knowledge in different places

Another thing I learned while implementing this is that AGENTS.md shouldn’t own every kind of engineering knowledge.

I think about the repository roughly like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
AGENTS.md
How should I work here?

ADR
Why did we make this decision?

API/Event Contract
What is the interface?

Tests
What behavior do we enforce?

Runbook
How do we operate it?

For example, if we decide to introduce SQS between two services, I don’t want to put the entire architectural reasoning into AGENTS.md.

The ADR explains why. The contract explains what is exchanged. The tests verify the behavior. AGENTS.md can contain the important constraint:

1
2
3
4
5
6
Order → Fulfillment is asynchronous.

Messages must be idempotent.

See:
docs/adr/0042-orders-fulfillment-async.md

This keeps each artifact focused.


The workflow I ended up with

The resulting flow is simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Developer changes code
Opens GitHub PR
Engineering Context question
Did engineering knowledge change?
       ┌──────────────┐
       │              │
      No             Yes
       │              │
 Explain why     Update context
       │              │
       └──────┬───────┘
        GitHub checks
         CODEOWNERS
       Context review
            Merge

AI can sit alongside this process and look for context drift.

It doesn’t replace the developer or service owner.


What I deliberately did not do

I didn’t create a rule saying:

Every PR must update AGENTS.md.

—> I didn’t create a 20-question documentation checklist.

—> I didn’t create a central documentation team.

—> I didn’t ask developers to document every implementation detail.

—> I didn’t ask AI to automatically rewrite AGENTS.md after every change.

And I didn’t treat AI-generated documentation as automatically correct.

All of those approaches can create a lot of activity without creating useful engineering context.


The rule I ended up following

The simplest rule I can give a team is:

If a future developer or coding agent would need to know something new to safely change this part of the system, that knowledge should be captured somewhere in the repository.

Not necessarily AGENTS.md.

It could be:

1
2
3
4
5
6
7
AGENTS.md
ADR
API contract
Event contract
Test
Runbook
Architecture documentation

The PR is where we decide whether that knowledge changed.


What this changed for me

I originally looked at AGENTS.md as an AI-specific file. I don’t anymore.

The AI agent is just another consumer of repository knowledge. Developers need the same information. Reviewers need it. New team members need it. The difference is that an experienced developer can compensate for missing context. An AI agent has to discover it. That makes stale or missing repository context more visible than it used to be.

And that’s why I don’t think the solution is:

“Developers should write more documentation.”

The solution I implemented was simpler:

Make engineering context part of the GitHub change workflow.

When code changes, the PR asks whether the engineering knowledge changed too.

If it did, I update it. If it didn’t, I explain why.

The team that owns the code owns the context. And automation helps identify when the two start drifting apart. For me, that’s a much more sustainable model than asking developers to remember one more documentation task.