Related reading: Coverage Was a Proxy. The Proxy Just Failed. is the direct predecessor; this post extends the same argument from assertions to structure. The Vocabulary Is the Product arc (The Hidden Output of TDD Was Never Code, Agents Amplify Whatever Vocabulary They Find) names what actually fragments when cohesion fails.
The codebase passes every metric on the dashboard and the concept has no address.
That is the shape of the new failure mode. The dashboard shows small files, small functions, low cyclomatic complexity, low coupling, and green tests. The reader looking for discount finds seven names for it in six files under three folders, no owning module, no shared type, and no test that exercises the concept from end to end. Nothing about that state is illegible to the metrics. Every function is small, every module is decoupled, every test passes. Structural modularity is intact. The concept the code was supposed to represent has been distributed across the codebase in a way that no per-file review will notice.
The industry inherited a set of structural-modularity metrics from an era when the concept and the code were shaped by the same author. When one team wrote the discount logic, the discount logic sat together, was named consistently, and drifted only under the pressure of turnover or time. Agents changed the shape. The same team no longer writes the concept end to end. The concept accretes across sessions, across prompts, across PRs, and the structural metrics keep saying “everything looks fine.” The next axis of measurement is not structural. It is conceptual. Cohesion is the property the suite now owes the codebase, and it lives at a granularity coverage was never asked to see.
The Old Metrics Measured Form, Not Concept
Files per line, average function size, cyclomatic complexity, dependency counts. Every one of these is a measurement of the shape of the code. None of them is a measurement of the concept the code represents.
That distinction was easy to miss for two decades because the two lined up. A team that broke a large function into small ones typically also broke a large concept into named pieces. A team whose cyclomatic complexity dropped had usually done the design work that would have dropped the conceptual complexity along with it. The metrics rode on the back of a design discipline the metrics never actually measured.
Remove the design discipline and the ride ends. An agent breaking a function into small pieces will produce small pieces. Every metric drops. Nothing in the metrics knows whether the pieces still add up to the concept. If the pieces are named for local behavior instead of the concept, and if no test crosses them, the concept has fragmented and the dashboard is still green. The metrics were measuring what was easy to count, not what mattered. The correlation was a gift the design discipline gave for free. The gift is now gone.
The metrics are not lying. They are answering the wrong question. The right question is not “is the code well-shaped.” It is “does the concept still have a home.”
Agents Produce Codebases That Pass Every Structural Test and Fail the Domain One
Three discount paths. Three near-identical reducers. Three subtly different names for the operation that applies a percentage to a subtotal.
That is what an agent-generated codebase looks like at scale. Sprint one, an agent added applyLoyaltyDiscount to PricingService. Sprint three, a different session added calculateDiscount to CheckoutModule, unaware of the first. Sprint five, a third session added discountedTotal to OrderService because the surrounding code used verbs that read that way. Every function is small. Every module has a single responsibility on paper. Every test passes.
A reviewer looking at any one PR sees a small, contained change with a matching test. A reviewer looking at the codebase six sprints later finds three implementations of the same rule, drifting quietly. If the loyalty percentage changes, one of the three gets updated. The other two do not, because their names did not include “loyalty” and grep did not surface them. Customers on the second and third code paths see the old percentage for a quarter, and the team files a bug that reads as a business-logic inconsistency because from the customer’s view that is what it is.
The failure mode is not “worse code.” Every one of those three functions is competent. The failure mode is that the concept discount has no owning module, no shared type, and no test that names the invariant across the three call sites. The structural metrics all pass. The concept is scattered.
The Numbers Are Already in the Record
The pattern has been measured. A 2026 maintainability report on AI-assisted codebases clocked duplication up eightfold across a two-year window and up 81% in the AI-assisted subset specifically. Cross-file function calls dropped by 35%. Refactoring line moves dropped by 70%. A separate 2026 debt-survival survey across nearly half a million AI-introduced issues found 22.7% still living at HEAD.
Read together, those numbers describe the same shape. AI-assisted code duplicates more, reuses less, refactors less, and ships defects that survive longer. Every one of those movements is compatible with passing per-function metrics. None of them are compatible with a codebase where the concept has a single address. The measurement gap is between code that looks modular and code that is cohesive around the domain it represents.
The Volume-Quality Inverse Law is not folklore. It is the visible artifact of a discipline (concept ownership across files) the old metrics never measured because the old workflow made it automatic. When the automatic mechanism went away, the metrics stayed calibrated to the world where it existed. The gap is where the damage compounds.
Cohesion Is Not Structural Modularity
Modularity says the pieces are independent. Cohesion says the pieces that belong together sit together, are named consistently, and read as one concept across their locations.
These are different properties. A codebase can be highly modular and low-cohesion at the same time. Every module is decoupled from every other module. Every module also uses its own name for what should be a shared concept. The dependency graph is clean. The domain model is fractured. Modularity metrics score the first observation. Nothing scores the second.
Cohesion in the sense used here is not the LCOM number the old literature named, and it is not the connascence taxonomy either. Both are useful and both live at the code level: they measure whether functions that share state are grouped, or whether the coupling between two units is structural or semantic. The domain-level version is one layer up. It asks whether the concept the team named on a whiteboard has a single address in the codebase, a consistent set of nouns, and a set of assertions that pin it across whatever files it happens to live in. The old vocabulary is the ancestor. The domain framing is the sharpening.
Modularity is a code metric. Cohesion is a domain metric. A codebase can be either without the other. The agent era makes the second one visible by making the first one uncorrelated with it.
The Test Surface Must Cross the Concept, Not the Function
A unit test pins the behavior of a function. A domain-concept test pins the behavior of a concept across whatever functions currently implement it.
That is the shift. If discount lives in three files under three names, the only test that can pin the concept is one that composes builders from all three modules and asserts an invariant that must hold across them. A per-function unit test on applyLoyaltyDiscount cannot see calculateDiscount. A per-function unit test on discountedTotal cannot see the other two. The invariant “loyalty discounts and promotion discounts never stack below zero” is not verified anywhere unless a test crosses the concept.
[Fact]
public void Loyalty_discounts_and_promotion_discounts_never_stack_below_zero()
{
var order = anOrder()
.forCustomer(aLoyaltyMember().inTheirFirstYear())
.containing(aBookCosting(60.dollars()))
.withPromotion(blackFridayDoubleDiscount());
var receipt = checkout.process(order);
receipt.total.Should().BeGreaterThanOrEqualTo(Money.Zero);
receipt.discountBreakdown.Sum().Should().Be(receipt.subtotal - receipt.total);
}
That test reads like a sentence in the domain and it exercises three modules in one run. If any of the three drifts, the test goes red. If a fourth discount path is added in a future PR, either the test fails (because the invariant no longer holds) or the new path composes against the same builders (in which case the concept is being maintained). The suite has taken ownership of the concept. No per-function test does that job.
The old test surface was per unit. The new test surface is per concept. The shift is not a rewrite. It is an addition. Unit tests still pin local behavior. Domain-concept tests pin what the local behaviors add up to. The suite that carries both is the suite that can catch the failure mode the metrics do not see.
Grep Traversal Is the Cheap Diagnostic
Pick a domain concept. Grep the codebase for its name. Count the matches, the distinct names, the files.
That is the diagnostic. It takes ninety seconds. It produces a number a team can act on. A concept whose name appears in one file under one shape is cohesive. A concept whose name appears under seven variants across six files under three folders is fragmented, and every future agent-generated PR is going to sample the fragmentation and add to it.
The traversal cost is a proxy for a real metric a team can define however it likes: number of files a reader has to open to understand the concept end to end, number of distinct nouns naming the same operation, ratio of concept-crossing tests to concept-implementing files. The exact metric matters less than the discipline of running it. A team that periodically greps its own vocabulary and treats the drift as a refactoring backlog keeps its concepts traversable. A team that does not discovers the fragmentation only when a customer files a bug.
Grep, a naming convention, and a quarterly review of the top ten domain concepts is a starting shape. The measurement is coarse. The action it drives is not.
Cohesion Is Owned by the Suite Now
Tests are the only artifact that can cross module boundaries at the concept level and refuse to compile when the concept fractures. That is the new job.
Coverage was structural: does the test exercise the line. Mutation was assertional: does the assertion actually pin behavior when the code changes underneath it. Cohesion is conceptual: does the suite have an assertion somewhere that pins what the concept must mean across whatever files currently implement it. The three metrics stack. A suite can pass coverage and fail mutation. A suite can pass both and still fail cohesion. Each layer catches what the previous layer could not see.
The refactor that fixes fragmentation is not a stylistic cleanup. It is the design work of naming the concept once and pointing the call sites at that name. A single Discount value type, a single applyDiscount policy chain, one file where the concept lives, one test that pins the invariant. Test count often drops, because the fragmented duplicated tests collapse into the concept-crossing test that replaces them. Duplication drops because the three near-identical reducers become one. Every future agent-generated PR now composes against a single named concept, because the vocabulary the agent samples has one canonical entry instead of seven. Cohesion is the property the refactor step authors. The suite is where it gets pinned.
The codebase passes every metric on the dashboard and the concept has no address. That was the opening. It is also the closing, because the situation is common enough now that it is the default outcome of shipping AI-assisted work without a concept-crossing test surface. The dashboard is calibrated to a world where the concept had an owner. The concept still needs one. The suite is where the owner now lives.
Coverage was line-shaped. Mutation was assertion-shaped. Cohesion is concept-shaped. The metric that survives the era is the one whose failure mode looks like the era’s actual damage.