Skip to content
Back to the blog

Volatility-based decomposition. Part 2: every module is an insurance policy

Decomposing by volatility never tells you which of the volatilities you find deserves a module, and that is where the money goes. This article prices every encapsulation like an insurance policy: the premium is paid on every future feature, the payout only arrives if the change happens, and the premium is not set by the code you write but by what the encapsulation forbids. With arithmetic, a documented case, and a scale of deductibles so you stop choosing between everything and nothing.

Part 1 closed with a line that now looks to me like a trap: the components that come out of a volatility-based decomposition «can be seen as options». True, and not enough. You buy an option for the right to something good. You buy a module that encapsulates a volatility out of fear of something bad. That is not an option: it is insurance.

The distinction is not semantic. Gregor Hohpe has spent years explaining that architecture sells options, and he is right at the level where he says it: deferring a decision has measurable value. But when you go down from strategy to the module, the correct financial analogy changes, and so does the question. An option is valued by what you stand to gain. A policy is priced by what you stand to lose and, above all, by what it costs to keep it alive while nothing happens.

That is the gap Part 1 left, and that almost everything written on this subject leaves, Löwy included: it explains how to find the volatility, not which of the ones you find deserves a module. Every architecture has twenty identifiable volatilities and a budget for three.

The policy you sign without reading it

Every time you encapsulate a volatility you sign a contract with these three parts:

  • The premium. What you pay every month, whatever happens. In architecture it is the indirection: the interface you have to maintain, the mapping written twice, the native capability you can no longer use. Martin Fowler called it cost of carry: the complexity the piece adds makes every feature that comes after it more expensive, not just the one that justified it.
  • The claim event. The change, if it arrives: the vendor that raises the price, the new regulation, the engine that has to be replaced.
  • The payout. What you save when the claim event arrives. Careful: it is not the cost of the change, it is the difference between doing it with the policy and without it.

All three can be estimated and none of them is ever estimated in a design review. What gets discussed there is whether the volatility is real, which is the easy question and one whose answer is almost always yes.

Why does nobody price the premium?

Because the industry inherited half of the original criterion. When Parnas set out the criteria for decomposing a system into modules in 1972 he did not say «hide what changes»: he said hide what is likely to change. There is a likelihood in there, and a likelihood is a number between zero and one. Fifty years later the criterion circulates without the number, turned into an absolute: hiding is good, therefore hide everything.

Fowler is the other half of the answer. His article on YAGNI is the best description I know of what it costs to build for a future that never comes, and then he writes himself out of scope: he clarifies that YAGNI does not apply to effort spent making software easier to modify. That leaves our case outside the only tool the industry uses to hold back speculative work, because encapsulating a volatility is, by definition, effort to make a future modification easier. Go right ahead. And in the same article there is another rule pushing the opposite way: YAGNI applies whenever you add complexity today that you won't take advantage of until later, and a module encapsulating a volatility that hasn't moved yet meets that definition comfortably.

I don't think he contradicts himself. I think the gap between his two rules is where over-insured architecture lives, and that it doesn't close with a third rule: it closes with a quote. Incidentally, the example he chose is an insurer pricing storm and piracy risk. The insurance was in the domain of the example; what I'm arguing here is that it was in the architecture too.

Getting the volatility right and losing anyway

Segment told the story publicly of how it split its monolith into one service per destination it sent events to. The volatility diagnosis was flawless: every destination is a third party's API that changes when it likes and fails when it likes, and a shared queue meant the slow destination punished all the others. They encapsulated exactly the right volatility.

And they ended up back in the monolith, because the premium grew with the catalogue. They were adding on the order of three destinations a month, and each destination brought its own repo, its own queue and its own service, so operational load grew in a straight line with the number of policies underwritten.

This is the part that falls away when the case gets cited as an argument against microservices: it wasn't a diagnosis error, it was a pricing error. They were right about the volatility. The bill arrived anyway.

The premium is not set by the code you write

Here is the sum that does fit on a napkin. A team of six, a five-year horizon, and the same 48 working weeks a year as always: about 1,440 person-weeks of capacity.

Policy number one, the most widely underwritten in the world: the repository layer so you can switch databases.

  • Premium: the layer forbids whatever is specific to the engine, so every query is written against the lowest common denominator. Call it a 3% overhead per feature: 43 person-weeks over five years.
  • Probability of the claim event, switching the primary engine within five years: be generous, 10%.
  • Cost of the change without the policy: 8 person-weeks of migration.
  • Expected payout: 0.10 × 8 = 0.8 person-weeks.

Forty-three against zero point eight. Fifty-four times over-insured, and the real number is worse, because this policy degrades its own payout: on migration day you don't move the system you would have built, you move the impoverished version the layer forced you to write. It is the lowest common denominator of multicloud, at module scale.

Policy number two: the tax calculation in a module.

  • Probability of the claim event within five years: 100%. Rates and invoicing rules change; it is the only thing they do.
  • Cost of the change without the policy, with the calculation spread across forty call sites: some 3 person-weeks each time, and there will be several.
  • Premium: a module with one function and one table, which forbids you nothing. Practically zero.

Both cost the same to write: a day's work, an interface, a few tests. And one is a 43-week waste and the other is the best buy on the project. The difference is not in the code.

The premium is not set by the code you write, it is set by what the encapsulation forbids.

That is the question missing from the design review. Not «how much does it cost us to build this?», which is cheap and therefore uncontested, but «what do we lose the ability to do for as long as this stands?».

«And if I do have to migrate and I don't have the layer?»

Then you pay for the migration, which was the plan. It is called self-insurance and it is what you do with your phone without thinking about it: insuring 200 euros for 40 a year makes no sense, and it doesn't start making sense because the phone actually breaks.

Fowler concedes this point and explains it better than I would: the times YAGNI goes wrong do exist, they are expensive and they are remembered, whereas the times it saved work leave no scar. It is availability bias. Every architect carries around a migration that would have been easier with the layer, and none of them remembers the eleven layers they didn't build.

Policies come with a deductible

Here the metaphor gives back more than I asked of it. In insurance nobody argues between full cover and nothing: you adjust the deductible, cover part and retain the rest. Encapsulation works the same way, and it is almost never framed that way.

Three levels, from higher premium to lower:

  • Full cover. Interface, swappable implementation and contract tests against two implementations. High premium, because the interface sets the ceiling on what you can use.
  • Deductible. You don't abstract: you contain. All the SQL lives in one directory, with no layer on top, using the engine to the full. If the claim event arrives you rewrite those files, and you know how many there are. Premium close to zero because it forbids you nothing, and a smaller payout than with full cover: that's what the deductible is for.
  • Self-insurance. Nothing. The claim is paid in full if it arrives, and the decision is written into the ADR along with the probability you were assuming.

Pricing a volatility: premium, expected payout and the three levels of cover.

Pricing a volatility: premium, expected payout and the three levels of cover.

Almost every design discussion is fought between the first level and the third, and the second wins most of the time. The middle level is uncomfortable because it can't be presented as a principle: it has to be presented as a number.

Where the insurance stops being alike

Hohpe warns that a metaphor is chosen for its dynamics, not its imagery: he tells how he swapped the fuel gauge for a piggy bank once someone pointed out that a tank fills fast and empties slowly, and political capital does exactly the opposite. So it is worth saying where this metaphor does not line up, and in both cases architecture comes out worse in the comparison.

An insurance premium is fixed in the contract; an encapsulation's premium grows. It is paid by every future feature, and there are more future features every year. You sign today's premium and it index-links itself.

And you can't cancel it by letter. Pulling an abstraction out of a live system costs more than never having put it in, because it has to be dismantled from every place already using it. It is a policy with an exit penalty, and that explains why repository layers outlive the teams that wrote them.

There is one dynamic that does line up, and that I wasn't looking for: moral hazard. With the policy in place you stop watching. A team that believes it can switch vendors whenever it likes evaluates its vendor less carefully, just as the insured parks where they wouldn't park uninsured. That part of the bill shows up in no estimate.

My suggestion

In the next design review where somebody proposes a module «so we can change X», ask for three numbers out loud before discussing the design:

  1. The probability that X changes within the system's horizon. A number, said in front of everyone.
  2. The cost of the change without the policy. Imagine the full refactor. It is usually quite a bit lower than the room believes.
  3. What the encapsulation forbids us. This is the one nobody estimates, and it is the one that sets the premium.

If the product of the first two doesn't beat the third, don't encapsulate it: contain it, and write into the ADR the probability you are assuming.

I know how this ends. You are going to underwrite the policy anyway, because building the interface is cheap, it looks good on the whiteboard and nobody has ever been fired for over-insuring. Do it. I ask only one thing: write the number down. Two years from now somebody will read it, and it will be one of two things: the best-documented decision on the project, or the invoice for insurance you never claimed. ;)

References

On the Criteria To Be Used in Decomposing Systems into Modules — David L. Parnas, 1972

Yagni — Martin Fowler

Architecture: Selling Options — Gregor Hohpe

Mighty Metaphor — Gregor Hohpe

Goodbye Microservices — Alexandra Noonan, Segment

Righting Software — Juval Löwy