# Staying the engineer who understands the system

> An agent that writes most of the code moves the understanding away from you unless you keep it. Read agent code like a colleague's, explain the system from memory after each increment, and keep the architecture decisions in a folder the project instructions point at.

Page: https://schubergphilis.github.io/ai-training/coding-with-agents/keeping-understanding/
License: CC BY-SA 4.0, https://creativecommons.org/licenses/by-sa/4.0/

In this lesson we look at what happens to your own understanding of a
system when an agent writes most of the code. The earlier lessons in this
course gave the agent a plan to follow, a spec to work from, and small
increments to land. Each of those keeps the agent's output checkable. This
lesson is about the other side: the engineer who does the checking has to
understand the system, and that understanding is easy to lose when
someone else types every line. We name where it leaks away, and the habits
that keep it. The page has no code to run. The examples refer to the
to-do fixture from *Working from a spec in small increments*, and the
exercise uses that fixture after its increments are merged.

The claim behind the lesson is plain. You can only review a change to a
system you understand, and every agent change gets reviewed by you. An
agent that produces more code than you can hold in your head has moved
the review from you to nobody. Brilliant's skills map for coding with AI
lists keeping your understanding and the coherence of the system as a
skill of its own within incremental development, separate from getting
the increments to land (Developing incrementally, Brilliant, Coding with AI skills map).

## Where understanding leaks

Reading code and writing code build understanding in different ways, and
an agent removes the second one entirely. Suppose you had written the
`overdue` command yourself instead of briefing the agent for it. You would
have decided where today's date comes from, seen a first try fail the test
with the two `TODO_TODAY` dates, and fixed it. The decision would stay
with you because you made it. When the agent merges the same increment,
the code arrives finished. The tests pass. The diff is small and the
report is confident. Every reason to slow down and read it
is gone, and so is the moment where the decision would have become yours.

The leak is slow. After one increment you still know the program, because
you wrote the spec. After ten, you know the spec and the agent knows the
code. After a hundred, a colleague asks in a design review why the date is
stored as a string and you open the transcript to find out. That's the
point where the objective of this lesson has been missed: you can no
longer explain how the parts fit or why they were built that way, and the
agent's next change goes into a system nobody on the team can vouch for.

#### Checkpoint: What did the small diffs cost?

An engineer merged twenty increments with an agent over a month, each one
tested and merged after a glance at the diff. Asked in a design review why
the queue is in-process, the engineer has to look it up. What did the
small, green diffs cost?

- Nothing. Small increments with passing tests are the point, and the engineer can read any part when needed.
- Some speed, because increments take longer than one large change would have.
- The engineer's own model of the system, because passing tests replaced reading and the reasons stayed with the agent.
- The agent's context, which has to be rebuilt each session because the increments were small.

## Read it like a colleague wrote it

The first habit is to read agent-written code with the attention you give
a colleague's pull request. That's a lower bar than it sounds, because
you already read colleagues' code for a living. It is also a higher bar
than most engineers hold agent output to. A colleague's PR doesn't arrive
with its tests already green and a paragraph saying it is done.

Reading means the whole diff, at the speed it takes to follow it. You stop
at anything you can't explain, such as a regular expression you can't
parse or a helper whose name doesn't say what it does. At each stop, you
have two moves. Ask the agent to explain the line in plain language, and
ask it for a test that documents the behavior you couldn't see. The test
stays in the suite after the explanation is forgotten, and it turns a line
you took on faith into one the suite checks.

#### Prompt (illustrative, not a recorded transcript)

```text
In `todo.py`, the `due` command validates the date with
`datetime.date.fromisoformat(text)`. Explain in plain language which
inputs that accepts and which it refuses, and add a test that documents
one accepted and two refused inputs.
```

#### Response

```text
`fromisoformat` accepts a date written as four digits, a dash, two digits,
a dash and two digits, such as `2026-10-01`, and raises `ValueError` for
`tomorrow` and for `2026-10-1`, where the day has one digit. Which other
forms it accepts depends on the Python version. On 3.9 and 3.10 the
dashed form is the only one. From 3.11 it also accepts other ISO 8601
forms, such as `20261001` without dashes and the week form `2026-W40-4`.
The spec's `bad date` criterion names `tomorrow`, and the `due` command
catches the error and prints `bad date: <input>` for it on every version.
I added `test_due_date_formats` in `test_due.py` with `2026-10-01`
accepted and `tomorrow` and `2026-10-1` refused, which holds on 3.9 and
on 3.11. If you want `20261001` refused too, the check needs to be
stricter than `fromisoformat` alone, and that is a spec decision.
```

This exchange is illustrative and shows the form of the question. The
version difference is the kind of fact you only learn by asking. Had you
skimmed past the line, the program would accept `20261001` on a
colleague's newer Python and refuse it on your own older one, and nobody
would know why. The answer also surfaced a spec question, which forms
count as a bad date, and that question goes back to the spec before
anyone writes more code.

The habit costs minutes per increment, and small increments are what
make it possible. A diff you can read in one sitting is a diff you can
understand. The brief that asks for small increments is the same brief
that keeps your reading possible.

#### Checkpoint: A line you can't read

The agent's increment for date validation includes a regular expression
you can't parse. The suite is green. What do you do?

- Approve it. The tests pass, and the agent reads regular expressions better than you do.
- Ask the agent to explain the expression in plain language, and to add a test with inputs it accepts and refuses.
- Ask the agent to rewrite it without a regular expression, since you can't read those.
- Leave a comment above the line that says the agent wrote it.

## Explain it without looking

Reading builds understanding, and the check that it worked is whether you
can explain the system without looking at it. After an increment is merged,
close the editor and say how the parts fit and why they were built that
way. For the due-dates feature that's a few sentences: the date is a
field on the item that some items have and some don't, `render.py` shows
it when it is present, `store.py` didn't change because the JSON file
takes whatever keys an item has, and `overdue` reads today from
`TODO_TODAY` so a test can pick the day. Each of those sentences has a
"because" in it, and the "because" is the part you lose first.

Then open the code and check your explanation against it. Where the code
does something your explanation didn't predict, you have found a gap in
your model, and the increment isn't done until you close it. Either the
code is right and you ask the agent why, or the code is wrong and it
comes back to the spec. Either way, the review did its job.

This is also the point to notice when the answer to "why" is a guess.
"The date is a string because that's what JSON has" is an explanation.
"The date is a string, I think because the agent chose it" is a decision
nobody made. A decision nobody made is a decision the next session can
contradict, and that's the subject of the last section.

#### Checkpoint: Which of these show understanding?

An engineer explains the due-dates feature from memory. Which of these
sentences show the understanding this lesson asks for?

Select exactly 2.

- The store module didn't change, because the JSON writer saves whatever keys an item has.
- The agent said the store module didn't need to change, and the tests passed after the increment was merged.
- Today's date comes from an environment variable when it is set, so the overdue test can pick the day.
- The diff for the overdue command touched one file and one test file, as the agent's increment list predicted it would.

## Keep the reasons where the agent reads them

The third habit is about the team, and about the next session. An
architecture decision that lives in your head is safe until the next
person, or the next agent session, makes a different one. A fresh agent
session starts from the files in the repository and, for some tools, a
private memory the agent keeps for itself. Claude Code, for example, loads
`CLAUDE.md` and its auto memory at the start of every conversation
(How Claude remembers your project, Claude Code documentation). The chat transcript of the last session is in
neither. A decision the session doesn't find in what it loads is one it
doesn't know about, and one it will reverse when the decision gets in the
way of the task in front of it.

So the reasons go in the repository, in two places that point at each
other. A decisions folder, often `docs/decisions/`, holds one short file
per decision: what was decided, why, and what was considered and
rejected. The project instructions point the agent at it, the way
[Writing project instructions the agent reads every session](https://schubergphilis.github.io/ai-training/coding-with-agents/project-instructions/)
showed. The to-do fixture has an instructions file, and its lines say
which commands must keep working and that `SPEC.md` is the source of
truth while a change is in progress.

A decision file for the fixture is short.

```md
# Due dates are stored as ISO strings

Date: 2026-10-01

## Decision

An item's `due` field is a `YYYY-MM-DD` string in `todos.json`, parsed
when it is compared and never at load time.

## Why

The JSON file has no date type. Parsing at load would make an item with a
bad date impossible to load, and the spec says an item without a `due` field
stays valid. Parsing at comparison keeps `list` working on any file that
loads today.

## Considered

A Unix timestamp: harder to read in the file, and the spec shows the date
in `list` as written. A separate dates file: two files to keep in step.
```

And one line in the instructions:

```md
- Architecture decisions are in `docs/decisions/`. Read the ones that
  touch the files you change, and add one when you make a new decision.
```

The instructions line does two jobs. It tells the agent where the reasons
are, so the next session that touches dates reads why they're strings
before proposing timestamps. And it tells the agent to write a decision
down when it makes one, which is how the folder stays current when the
agent merges most of the increments. You still read every new decision
file as part of the diff, because a decision the agent wrote and you
didn't read is back in nobody's head.

#### Pitfall: The decision that only lived in the transcript

A team merges a feature over three weeks of agent sessions. In the first
week the engineer and the agent decide, after some back and forth, to keep
the job queue in-process, because the deployment has one machine and a
broker would be a second thing to run. The reasoning is in the transcript
and nowhere else. In week three a fresh session, asked to make the queue
survive a restart, adds a broker, its client library, and a configuration
file, all correctly. The reviewer approves it because it works. The
decision was reversed by an agent that never knew it had been made. A
file in `docs/decisions/` and a line in the instructions pointing at it
would have put the reason in front of that session before it typed a
line.

#### Checkpoint: Will the next session see it?

Buckets:

- The next agent session reads it
- The next agent session never sees it

Items:

- A file under docs/decisions/, with the folder named in the project instructions
- The transcript of the session where the decision was made
- A message in the team chat explaining the choice
- The project instructions file itself
- The engineer's memory of the discussion

## Exercise

Take a copy of the to-do fixture from *Working from a spec in small
increments* with its first two increments merged, as that lesson and its
exercise leave it. Close your editor and write down, from memory, how the
due date flows from the `due` command through the modules to `list` and
`overdue`, and why the field is optional. Then open the code and mark
every sentence right or wrong. For each wrong one, ask the agent to
explain the line you misread. Finish by adding `docs/decisions/` with one
file recording why dates are stored as strings and parsed late, and a
line in the fixture's `AGENTS.md` that points the agent at the folder.
Plan on ten minutes. This is the check you run after any increment you
didn't type yourself.

A good result: your explanation names the field, the three modules and
which of them changed, and says why an old item without a date still
loads. At most one sentence needed correcting after you read the code.
The decision file says what was decided, why, and what was rejected, and
the `AGENTS.md` line names the folder. Then answer one question: which
sentence of your explanation would you have got wrong a week from now,
and is the reason for it written down anywhere the agent reads?

Stretch: Do the same after the third increment, date validation. Then compare your two decision files: does the second one contradict anything in the first, and if so, which of the two changes?

## Recap

1. Understanding leaks when the agent writes and you only run the tests. Passing tests check the change and say nothing about whether you can still explain the system (Developing incrementally, Brilliant, Coding with AI skills map).
2. Read agent code with the attention you give a colleague's pull request. Stop at every line you can't explain, and ask for a plain-language explanation and a test that documents it.
3. After an increment is merged, explain from memory how the parts fit and why, then check the explanation against the code. A gap is a review finding.
4. Architecture decisions and their reasons go in a decisions folder in the repository, and the project instructions point the agent at it, so the next session reads the reason before it proposes to reverse the decision.

## References

- Developing incrementally, Brilliant, Coding with AI skills map
- How Claude remembers your project, Claude Code documentation, https://code.claude.com/docs/en/memory
