Saturday, 22 August 2026

Complexity Is Not the Metric. Cohesion Is.

Third in a series on how software architecture shapes AI-driven code degradation. Last time the standard erosion metric gave the backwards answer. Here is why, and what to measure instead.

The previous post ended on a problem. Erosion failed because it was blind to location. It measured complexity. And complexity, it turns out, is the wrong thing to measure.

This post is about the right thing.

What complexity actually measures

Cyclomatic complexity counts branches. It tells you how tangled the control flow of a function is. That is a real property. It is also a property of an algorithm, not of an architecture.

Here is a thought experiment. Take one rule. Validate a phone number. Say it carries a complexity of 8. You can drop that logic straight into the existing endpoint handler. Or you can put it in its own small function called ValidatePhone. The complexity is 8 either way. Cyclomatic complexity cannot tell the two apart.

But they are not the same. Not for the humans who maintain the code. Not for the AI agent that changes it next. In one version the phone logic is tangled with twenty other rules. In the other it stands alone. Same complexity. Completely different code.

That is the whole point. Complexity is a property of the code. What we care about is a property of the arrangement.

The word for it is cohesion

Cohesion asks a simple question. Does each unit do one thing, or does it do many things mashed together? A cohesive function has one reason to change. A god method has twenty.

This is what the additive versus mutative story is really about. Not less complexity. Better placed complexity. Small, separate, single-purpose units instead of one growing method that every new rule reaches into.

So the honest fix for our failed metric is not a tweak. It is a change of target. Stop measuring how complex the code is. Start measuring whether responsibilities are kept apart.

Why the textbook cohesion metrics do not help

Cohesion is not a new idea. There is a whole family of metrics for it. LCOM, for Lack of Cohesion of Methods. TCC, for Tight Class Cohesion. So why not just use those?

Because they were built for a different shape of code, and ours breaks their assumptions three ways.

First, they measure cohesion through shared fields. Two methods are cohesive if they touch the same instance variables. But our degradation lives in stateless request handlers. They barely have fields. Their dependencies are injected and shared by everything. So field sharing says nothing. Every handler looks cohesive.

Second, the graph variant, LCOM4, joins methods that call each other. The god method calls all of its little helpers. So they all connect into one component. The class scores as cohesive at the exact moment it becomes a god class.

Third, and worst, none of them look inside a single method. Our main failure mode is one method doing twenty things. That is one method. A metric that measures cohesion across methods cannot see it at all.

The classic tools are not wrong. They are aimed at data classes. We have stateless pipelines and bloating handlers. Different problem.

The trap of fake modularity

There is a tempting shortcut here. If cohesion means small separate units, just reward small separate units. Count the files. Count the functions. More small pieces, better score.

That shortcut is a trap, and it has a name. Recent work on AI-generated code calls it the Modular Mirage. Agents happily split code across many files. But file separation is not cohesion. You can shatter a god method into ten fragments that still make no sense apart. That is not clean. It is a different kind of mess.

So the bar is higher than counting units. A good measure has to reward real isolation. It has to refuse to be fooled by scattering. This matters for us specifically. It would be easy to claim OfficeFloor wins simply because it makes more files. That claim would be worthless unless the new units genuinely absorb change on their own.

The reframe

Put it together and a better idea appears. Do not score a snapshot of the code. Score a change.

When a new rule arrives, ask one thing. How much existing, entangled code did it force the agent to disturb? A cohesive system lets you add a small new unit and walk away. An incohesive one makes you reach into a crowded method that many earlier rules already depend on. And the cost of reaching in should scale with how tangled that method already was.

That is the shift. Not the complexity of what you touched. The complexity of what you had to disturb to touch it. Isolated new work should cost almost nothing. Surgery on a god method should cost a lot. Fragmentation without cohesion should not be free either.

That is a lot to ask of one number. It turns out to fit into one. The next post builds it, and then puts it to the test.

No comments:

Post a Comment