Complexity Has Become the Lazy Solution

In software engineering, complexity is often seen as a sign of professionalism.

A simple system looks suspicious.
A complex system looks serious.

Yet my experience has repeatedly shown the opposite.

A decade of success is not enough to convince

In my final year of engineering school, I built a CTF platform. At the time, no tool like CTFd existed. Everything had to be built from scratch.

I made simple choices: serve static files to reduce the attack surface, generated on demand; classic Unix services glued the pieces together, low-level containers isolated services from one another.

The first year was a bit chaotic, as any first attempt tends to be. But iteration after iteration, the system stabilized. A few well-scoped micro-services came to structure the critical parts. And for ten years, it ran like clockwork. No major incidents. No outage caused by a poorly understood service going down.

Yet every time I presented the architecture, I ran into the same questions:

  • Why isn’t there Kubernetes?
  • Why are you using the filesystem like that?
  • Why isn’t there a REST API?

I never managed to convince anyone. When I left, my work was thrown away.

The new solution is built on CTFd: a generic product that requires far more computing power, offers fewer features, and runs on Kubernetes, of course.

Since then, I keep asking myself the same question: why does the software industry value complexity over simplicity?

Complexity has become the default reaction

Today, faced with a technical problem, the instinctive reaction is almost always the same: add more. More machines, more software layers, more cloud, more abstractions.

CPU at 100%? Add more CPU.
The system is slow? Migrate to the cloud.
The architecture doesn’t look right? Switch to microservices.

But these decisions are often made before anyone has understood the problem. When a system becomes slow, the modern reaction is to add servers. An engineer’s reaction should be to ask why.

Saying “it’s slow” is not a diagnosis. It’s an admission that you’ve lost control of your system.

A simple system is a mastered system

I’m not advocating for a lack of technology. A simple system is not a rudimentary system. It’s a system where every element has a reason to exist.

A mastered system is one where:

  • you know why the CPU is saturated;
  • you know why you need an additional network card;
  • you know why a faster disk is necessary;
  • costs are genuinely understood, not just “10% less than last month.”

This is the opposite of what I see in many companies. Starting with cloud provider dashboards: meant to represent the state of the art, they are themselves frustratingly slow.

When the tools selling you performance are not themselves performant, that should be a signal.

The paradox of professionalism

In many organizations, complexity has become a marker of seriousness.

I experienced this firsthand. My Unix architecture, my static files, my lightweight micro-services: all of it was perceived as tinkering. While other event organizers struggled with their servers and CTFd, my system ran without a hitch.

Yet to the executive team, if some “engineer’s tinkering” works for 10 years, it must not have been that complicated.

On the other hand, a stack with Kubernetes, Kafka, and a service mesh will immediately be seen as “a real architecture.” Even if no one on the team truly understands it.

A simple system looks amateur. A complex system looks professional.

This phenomenon has several roots. There is mimicry: copying Netflix and Google architectures without considering their constraints. It’s the equivalent of building a three-Michelin-star restaurant kitchen to cook pasta.

There is fear of blame: if the system breaks and you’re using “industry standards,” no one will be pointed at.

And there is the résumé effect: working with Kubernetes and Kafka boosts a career. Postgres and a monolith, much less so.

The custom doesn’t disappear — it just moves

When my system was replaced by CTFd, in the name of standardization, the non-standard didn’t disappear. It just changed location.

Sure, the core became a generic product, but everything around it — the orchestration, the scripts, the duct tape, the adaptations — became the new custom.

And that code is often worse: less coherent, more scattered, less thoughtfully designed as a whole. You replace a system conceived as a whole with an accumulation of workaround layers.

This is extremely common with modern architectures. You don’t eliminate “complexity” — you move it to less visible, less controlled places. Worse still, you have to keep up with the standard’s evolution constantly: adapting your customizations, fighting structural changes, praying for the goodwill of others.

The cloud as a lazy solution

As a logical consequence, the cloud is today the primary lazy solution. Not because it’s useless — it has legitimate use cases — but because it allows you to mask problems instead of solving them.

Faced with a technical problem, there are two paths. The first: measure, understand, analyze. The second: add resources, pile on abstractions, move the problem elsewhere. Why has the second path become the norm?

Adding machines is often easier than understanding a program.

And that is where the real laziness lies. Not in choosing simple hosting or a lightweight architecture. But in refusing to understand what is actually happening inside the system.

Minimalism is not the absence of complexity

Some systems are extremely complex, and that’s perfectly fine. Google’s search engine, Criteo’s real-time ad auctions, …: these are surgical systems where every component is justified, sometimes multiple times over.

That is exactly what minimalism in engineering means: keeping only the complexity that is indispensable.

Complexity is not a problem. Unnecessary complexity is.

A startup with a few thousand users and a team of five developers does not face the same constraints as Netflix. Yet many deploy the same architectures.

AI makes this problem even more urgent

Today, AI tools can produce code and architectures at an unprecedented pace. An entire system can be generated in a matter of hours. This means producing complexity has never been easier.

In this context, the essential skill is shifting. It is no longer just knowing how to code. It is knowing how to contain the complexity that gets produced. To remain in a reflective position rather than rushing headlong toward the first generated solution.

AI is a formidable tool for quickly pivoting toward alternative solutions, for exploring different approaches. But you still need the perspective required to choose the right one.

Machines are learning to produce complexity. Engineers must learn to contain it.

The real role of the engineer

An engineer is not a code producer. Their role is to understand systems, master resources, and eliminate unnecessary complexity.

A very good engineer often does less. A well-designed system looks almost too simple. And that is precisely why it goes unrecognized: success erases the perception of difficulty.

When everything has been working for ten years, people end up thinking it couldn’t have been that hard.

As I sometimes hear: today, the rarest skill in software is not writing code. It’s knowing when not to.

Simplicity is a discipline

Simplicity is not an absence of technology. It is the result of demanding engineering work. It requires measurement, observation, and reflection. It sometimes requires resisting the social pressure that pushes toward complexity.

And in a world where machines can generate increasingly complex systems, the engineer’s responsibility is to keep those systems understandable.

Simplicity is not amateurism. It is what engineering produces when it matures.