Home Lessons LearnedThe Human Context Limit

The Human Context Limit

by Marc

My manager asked me last week how I had fixed an issue. Not what the fix changed, and not whether it was correct. How it worked, in the code. I could answer the first two questions without hesitating. I could not answer the third, so I told him that.

Then I told him what I actually think, which is that we should not be required to know every implementation detail of everything we ship. We should be required to have enough tests in place to catch it when something breaks.

This is the fourth post in a series I did not plan on writing. The first three were about what working with an AI agent gave me. This one is about what it took, and what I think replaces it.

The argument in short:

  • I now build faster, deeper and across more layers than I can hold in my head. That is arithmetic, not a discipline problem.
  • Knowing the implementation was never the real guarantee. It was a proxy that worked when one person typed everything by hand.
  • Tests are the guarantee that survives context switching, because they do not depend on my attention.
  • What makes delegation safe is not writing tests. It is a loop: every failure, and every failure I can imagine, becomes a permanent test.
  • Source code is on its way to being a layer we do not read, the way machine code already is. The tests become the contract.
  • The old expectation exists for a good reason, so the answer is symmetry: point questions at the documentation and let an agent answer them, instead of at one person’s memory.

Fast is the least interesting part

Speed is the part everyone writes about. Two other things did the actual damage.

Depth. The agent does not stop at the layer I am comfortable in, so neither do I. In a few months I have been to the bottom of a seccomp filter to find the exact syscall killing a binary, into a proprietary radio protocol with a microcontroller, into training a small CNN and reading its confusion matrix.

A year ago each of those was a project with a learning curve measured in months. Now each one is a Tuesday.

Width. This is the one I underestimated. 37 git repositories in my home directory. Working code in nine file types. Nine service-account keys across separate cloud projects. Deployment targets that include a cloud warehouse, container jobs, a rented server, three WordPress sites, several single-board computers, a couple of microcontrollers and an Android phone.

None of those layers share an idiom, and switching between them is not free. The cost is not in the tooling. It is in me.

Three counts and the deployment targets the work has to land on

Three counts and the deployment targets the work has to land on

The evidence I did not mean to collect

I keep two records of this work, and I built neither of them to be evidence. One is a log the agent appends to whenever it learns something non-obvious. The other is a directory of memory files, one fact per file, loaded at the start of every session.

Different processes, different files. That makes them independent, so when they agree it means something.

Two records of the same seven months: session discoveries logged and new memory files written, both peaking in July 2026

Two records of the same seven months: session discoveries logged and new memory files written, both peaking in July 2026

Month (2026) Discoveries logged Memory files with that timestamp
February 122 0
March 108 20
April 76 11
May 78 42
June 272 35
July 496 122
August 226 64

July: 496 logged discoveries in 31 days, sixteen a day, every day, alongside 122 memory files. The log now holds 1,403 entries and 1.8 MB of text.

That is not the record of a busy month. It is the record of a month I could not possibly have retained, written by the only participant taking notes.

My own fix hit the same ceiling

The memory directory was my answer to exactly this problem. Small typed files, one concept each, with a one-line index in a file called MEMORY.md.

It now holds 295 files. The index pointing at them is 260 lines and 45 KB.

While I was drafting this post, the session helping me write it reported:

WARNING: MEMORY.md is 260 lines and 44.3KB. Only part of it was loaded.

My externalised memory has exceeded its own context limit. The index I built so I would not have to remember 295 things is now too big for the thing that reads it on my behalf.

I find that funnier than I probably should. It is also the clearest evidence I have that this is a structural limit rather than a personal failure of discipline.

Two windows, and why the number is not the point

My current mitigation is a hard cap of two agent terminals at once. It does not hold. When something needs an answer quickly and the context lives elsewhere, I open a third rather than destroy the two I have loaded. Sometimes I am at six or seven, with topics that stay open for days because nothing gets closed, it only gets paused.

What I got wrong is thinking the constraint was the number of windows. It is the distance between them. Two windows on one subsystem is nearly free, because that is one mental model with two cursors in it. Two windows where one is a warehouse model and the other is microcontroller firmware costs more than the sum of both, because every switch is a full reload and the reload is the expensive part.

Even at two, if the two are far apart, I am the bottleneck.

Two windows on one subsystem versus two windows far apart

Two windows on one subsystem versus two windows far apart

Knowing the details was never the guarantee

It was a proxy for one, and a good one, back when a person wrote two hundred lines a week by hand. If you typed it, you remembered it, and your memory was cheap evidence that the thing was understood.

At the volume I work at now, that proxy has stopped tracking what it stood for. My recall of an implementation says almost nothing about whether it is correct. It only says whether I happened to be the one holding it recently.

Tests do not degrade when I context switch. They do not get vaguer after four days on something else. They are the part of my confidence that survives the surface area, and they are what keeps agent output on the rails, because they are the only mechanism in the loop that does not depend on my attention being in the right window at the right moment.

It is also the only version of this I can sustain. In the caveat post I described the tiredness of reading, judging and deciding all day. Tests are what let me stop reading every line without that becoming negligence. That is not a productivity argument. It is the reason I still have my sanity.

Which means correcting myself. In April I wrote that every line of agent output needs to be looked at, because trusting an agent blindly is how you ship subtle bugs.

That is no longer how I work. I run the agent in auto-mode and I do not read every line. The only reason that is not the negligence I was warning about is that what I stopped doing by hand, I replaced with something that does not get tired.

The loop matters more than the rule

“Write more tests” is weak advice and I ignored it for most of my career. What changed things is narrower, and it is a loop rather than a rule.

Delegate the work. When something fails, or when I notice a class of thing that could fail, that goes into the suite before the fix counts as done. Not the broken line. The class. Then it never comes back and I never have to hold it in my head again.

The cost gets paid at the moment of highest information, right when I understand the bug and never will again as well. The benefit is permanent, and it transfers: a rule learned from one system rails work I have not delegated yet.

That is what makes this a positive feedback loop instead of a chore. Without it, every new delegated system adds risk that only I can watch, and watching does not scale. With it, each new system arrives pre-railed by the mistakes of the ones before it, so delegating more makes the whole thing safer rather than more fragile.

The loop: delegate, something fails, the class becomes a permanent test, it can never return

The loop: delegate, something fails, the class becomes a permanent test, it can never return

What that looks like once

A real example, picked because the fix is one line and the tests around it are not.

A source application wrote one event timestamp in true UTC on some rows and in local wall-clock time on others, while two neighbouring fields on the same row always used the local convention. Downstream, a step shifted the value into a common timezone, so the rows that were already UTC got shifted twice. 559 records were quietly wrong.

The fix is to decide per row which convention was used. That part is small. What shipped alongside it is the point:

A test for the failure’s signature, not the failure. A timezone fault of this kind has one shape: the stored value sits exactly one timezone offset from an independent witness. Any other distance is a different bug. A reprocessed record drifts by minutes or days, a migration re-insert by months, commit latency by seconds. So the test looks for that shape across every row with a witness and expects zero. 559 before, 0 after.

A test for the assumption the fix rests on. The per-row decision uses a threshold, and it is only safe because the two populations sit either side of a wide empty band. If that band fills in, the threshold starts misclassifying rows and nothing else notices. So a second test exists purely to assert the band is still empty.

Two more for the sync’s own invariants, one active row per key and a deterministic current version, because the whole approach quietly assumes both.

Four tests: one for the bug, three for the ways the fix could stop being true. That last category is what I would not have bothered with a year ago, and it is why I can leave that pipeline alone now. I do not remember the threshold value. I do not need to. If the premise it rests on changes, the suite tells me before a stakeholder does.

Code becomes a layer we do not read

Follow that far enough and you end up somewhere I did not expect to be comfortable.

If the suite encodes what the business needs, the implementation behind it is a black box, and that is fine. We know the output we want. We know what the business is asking for. The code is whatever currently satisfies that, and it should adapt to the specification rather than the specification bending around whatever the code happens to do.

That inverts the arrangement I worked under for a decade. Code was the asset and tests were the safety net you added if there was time. Now the tests are the asset, because they carry the intent, survive a rewrite, and still mean something in six months. The code is the disposable half.

We have done this before, one layer down. Nobody reads the machine code their compiler emits. That was normal once, then compilers got good enough that the output stopped being a document humans audit, and the profession moved up a level. Nobody calls that negligence. Source code is on the same path.

The attention line moving up from source code to tests

The attention line moving up from source code to tests

The disanalogy is not in my favour, though. A compiler earns that trust by being deterministic and enormously verified. An agent is neither. So the trust has to be manufactured somewhere else, and the tests are where. That is why the suite is not optional: it is doing the job a compiler’s correctness does in the older stack, with much weaker guarantees.

The objection, and my answer

My manager’s case is not obstruction and it is not nostalgia. It has a real origin, and I should make it properly.

Developers were expected to know what they did so the layer above could report what was done and why. That is a chain, and it worked. Detail sat with the person who typed it, the manager aggregated upward, and the organisation could give an account of itself. Remove the bottom link and the chain reports nothing.

It goes past reporting. Detail knowledge is how people review each other, how someone notices two teams solved one problem two ways, how a new joiner gets up to speed, and how someone smells a system about to break before any test fails. Tests are lagging indicators. A person carrying the model is a leading one. At three in the morning, tests tell you something is wrong, not why.

There is also a version aimed at me specifically: my position is extremely convenient for me. It licenses exactly how I already work. That deserves suspicion.

Here is my answer.

The chain is not wrong. It is unaffordable at this pace. It assumed one person’s week of work fits in one person’s head, and that assumption is gone. If I hold to the old standard, the honest consequence is not that I explain more. It is that I build a fraction as much.

What the objection correctly identifies is not a need for retention. It is a need for availability. An account of what was done and why has to exist, has to be reachable fast, and must not depend on one person’s recent memory.

That is the half of the deal I pay for. Document everything properly, in the tracker and in the repository, as a condition of the work being finished rather than as a favour to my future self. If the detail is not in my head, it has to live somewhere better than my head.

Then the part I think most people miss. The account should be retrieved the same way it was produced.

If the producing side is AI-augmented and the reporting side is not, the asymmetry lands on the one human in the middle, who is asked to be a search index for work that no longer fits in a person. The fix is symmetry: point the questions at the artifacts, in a natural way, and let an agent answer from the tests, the commits, the tickets and the architecture docs.

On my best day that is a better source than my recollection. It does not fade, and it does not summarise selectively in my favour.

Fight AI with AI. Used on all sides, the work stays symmetric and the chain still functions. Used only by the builders, it breaks at the last human link, and the response will be to slow the builders down.

One piece this genuinely does not solve: tests carry behaviour, never intent. Nothing in a suite explains why anyone wanted the behaviour. A person has to write that down at the moment the decision is made, and that is the part I now treat as non-negotiable.

Where I could be wrong

Three places, and I would rather name them than have someone else do it for me.

Tests only rail what you thought to test. They are excellent at “this still works” and poor at “we built the wrong thing.” Coverage is not comprehension, and an agent writing both the code and the tests can be self-consistently wrong. I read the tests far more carefully than the code now, which is a strange sentence to write, but that is where the leverage moved.

If the suite is the contract, a hole in it is a hole in the specification. The timezone example works because someone thought to test the premise the fix rested on. Nothing forced that. There is no test that tells me which test I failed to write, and the more weight I put on the suite, the more that missing piece costs.

Design questions stay mine. “Why is it built this way” is not an implementation detail. Interfaces, constraints, what we deliberately did not do and why, where it breaks when it grows: I answer those in the room without notes, or I am not doing my job. I own the design. Implementation detail I will reconstruct on demand.

So the honest answer to my manager is not “I don’t know.” It is “give me ten minutes.” And the ten minutes have to actually work, which is a requirement on my tooling, not on my memory. That is what the artifacts are for: a top-level index of every project, an architecture document per system, memory files that carry the reason and not just the rule. I have stopped optimising for retention and started optimising for reconstruction time.

The skill I did not have

None of this is really a tooling problem, and that took me a while to see.

What I am doing now is project management on complex, multidisciplinary work, handed to a team that never sleeps, never pushes back unless prompted, and never mentions that this contradicts what we did last week. Decomposition, scoping, sequencing, acceptance criteria, knowing which thread is blocked and which is merely quiet. I never had that skill. I was an engineer, then an analyst, and the management layer existed so I would not have to be one.

I am developing it now, badly at first, out of necessity. It is the least technical thing I have learned this year and the highest leverage.

It also forces an update to Builder is the New Role, where I said creativity was the new bottleneck. That is still right about starting things. It is wrong about finishing them at volume, where the binding constraint is comprehension.

And you do not buy comprehension back by remembering more. You buy it by making the work verify itself, and by writing down enough that the model can be rebuilt faster than it was lost.

The first post in this series was about output having a tail at infinity. What I missed is that a person does not. My capacity is flat, and it was flat before December.

So the trade is not time for output. It is detail for reach, and that is only a good deal if you replace the detail with something that does not need you to be awake.

You may also like

Leave a Comment