Some time ago I happened to coordinate the rearchitect of a software system for a public administration. It was an internal system, used by fewer than thirty people, not all of them concurrently, not every day. It was also exposed to citizens, for lookup features and a few simple requests: four or five a day at peak times.

When I opened the code, I found a multi-tier architecture that would have held up without a problem under the biggest national click day. Stacked layers of abstraction, interfaces for every component, formal separation between every responsibility in the system. All correct. All documented. All, in that context, completely useless.

It wasn't a badly built system. It was an oversized one. And that difference, between badly built and oversized, is the point of this article.

Two kinds of complexity

There is complexity that is needed and complexity that is not.

The first arises from the problem itself. When a system integrates multiple data sources, must stay consistent under load, and must handle failures without losing information, the layers of abstraction that govern it have a precise reason to exist. You pay an architectural cost, but you solve a real problem. These cases exist, but they are the minority. Most of the software produced for the PA has nothing distributed about it, nothing to integrate at scale, nothing that justifies that structure. And yet that structure is almost always there.

The second is complexity that answers no need of the system. It is there because it is expected to be: because "that's how it's done", because it gives the impression of a serious product, because nobody ever stopped to ask whether it was really needed. It's what I call ritual complexity: it answers to a form, not to a function.

The problem with the Italian PA is not necessary complexity. It is the amount of ritual complexity it produces, pays for and maintains.

Why the system produces it

There is a tendency to blame this complexity on the people who write the code. That is a mistake of perspective. Ritual complexity almost never comes from a technical choice. It comes from incentives.

PA technical specifications rarely impose a specific way of building the software. What they do is reward, in the evaluation criteria, whatever is formally measurable: the amount of documentation, the presence of an elaborate structure, adherence to a generic idea of an "enterprise solution". Simplicity, which is harder to measure than a list of components, is rarely valued.

On top of this comes risk aversion. In a public procurement, looking too lean is dangerous: it exposes you to the question "is it robust enough?", far more than an excess of structure exposes you to the question "was all of this really necessary?". The first question gets asked at acceptance testing. The second almost never.

Faced with these incentives, the supplier responds rationally: it builds what gets rewarded. No bad faith is required. All it takes is a system that measures software quality by form, not by effectiveness. That system is the problem, not those who adapt to it.


What it costs

Ritual complexity has a price, and it is paid twice.

The first time is at construction. The same software, with the same required functions, costs the public administration several times what it would cost a private company of comparable size.

Part of this gap is legitimate: regulatory constraints, accessibility requirements, security, interoperability with public systems are real, due, justified costs. But a significant part has no technical justification at all. It is the cost of a structure nobody questioned, of documentation produced to meet a requirement, of development time that grows more than proportionally with complexity that wasn't needed.

This initial price, however, is only the down payment. The real cost comes later, and accumulates over time.

Onboarding time. Anyone taking over a system whose logic is spread across many layers takes weeks just to get oriented. Weeks in which they don't produce, but still cost.

Diagnosis time. When something breaks, and sooner or later it does, tracing the problem back to its source means going through every layer. In a lean system it's a matter of hours. In an over-engineered one, of days.

Resistance to change. The more elaborate the system, the more each change touches different components, requires coordination, and raises the risk of regressions. The practical result is that changes become rarer and rarer. The software stops evolving and grows old while still in use.


The cost that never ends

There is one last element that turns a high cost into a permanent one: documentation.

The specifications require it, and it gets produced. But in most of the cases I've seen, it is produced to satisfy the requirement, not to be useful to whoever will work on the system in the future. It describes the structure, not how it works. It lists the components, but doesn't explain why they exist, how they behave in edge cases, what decisions were made and for what reason. It is documentation that exists and doesn't document.

The consequence is that the knowledge that matters, the knowledge accumulated by those who built the system, is not written down anywhere. It stays where it has always been: in the supplier's head.

This is where lock-in comes from. An oversized, poorly documented system can't practically be handed over, even when it formally can: whoever takes over has to rebuild their understanding of the system from scratch, and changing supplier becomes too risky, too slow, too expensive. Whether this is a deliberate outcome or simply the product of the way the system was built, for the contracting authority it makes little difference. The result is the same: it pays for construction once, and for maintenance forever, often to the same supplier, because viable alternatives no longer exist.

It is worth remembering this when we talk about modernization: moving a system to new infrastructure doesn't solve this problem if the architecture and the documentation stay as they were. The lock-in isn't in the place where the software runs. It's in the software.


What should change

Nothing I've described requires new technologies or revolutionary principles. It requires consistently applying a few things we should already be doing.

Proportionate requirements. The structure of a system should be sized to the real problem: how many users, what concurrent load, what operational criticality. A system used by thirty people shouldn't be designed as if it served thirty thousand. It seems obvious. In practice, it almost never is.

Useful documentation. Documentation should be judged by a single criterion: can a third party, with it in hand, take over the system independently? If the answer is no, the page count doesn't matter. Only that does.

Real reversibility. The ability to change supplier should be an explicit, verified and measurable requirement, not a theoretical assumption never put to the test. A system you can't leave is a system over which you have already given up control.

Expertise on the client side. Everything else is pointless without someone, on the commissioning side, able to technically evaluate what they receive. Someone who can recognize ritual complexity when they see it, and has the authority to reject it even when the specification seems to legitimize it. Without this figure, every well-written requirement stays on paper.

Simplicity is not a shortcut, nor a compromise on quality. It is a design choice, and often the hardest one to defend. Making it the rewarded choice, rather than the suspect one, is the real change.


A note on artificial intelligence

All of this predates artificial intelligence, but AI makes it more urgent, because it is an amplifier.

By default, generative AI tools produce elaborate architectures: they were trained on existing code, which is full of ritual complexity. But "by default" is the key phrase. Guided precisely, with the system's context clearly defined, the real constraints, the expected load, the nature of the operations, the same AI produces lean, well-reasoned solutions. It can also analyze and document poorly documented existing systems, and speed up diagnosis on code that nobody knows in its entirety. Used with expertise, it is one of the most effective tools for regaining manageability on systems that have lost it.

Used without expertise, it does the opposite. AI doesn't reduce ritual complexity: it produces it faster. A poorly designed system that took six months by hand can be generated with AI in three weeks. You save time. The damage is identical.

The decisive variable is not the tool, and it is not the infrastructure. It is who guides it: someone with architectural expertise, knowledge of the application domain and the ability to evaluate the result critically, not just produce it. In the Italian PA this figure exists. It is just too rare. And that, more than anywhere else, is where it would be worth investing.