Engineering Principles · Software / Agentic
Test-first engineering in the age of AI agents.
When generating implementation is cheap, defining correct behavior and verifying it independently become more valuable—not less.
The principle
Specify the behavior before the implementation.
Test-driven development (TDD) follows a short cycle: RED, GREEN, REFACTOR. Write a test for missing behavior; write only enough production code to pass; then improve the design while keeping the tests green.
Robert C. Martin's three laws sharpen the discipline: do not write production code before a failing test, do not write more test than needed to fail, and do not write more production code than needed to pass. These are practical constraints on the development loop, not proof that every project should be built with strict TDD.
The workflow
Tests first; agents second.
1. Human owner: Write the acceptance criteria, invariants, constraints, and failure cases. Check that the specification expresses the actual requirement.
2. Test author: Build tests and confirm they fail for the right reason against the pre-implementation state. A missing module is not, by itself, evidence that the test meaningfully captures behavior.
3. Implementer: Generate the smallest implementation that satisfies approved tests. Do not silently weaken those tests.
4. Reviewer: Run independent tests and examine the diff, security boundaries, architecture, failure modes, and unexpected side effects.
5. Human owner: Authorize the next environment or release only after reviewing the evidence and operational risk.
The limitation
Passing tests can still mean building the wrong system.
An agent can meet incomplete or incorrect acceptance criteria. Tests can reproduce an author's mistaken assumptions. Different agents can share the same blind spots. And a successful test pipeline can itself be broken or misconfigured.
Use boundary cases, property-based checks where useful, integration tests against representative dependencies, and independent verification of the test harness. Keep meaningful acceptance tests under change control.
DBRE example
An Aurora parameter-change agent needs stronger guarantees than an API success.
Suppose an agent proposes changes to Aurora PostgreSQL parameter groups. Merely checking that an AWS API call returned success says little about whether the correct environment, account, cluster, or effective setting was affected.
Before implementation, make the non-negotiable behaviors explicit: read-only investigation must not mutate resources; production changes require separately authorized approval; the target account, region, and cluster must be constrained; pending-reboot state must be visible; retries must not repeat unwanted side effects; and ambiguous observations must stop autonomous execution.
Test policy decisions locally. Test AWS integration in an isolated environment. Verify resulting database state rather than assuming a configuration request was applied effectively. Reboot authorization remains a distinct operation.
The design review
Ask whether the implementation should exist in this form.
Tests can support refactoring; they do not guarantee good abstractions. Keep data collection, analysis, policy decisions, approved execution, and audit evidence separate. Favor small, reviewable changes over a large generated framework whose complexity obscures authority boundaries.
Reading
Sources and further study.
Martin Fowler — Test-Driven Development ↗
Anthropic — Claude Code Best Practices ↗
Anthropic — Demystifying Evals for AI Agents ↗
OpenAI — Harness Engineering ↗
Google — Site Reliability Engineering Books ↗
Foundational books: Kent Beck, Test-Driven Development: By Example; Robert C. Martin, Clean Code; Martin Fowler, Refactoring.
Engineering takeaway
Define what success means. Make it executable. Then verify reality.
Explain the system, demonstrate its behavior, evaluate its limitations, and design a better solution when the evidence warrants it.