Tests are the harness
How fast we write code has rarely been the bottleneck for most software, and now it matters less than ever. A coding agent writes code at a pace we can’t match in code review, so the hard part has become knowing whether what it produced is right: whether it does what we intended, and whether it’s well designed.
The teams I’ve seen get real value from agents aren’t the ones with the cleverest prompts. They’re the ones that already had a test suite they trusted. That suite is the harness: it limits what the agent can break, and it tells both of you, quickly and without argument, when something went wrong.
Self-testing code has a new user
Martin Fowler describes self-testing code as a suite you can run with one command and trust to catch bugs. The payoff was always the feedback loop: make a change, run the tests, know. At Thoughtworks we treated test-driven development and continuous integration as sensible defaults for exactly that reason: we wanted to always know, from several angles and with the highest possible confidence, whether the code worked as expected.
An agent lives entirely inside that loop. It doesn’t know why the code is shaped the way it is, or which corner broke production last spring, and it’s confident regardless. What it can do is read the tests as runnable specifications. They’re the team’s knowledge in a form that runs continuously and should always pass, so after every change they tell the agent whether it’s still heading in the right direction.
So the qualities that always made tests good matter more now, and the weaknesses we used to tolerate cost more.
What makes a good harness
Fast. An agent runs your suite dozens of times on a single task. If that takes twenty minutes, it either skips it or you pay in wall-clock time and tokens. Push most checks down to the narrowest level that can catch the bug, the old test pyramid argument, and keep a few broad tests to prove the wiring. Integration tests can also run faster than ever with techniques like sharding: each shard gets its own isolated dependencies, such as its own database, so the shards can run in parallel. That setup used to take real effort to build, and now an agent can write it for us.
Deterministic. A flaky test teaches the agent that red means nothing. So it retries, adds a sleep, or loosens the assertion until it goes green. People do the same; an agent just does it faster and without guilt. Fix flaky tests or quarantine them, but never leave them in the loop.
Pinned to behaviour, not internals. This one matters most. Changing how something works inside shouldn’t force its tests to change; they should change only when the outcome does. Then refactorings leave the suite alone, enhancements and new features touch far fewer tests, and the agent never learns that editing tests is part of the job.
Clear when they fail. A failure message is now a prompt. expected 3, got 4 sends the agent guessing. expected a returning customer's total to be 90.00 after discount, got 100.00 points it at the problem.
Who writes the tests?
The tests I review most carefully are the first ones on a project. An agent follows the conventions it finds, so those first tests set the pattern for the next hundred. If the convention is great, the chances are high the agent produces good tests on its own, and the software gets built faster and better at the same time.
AI writes great tests, with the right guidance. Yes, it’s obvious. Tests are the specification, so I delegate the typing, not the thinking. Of course, LLMs are very good thinking partners too: they’re great for inspiration, for getting unstuck, or for giving a half-formed idea some shape. The call on what the software should do still stays with me.
Left unguided, an agent that writes the code and its tests together tends to describe what the code does rather than what it should do. The tests pass, look thorough, and pin down the bugs as carefully as the features.
That’s why testing fundamentals matter more than ever. The FIRST principles (fast, independent, repeatable, self-validating, timely), testing behaviour rather than implementation, one clear reason for each test to fail: these used to be good habits. Now they’re the guidance the agent works from.
A diff that touches both a test and the code under it deserves a slower read, and mutation testing is a cheap way to find assertions that never fail.
All of this depends on recognising a good test when you see one, and that’s where I see a challenge. Most of us built that eye the slow way: writing poor tests, living with them, and learning what hurt. If agents write most of the tests from day one, I’m not sure how new programmers will develop it; time will tell. My best guess is that it has to be taught on purpose, through pairing and code review, because it no longer comes for free.
The same investment, paid twice
None of this is new. Fast, deterministic, behaviour-focused tests are what we were already arguing for, because they let people change code without fear. The difference is how quickly their absence hurts. It used to show up slowly, as refactorings nobody dared to start and nervous releases. With an agent it shows up in an afternoon.
A good testing strategy was always a harness for the team. Now it’s also the harness for the fastest and least cautious contributor you’ve ever had.
The strategy is still ours
So pay attention to how your tests are written, and to the strategy behind them: which tests run on every commit, which ones gate a deployment, and how quickly your CI and CD pipeline tells you something broke. The DORA metrics show whether that strategy is working: how often you deploy, how long a change takes to reach production, how often changes fail and how quickly you recover. Coming up with a good strategy still takes understanding your software’s context: what it does, who relies on it and what a failure costs. That part is still our job.