A quarter of work, counted in the repository rather than in a feeling.
Around twelve hundred commits between the start of July and the end of September. Three hundred and seventy six of them are features, two hundred and ninety four are fixes, and the rest splits between deployment chores, refactors, tests and documentation. On the largest day the repository took forty nine commits.
That is not a normal shape for a team our size. Some of it is a port of an existing application into a new runtime, which front loads a lot of mechanical work. But a good portion of it is agents doing work that a person would have done, and the useful question is which parts of that worked.
This post is deliberately not a list of what agents can do. It is an account of where they genuinely earned their place, where they consistently went wrong, and what the review habits had to become.
Agents are very good at the mechanical layer
The port of the write API from one runtime to another was mostly mechanical, and this is where the loop paid for itself.
Generated entities and repositories, queue consumers, scheduler wiring, container packaging, a Helm chart, metrics and tracing setup. Each of these is a small, verifiable piece of work with an obvious shape and an existing example in the repository. An agent that can read the existing pattern and follow it produces correct code at a rate I cannot match by hand.
The tests came out of the same place. A hundred and sixty test files in the new service, a coverage floor configuration on the TypeScript side, and over a hundred end to end specs. Writing tests against existing behavior is the single best use of an agent I found, because the specification already exists in the code and the work is repetitive without being trivial.
The coverage config is worth a note, because the comment in it is the part I would keep. The thresholds sit one to two points under the measured numbers, and the comment says why: they are floors, not goals, set so coverage cannot silently regress without pretending every uncovered line is worth a test. Then the line that matters, “raise them as coverage grows; never lower one to make a branch green”. That is a rule an agent can read and a reviewer can enforce.
The important qualifier: it worked because there was an example to follow. Where we had a clear existing pattern, output was good. Where we were inventing a new shape, output needed rewriting, and the rewrite cost more than writing it myself would have.
Agents are bad at knowing what they are allowed to touch
The most consistent class of bad output was scope.
A fix for one bug that also fixed an adjacent bug nobody asked about. A refactor that touched files outside the change. A security check loosened to make a test pass. None of these were malicious or even unreasonable in isolation. Each was a locally sensible move that widened the change.
The rule that fixed this is one line, and it is counterintuitive to write down: implement only what the requirement asks, and do not fix unrelated infrastructure as a side effect. Both a person and an agent will argue that the adjacent fix is obviously correct. It often is. It still does not belong in this change.
This is also why “the tests pass” is not a verification. An agent that loosened an assertion can make the tests pass and be completely wrong. Reviewing what changed is the verification, and there is no substitute.
Bugs found the new runtime that were dependencies on old behavior
The port surfaced a category of bug that no amount of reading the diff would catch, and I have already written about the details in the port post. The short version: behavior that worked because of when a framework committed a transaction, error response shapes that consumers depended on without anyone remembering, and encrypted values in existing rows that had to keep being readable.
Every one of these would have shipped if the ports had been verified only by unit tests. What caught them was running the existing end to end suite against the new service and treating a failure as a contract difference rather than a broken test.
I have a version of this I now say to myself about agent written changes generally. The tests tell you whether the code does what the tests say. They do not tell you the tests were right. For a change that crosses a boundary, only an exercise of the real path does that.
The housekeeping is real work and it accumulates
Three areas produced a steady trickle of commits that no feature story would have planned for.
Documentation kept drifting. Describing a service that had been replaced, a set of environments that had been removed, a model that had been deleted. Documentation that is confidently wrong is worse than documentation that is missing, because it is the thing an agent reads before implementing.
Test data kept needing repair. Several test failures came from seed data that no longer matched the schema, and the fix in one case was to stop a test deleting from tables the branch drops.
And configuration kept leaking between environments. Secrets reaching places they should not, credentials appearing in URLs, image tags being shared between pipelines that should not share them.
None of this is glamorous. All of it is the reason a repository is usable six months later, and it does not happen unless someone treats it as work with the same status as a feature.
The habits that made the difference
Write the rule down the first time it is violated. Every instruction in our agent guide came from an incident. That is slower than writing a comprehensive guide upfront and produces a document that is actually true about this codebase. The instructions file, the skills it points at, and the generated knowledge bundle are covered in their own post.
Review the change, not the summary. An agent’s description of what it did is a claim, not evidence. The diff is the evidence. We also put an agent on the other side of that review, which is its own post: a reviewer that reads the repository.
Keep the important things outside the agent’s reach. One tool merges branches. The database is read only unless someone says otherwise. Those two constraints prevent the failures that cannot be undone, and they prevent them by structure rather than by instruction.
Do not accept a check as proof. Existing tests passing, types compiling, a linter clean. These are floors. The path you changed still has to be exercised.
Prefer the boring solution that the repository already contains. The reuse question is the one an agent gets wrong most often and the one a reviewer should ask first.
What I actually think
The honest position is that the agent loop changed the shape of my work more than it changed the volume.
I write less boilerplate and spend more time on the parts that decide whether a change is correct: the boundary, the contract, the failure mode, and the question of whether this belongs in this change at all. The commits per day went up. So did the number of pull requests where the interesting work was in the review rather than in the writing.
The failure modes are consistent and they are human failure modes. Widening a change because the adjacent fix is obvious. Loosening an assertion because the test was in the way. Trusting a green check because green is reassuring. An agent does all three at scale and without the hesitation that a person develops after being burned.
That is the whole reason the guardrails matter more than the prompts. The prompts decide how good the first draft is. The guardrails decide whether a bad draft can reach the release branch.
Fourteen years in, the loop is the most significant change to how I work that I can remember. It has not changed what makes a change correct.