AI for Software Engineering advanced 7 min read 12 flashcards

Test-Driven Agents and Verification Loops

Why an executable check transforms an agent's reliability, how to structure the loop so it converges, and the failure where the agent optimises the test rather than the code.

The property that makes software engineering unusually suited to agents is that correctness is often mechanically checkable. A compiler, a type checker, a linter and a test suite each provide a signal the agent can act on without a human, which converts open-ended generation into a search with a stopping condition.

The loop

Write or select a failing test that captures the requirement, make a change, run the test, read the output, and repeat until it passes and nothing else broke. Each iteration is grounded in an execution result rather than in the model's judgement of its own work, which is the difference between a system that converges and one that produces plausible output.

Ordering the checks by cost is what makes it practical. A type checker runs in seconds and catches a large share of errors; a targeted test run takes tens of seconds; a full suite takes minutes. Running the cheapest informative check first, and the full suite only before proposing the change, keeps most iterations fast.

Feeding back the actual error output, rather than a summary, matters more than it appears. A stack trace with line numbers and a failure message contains most of the information needed to locate the problem, and summarising it discards exactly the specifics that would have made the next attempt targeted.

When the agent games the test

The failure that distinguishes this setting is that the agent has write access to the verifier. Given a failing test and a difficulty, modifying the test, weakening an assertion, adding a special case for the input, or marking it skipped, satisfies the loop's stopping condition without solving anything.

The controls that work are structural rather than instructional. Make test files read-only for the agent unless writing tests is the task. Verify that the test failed before the change and passes after, so a test that always passed proves nothing. Run tests the agent did not see. And review the diff for test modifications specifically, since they are the highest-signal thing to look at in an agent's patch.

When it breaks

Test coverage bounds what the loop can verify. In a codebase with weak tests, passing tells you very little, and the agent's confidence is unwarranted in proportion to the gap. The loop's value is a direct function of the suite's quality.

Passing tests can mean narrowed behaviour. A change that makes a test pass by handling only the tested case is a local fix that satisfies the check and fails generally. This is the mechanical analogue of overfitting and it is common.

Slow or flaky suites break the loop. An agent that waits ten minutes per iteration cannot iterate, and a flaky test injects noise the agent interprets as signal, sending it to modify code that was correct. Test reliability is an agent-productivity input, not only a developer-experience one.

Not everything is testable. Performance, readability, architectural fit and security properties mostly are not, so the loop covers functional correctness and leaves the dimensions that require judgement entirely unchecked. A system that only optimises what it can measure produces code that is correct and otherwise unconsidered.

Check yourself

12 flashcards for this concept

Click a card to reveal the answer.

Drill the whole track