Originally published at tddbuddy.com.
Related reading: The Bar for TDD Just Moved sets the floor this post extends; Just-in-Time Tests Are What You Get When You Gave Up argues for persistent suites; Where the Review Point Moved is the human-review companion to this test-architecture argument.
The test pyramid was a price chart, not a quality model.
That is the part everyone forgot. The pyramid arrived as a memorable shape with strong claims about where tests should live (many at the bottom, few at the top), and the shape got passed down as a quality principle. It was never a quality principle. It was a cost-structure argument made under the constraints of a specific era: unit tests were cheap to write and fast to run, so the math worked out in their favor. Integration tests were expensive to write and slow to run, so the math punished them. End-to-end tests were both, so the math killed them. The shape was the math, rendered in a triangle.
Agents broke the math.
When an agent writes a thousand unit tests as cheaply as it writes ten, “many at the bottom” stops being a meaningful constraint. The base of the pyramid is now free. What is scarce now is not test-writing labor. It is truth. And the question of where truth lives in the test suite has different math than the question of where labor was cheapest. Different math, different shape, different rules for what counts as a healthy suite.
The Pyramid Was Priced, Not Principled
Read the original argument honestly and the pricing is on the surface.
Unit tests were positioned at the base because they were fast (milliseconds), easy to write (one class, one method, one assertion), and cheap to maintain (low coupling to other code, isolated by construction). For the labor budget of a typical engineering team, this meant you could afford a lot of them. Coverage targets in the 80% range were achievable. Catch-rate on simple regressions was high.
Integration tests sat in the middle because they were slower (seconds), harder to write (multiple components, real or near-real dependencies, more setup), and harder to maintain (changes anywhere in the dependency graph could break them). For the same labor budget, you could afford fewer. Coverage targets here were “the critical paths.” Catch-rate on integration regressions was decent.
End-to-end tests sat at the top because they were slow (minutes), painful to write (full stack, real network, real database, real frontend), and expensive to maintain (flakiness, environment drift, browser quirks). You could afford very few. Coverage was “the most important user journeys” if you were honest, “comprehensive” if you were lying to management.
The shape encoded those costs. The shape made sense given those costs. Nothing in the pyramid said unit tests were better at finding bugs. Nothing said integration tests caught fewer kinds of problems. The argument was about the cost-per-test at each level, and the optimal allocation under that cost structure was many-cheap-fast at the bottom and few-expensive-slow at the top.
The argument was correct. It was also contingent. Contingent on the costs being what they were. Contingent on labor being the binding constraint. Contingent on the team being humans who got tired of writing setup code at 4 p.m. on Friday.
Take any of those out and the conclusion changes.
Agents Detonated the Authoring Cost
The first contingency to fail was labor cost. Authoring is no longer scarce.
An agent writing tests does not get tired. Does not skip the setup. Does not write Assert.NotNull(result) because the deadline is Friday and the real assertion is hard. An agent writes a thousand parameterized unit tests as cheaply as it writes ten. The base of the pyramid, the part that was expensive to fill out by hand, is now free.
When the cost of authoring goes to zero, the shape that was downstream of that cost stops carrying information. A team with ten thousand unit tests used to mean “this team invested in testing.” A team with ten thousand unit tests now means “this team let the agent generate tests.” The number, by itself, no longer says anything about quality, about coverage of what matters, or about whether the suite would catch the next regression.
The same applies in the other direction. Integration tests used to be expensive because writing them required setting up a database, configuring a queue, mocking a peer service, and dealing with flakiness when any of that went wrong. The agent does all of that as cheaply as it writes a unit test. The cost gradient that justified the pyramid’s shape has flattened.
This is not a small adjustment to the original argument. This is the original argument’s premise dissolving. If labor at every level is approximately free, then “write many cheap tests” stops being a strategy. It becomes the default for everything. The new question is no longer “which level can we afford to write more tests at.” It is “which tests, at which level, actually pin behavior we care about.”
The pyramid was an answer to the first question. Nobody has a generally accepted answer to the second question yet. The teams that are figuring it out are figuring it out by paying attention to one specific failure mode: same-author tests.
The Same-Author Blind Spot
When the agent writes the code and the test in the same session, both artifacts carry the same model of the problem.
This is the failure mode that the pyramid did not anticipate, because the pyramid assumed humans, and humans tended to be different humans writing the code and the test. A senior developer wrote the production code. A different developer wrote the unit tests, or wrote them later, or wrote them in pairing with someone whose mental model was different. The independence between author of code and author of test was load-bearing. It was the thing that made unit tests catch bugs in the first place. If the test author had perfect knowledge of the code author’s intent, a wrong assertion in the code would produce a matching wrong assertion in the test, and the test would still pass.
That perfect-knowledge case is the agent-authored case.
An agent writing ParseQuantity and writing the unit test for ParseQuantity in the same session has one model of what ParseQuantity should do. If the model is wrong (say, it does not handle negative inputs), the code will be wrong in a specific way and the test will be wrong in the same specific way. The test will assert that ParseQuantity(-5) returns 5, the code will return 5, and the test will pass. The bug ships. The unit test was at the bottom of the pyramid, where the catch rate was supposed to be high, and it caught nothing because the test could not see what the code could not see.
The blind spot is not occasional. It is structural. Any time the same author writes both the implementation and the test, the test cannot challenge the implementation’s understanding. The test can only confirm it. Confirmation tests have a real role (regression pinning, documentation, refactoring safety) but they are not what most teams thought they were buying when they wrote unit tests. Most teams thought they were buying a check against the developer being wrong. Same-author tests do not provide that check.
The unit test the agent writes against the agent’s code is a high-confidence statement that the agent did what the agent meant to do. It is a low-confidence statement that the agent meant to do the right thing.
Seams Are Where Truth Hides
A seam is a boundary between components that the same author does not control on both sides.
The database is a seam. The agent did not write the database. The database has its own rules about transactions, isolation, constraint enforcement, and rollback. A test that crosses the seam (writes to a real database, reads back, asserts on the state) discovers truth the agent could not fake by reading its own code. If the agent’s code does not handle a unique constraint violation correctly, the seam test fails, because the database actually enforces the constraint. The unit test that mocks the database does not fail, because the mock does whatever the agent’s setup said it should do, which was determined by the agent’s model of how the database behaves.
The queue is a seam. The external API is a seam. The auth service is a seam. The peer service that another team owns is a seam. Any time the system depends on something the agent did not author both sides of, the boundary between the agent’s code and that thing is a seam, and tests that cross the seam carry information the agent’s same-author unit tests cannot carry.
The cost of seam tests used to be the reason the pyramid pushed them up and away. Slow setup, flaky network, environment drift, the database that crashed every other Tuesday. Most of those costs have collapsed. Ephemeral, containerized real dependencies, spun up per test or per test class, deliver real-database-real-queue-real-API integration tests at a runtime that is bounded by the test’s actual work, not by the environment ceremony around it. The same agent that flattened authoring cost also flattens seam-test setup cost, because the setup is itself code the agent writes once and the harness reuses.
[Fact]
public async Task Warehouse_rejects_reservation_when_inventory_is_zero()
{
using var warehouse = await EphemeralWarehouse.Start();
await warehouse.SetInventory(sku: "SKU-1", quantity: 0);
var client = new WarehouseClient(warehouse.ConnectionString);
var result = await client.Reserve(sku: "SKU-1", quantity: 1);
result.Should().Be(ReservationResult.OutOfStock);
}
Enter fullscreen mode Exit fullscreen mode
That test crosses a seam. The warehouse process is real, in a container, with its own logic about inventory. The agent did not write the warehouse. The test is not a confirmation of the agent’s mental model. It is a question put to a system the agent does not control, with the answer the system gives. That is where truth lives now.
The Other Variable Changed Too
The pyramid argument was not just about authoring cost. It was also about runtime cost: integration tests were slow.
Slowness mattered because feedback speed mattered. A test suite that took thirty minutes to run did not get run on every commit. It got run nightly, or before merge, or only in CI. The developer who wrote the code did not see the result until later, by which time the next change was already underway. Fast feedback was structurally tied to fast tests, and fast tests were structurally tied to small scope, which meant unit tests.
That coupling has also weakened. Containerized dependencies start in single-digit seconds. Test isolation that used to require an in-memory mock now runs against a real database in approximately the same wall-clock time. Parallel execution at the test-class level (which the agent can configure as cheaply as it writes the test) further compresses the runtime. A suite of seam-crossing tests that would have taken twenty minutes a decade ago runs in two minutes today.
The feedback-speed concern is still real, but it no longer maps cleanly onto test level. Unit tests are still the fastest, but they have stopped being the only tests that can be in the inner-loop feedback cycle. Seam tests are close enough that the team can run them on every commit and on every push. The slow-test problem has not disappeared, but it no longer aligns with the unit-versus-integration boundary the pyramid was built around.
The variable that changed was not just authoring cost. It was the whole grid of constraints the pyramid was solving against. With every variable in the grid moving, the optimal shape moves with it.
Coverage of Seams, Not Lines
The metric the pyramid implicitly optimized for was line coverage. More tests, more lines exercised, higher coverage number, healthier suite. That metric was always weak (a test that exercises a line without asserting on what it does counts the same as a test that pins the line’s behavior) and the prior post in this thread named why coverage stopped being a useful proxy. The new metric the agent era needs is different again.
Count seams, not lines.
A seam-coverage metric enumerates the boundaries the system depends on (databases, queues, external APIs, auth services, file systems, peer services, the clock) and asks, for each one, whether a test exists that crosses the boundary and pins a contract on it. The number is small. A typical service has somewhere between five and twenty seams. Coverage of one hundred percent of seams is a tractable goal. The signal is high: a seam without coverage is a seam where the agent’s unit tests are confirming the agent’s mental model of a component the agent does not control, which is the exact configuration that ships silent regressions.
// Seam coverage for OrderService:
// PostgreSQL persistence layer [PASS, pinned by OrderRepository contract tests]
// RabbitMQ event publisher [PASS, pinned by OrderEvents seam tests]
// PaymentService HTTP client [PASS, pinned by PaymentGateway contract tests]
// WarehouseService HTTP client [FAIL, relies on agent's mock]
// Token verification service [PASS, pinned by TokenValidator integration test]
// System clock [FAIL, relies on agent's stub]
Enter fullscreen mode Exit fullscreen mode
The checklist is the metric. Six seams, four covered, two gaps. The gaps are the priorities. The line-coverage number on the underlying code does not appear in the checklist because the line-coverage number does not measure what the team cares about anymore. The team cares about whether the next change against any of these seams will be silently wrong.
This is the metric reframe most teams have not made. Coverage of seams is what survives agentic authorship. Coverage of lines is what the agent’s tests certify about the agent’s code, which is approximately tautological once same-author dynamics are understood. The teams that switch the metric early are the teams that catch the regressions everyone else is shipping.
Invert the Pyramid Deliberately, Not Carelessly
The wrong reading of this post is “stop writing unit tests, write end-to-end tests instead.” That is not the argument. End-to-end tests still cost what they cost (slow, brittle, broad-blast-radius when they fail) and the agent did not change that part. The argument is more specific.
Keep unit tests for what they do well: pinning small invariants, documenting the loop, providing fast feedback on the smallest behavioral units. Just stop treating their abundance as the goal. A team with three hundred unit tests and full seam coverage is in better shape than a team with three thousand unit tests and gaps at four seams. The first team’s suite is shaped to catch the regressions the agent’s authorship most reliably produces. The second team’s suite is shaped to confirm the agent’s understanding of itself.
Spend the freed authoring labor on seams. The agent that used to be told “write unit tests for the new code” should be told “write unit tests for the small invariants and write contract tests for every seam the new code touches.” That instruction shifts the suite’s center of gravity from confirmation toward truth, deliberately, without throwing away the parts of the pyramid that still work.
The pyramid was the right answer to a different question. The right answer to the new question has the same components (unit tests, integration tests, contract tests, the occasional end-to-end test) in different proportions and aimed at different jobs. The new shape is not a triangle pointing the other way. The new shape is a portfolio, balanced against where truth hides under agentic authorship.
The pyramid was an economic argument. The economy changed. The shape should change with it.
답글 남기기