Skip to content
← Notes

Agentic Software Engineering · AI-Native Engineering · Software Architecture

Staying attached to code you didn't write

Plan, teachback, ADR - three deliberate moves that stop an agentic workflow from quietly detaching you from your own architecture.

About six months into working with coding agents full time, I noticed something I didn’t like.

The features shipped. Tickets closed at a rate I couldn’t have matched by hand. But when a colleague asked me why a module was structured the way it was, I couldn’t answer without opening the file. On code I had approved. Sometimes on code I had approved that week.

That is a different failure from “the AI wrote bad code.” The code was fine. What was missing was me.

Why it happens

When you write code yourself, you learn it through recall. You hold the problem in your head, construct a solution, hit the constraints, back out, try again. The dead ends are the part that sticks. By the time it works you know the shape of the thing and every place you almost went wrong.

When an agent writes it, you learn it through recognition. A diff appears, you read it, it looks right, you approve. Recognition feels like understanding in the moment and encodes almost nothing.

Two ways to learn a codebase Two horizontal tracks compared. The recognition track is a thin straight line through ticket, agent writes it, you read the diff, approve, ending in a retention meter with one of six cells filled. The recall track is a thick line through ticket, you weigh three options, you hit constraints, you decide, with backtrack arcs marked dead end and rethink, ending in a meter with five of six cells filled. Two ways to learn a codebase Same feature ships either way. The difference is how much of it stays in your head. what you retain Recognition reading a diff ticket agent writes it you read the diff approve One straight pass. No dead ends, nothing held in your head. shallow Recall constructing a solution ticket you weigh 3 options you hit constraints you decide dead end rethink deep The dead ends are the part that sticks. An agent removes them, and takes the learning with them.
Scroll →

None of this is my observation. Learning research splits it into fluency strength - can you retrieve it right now, and storage strength - will it still be there in six weeks. Reading a good diff produces fluency in abundance, and fluency is the one that feels like mastery. The name for deliberately trading comfort for retention is desirable difficulty, and it long predates any of this tooling.

You can recognise correct code without being able to reproduce the reasoning behind it, and reasoning is exactly what you need six weeks later when it breaks in production, or when someone asks you to defend the design in review.

So the problem isn’t speed and it isn’t code quality. It’s that the effort which used to produce architectural knowledge as a side effect has been removed, and nothing replaced it.

What I didn’t do

I didn’t go back to writing everything by hand. The productivity is real and I’m not giving it up out of nostalgia.

The fix is narrower: put deliberate cognitive effort back in, but only at the moments that are architecturally significant. Most code isn’t. Commodity CRUD, a form, another list endpoint - the agent handles those and I don’t think about them again. That’s the point of the tool.

For everything else, three moves. Two of them lean on skills I did not write - Matt Pocock’s wayfinder, grill-me and grill-with-docs, from his skills repo - and one I had to put together myself, because I could not find anything that did it.

/plan - decide before you build

The command forces the agent to stop before writing code. It has to produce:

  1. A short restatement of the problem and the constraints in the existing architecture.
  2. At least three approaches, each with what you gain and what you pay.
  3. A recommendation with an explicit “why this one”.
  4. Implementation broken into increments of roughly 150 lines or less.

Then it stops and waits.

The last line of the prompt is the whole trick: do not write code until I tell you which option I’m choosing.

That choice is where the memory forms. Reading one plan teaches you nothing, because there’s nothing to weigh. Reading three and rejecting two means you had to hold the trade-offs in your head and commit. Two months later I don’t remember the code, but I remember that I picked the option costing more memory to avoid a network round trip. That’s the thing I actually need to recall.

For work too big to hold in one session, wayfinder is the layer above this: it charts the whole effort as a map of decision tickets and resolves one per session, under a rule identical to the last line of my prompt - plan, don’t do. grill-me and grill-with-docs are the other half, interrogating a design before any code exists. That timing is worth being precise about, because the next move is the exact opposite.

/teachback - find the holes before you merge

This is the one I had to build. Grilling skills exist and they are good, but they all run before the code does. I wanted the mirror image: the feature is written, it is about to merge, and the only open question is how much of it stayed in my head. teach names the mechanism I was reaching for - retrieval practice, recall from memory rather than review - and warns about the failure mode below by name.

After a feature is implemented, this one turns the agent around. It doesn’t explain the code to me. It quizzes me on it, four to six questions, one at a time, waiting for my answer before moving on:

  • the main data flow, from input to database and back
  • why we made the key decisions, not just what they were
  • where the module boundaries are and what’s coupled to what
  • the most likely edge case, or where this will break first

After each answer it tells me whether I was right and corrects me tightly if I wasn’t.

One honest caveat, because this is the part people get wrong.

The same command, run two ways Two panels side by side. Left, titled you let it explain: the agent says the store subscribes to the socket, the user says yes makes sense, outcome recognition again, retention meter with one cell filled. Right, titled you answer first: the agent asks where this breaks first under load, the user guesses the socket reconnect, the agent corrects that it is the retry queue, outcome you tried to recall it and could not, with one cell marked as a located gap. The same command, run two ways Only one of them does anything. The comfortable one is the useless one. You let it explain agent “The store subscribes to the socket, then…” you “Yes, makes sense.” Recognition again. You have just re-read the diff with extra steps. You answer first agent “Where does this break first under load?” you “…the socket reconnect, I think?” agent “Close. It is the retry queue.” You tried to recall it and could not. Now the gap has an address. That discomfort is the whole point. the one you now know about
Scroll →

It only works if you answer out loud first and then check. If you let the agent recite the answers while you nod along, you are back to recognition and you’ve gained nothing but a pleasant feeling. The entire value is in the moment where you reach for something, find nothing, and now know exactly where your gap is.

It’s uncomfortable. That’s the signal.

/adr - leave a sentence for the next person

The last one generates an Architecture Decision Record stub from the session’s changes. Status, context, decision, alternatives considered, consequences.

Whether a decision earns one at all runs through the same gate as the rest of the loop: it changed the architecture, it is expensive to reverse, or a future reader will look at the code and wonder why on earth we did it this way. That is the diamond in the diagram further down, and it governs the whole loop, not just the ADR.

Rules:

  • One screen, maximum. Nobody reads more.
  • The alternatives section must always list at least two rejected options. If we never discussed any, the agent has to state the logical alternatives and why they’d lose. Undocumented rejections come back as “why didn’t you just…” a year later.
  • The decision has to be phrased as a sentence I can say to a colleague who asks how this works.

That last rule does more for me than the file itself. If a decision can’t be compressed into one speakable sentence, I usually don’t understand it well enough yet.

All three come straight out of my own ADR template, which makes a Rejected section mandatory and asks for the decision in one sentence. They stay mandatory for a reason specific to agent work: when I didn’t write the code, the rejected options are the only part of the session that survives nowhere else. The diff records what we built. Nothing records what we ruled out and that is the question I actually get asked.

The loop

One ticket through the loop Flow diagram. A ticket reaches a decision diamond labelled worth keeping. The no branch goes to an upper lane where the agent implements it, you skim, you merge. The yes branch goes to a lower lane of five steps: slash plan, you choose, increments, slash teachback, slash adr, then merge. An arc runs back from teachback to increments labelled a gap means you go back into the code. One ticket through the loop Most code never enters the lower lane. That is what keeps it from becoming ceremony. ticket worth keeping? worth keeping = architecturally significant, risky, or the thing people keep asking you about no yes Commodity code Agent implements it. You skim, you merge. CRUD, forms, another list endpoint. You will not think about it again. The parts you will have to defend /plan 3+ options, with trade-offs then it stops you choose rejecting two is what you still remember increments 150 lines or less per step /teachback it quizzes you you answer before you look /adr one screen 2+ rejected alternatives a gap means you go back into the code merge
Scroll →

Per ticket: /plan, choose an option, let the agent implement in increments, /teachback before merging, /adr if the decision was architecturally significant.

And, just as important: skip all three for commodity code. If you run this on every commit it becomes ritual, you’ll resent it, and you’ll drop it within two weeks. Reserve it for the risky parts and the parts people ask you about.

What it actually buys

I built AttestBIM, a browser-based IFC compliance checker, across roughly ten agent sessions. Monorepo, a core package and a web app, fifteen compliance checks, ninety-odd tests. I did not type most of it.

I can still tell you how the check pipeline is wired, which decisions I’d revisit, and where it will strain first on larger models. That’s the outcome I’m optimising for. Not fewer AI-written lines - fewer lines I can’t defend.

Because the part that hasn’t changed at all is who gets called when it breaks. The agent isn’t on that rotation. You are, and the review, and the incident, and the conversation with the client are all still yours. Speed is worth very little if you can’t stand behind the result.

This workflow is my answer to that. It costs maybe twenty minutes per significant feature. It’s the cheapest thing I do.