DEV Community

Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Why Simple Systems Always Win

One of the most expensive lessons I've learned in my career was seeing how much we can complicate systems when we set out with good intentions, saying "let's do it better." It wasn't a line of code, but a "yes" that taught me this lesson. That "yes" taught me the reality that a new technology or an architectural pattern presented as a "best practice" can promise to solve more problems than we currently have, turning the system into a swamp.

I've been working with systems, networks, and software for twenty years; the common denominator underlying every major collapse and every long debugging session I've witnessed during this process has almost always been unnecessary complexity. Simple systems always win in the long run, under all circumstances. For me, this is more than an opinion; it's a field reality tested repeatedly.

The Allure of Complexity and Its Hidden Costs

We all know that new technologies and architectural patterns are appealing. Especially recent graduates or those who want to keep up with industry trends tend to immediately integrate concepts like "microservices," "event-sourcing," and "serverless" into their projects. While this is often a well-intentioned effort, it sometimes creates new problems rather than solving existing ones.

In a production ERP, we designed a module within a single monolithic service that had been running flawlessly for years as a separate service to make it "more scalable." Our intention was good: independent deployment, more flexible scaling. However, the result was a new API Gateway, a message queue, a separate database instance, and a new monitoring layer to observe everything. Problems that could previously be solved with a single git blame now turned into a nightmare requiring the coordination of three different repositories, five different services, and two different teams. We had no scaling need, nor was independent deployment a problem; we just tried to be "more modern."

⚠️ Lesson Learned

It's easy to get caught up in the allure of complexity. Setting out without always asking, "Is this really necessary?" eventually creates an insurmountable pile of technical debt. The cost of adding a new technology isn't just the initial integration time; it also brings ongoing maintenance, monitoring, and debugging costs throughout its lifecycle.

Delayed Errors and Incomprehensible Dependencies

One of the biggest problems with complex systems is that the impact of errors is delayed and unpredictable. A simple configuration error can suddenly cause a domino effect, crashing seemingly unrelated services. Once, on an internal banking platform, due to a small DNS change, BGP routing decisions were affected, and some services behind L4 load balancing couldn't receive traffic correctly. This manifested as a generic error like "connection reset by peer" instead of a direct error message, and it took us days to find the root cause.

Another example happened with the financial calculators of my side project. It had simple business logic, but I tried to make it smarter with an "agent pattern." Soon, I found that a 429 Too Many Requests error from an API, not properly handled by my agent, caused the entire calculation pipeline to stall. A simple retry mechanism and rate limiting rule could have done the same job with much less code and more reliably. Instead of a WAL rotation alarm at 03:14 AM, we were getting a generic "API Gateway Timeout" error, and it took weeks of log hunting to find the root cause. In such scenarios, complexity not only slows down the system but also turns the debugging process into torture.

The Triumph of Simplicity: Reliability and Speed

Simple systems, on the other hand, are generally more reliable. Fewer moving parts mean fewer points of failure. It's much easier to find and fix the source of an error. In my own systems, I often solved seemingly complex automations with SystemD timers and simple shell scripts. Aside from the times I wrote sleep 360 and got OOM-killed, such simple solutions have never let me down.

For example, when I experienced a WAL bloat issue in PostgreSQL, instead of immediately looking for a complex replication or archiving solution, I first checked my pg_wal_archive_cleanup_command setting and ensured archive_command was working correctly. The source of the problem is often in a simpler place than we expect. In another client project, when segmenting the network, we defined too many VLANs unnecessarily, which caused routing flaps. The problem was solved by reducing the number of VLANs and simplifying the routing tables. Simplicity is a discipline, not just in code, but in every layer of the infrastructure.

💡 Simplicity is a Discipline

Simplicity doesn't happen by itself. It's a conscious choice and requires continuous effort. Every time you add a new feature, every time you introduce a new dependency, you need to question whether you are increasing the overall complexity of the system. This discipline is vital, especially in large teams and long-term projects.

Conclusion: Why Simple Always Wins?

Simple systems always win because they are understandable, sustainable, and most importantly, predictable. Our biggest task as a system architect is to cut out unnecessary complexity. This not only solves today's problems but also makes our system more resilient to future changes and errors. Less code, fewer services, fewer dependencies mean fewer errors, less maintenance, and faster development.

What was the biggest complexity lesson in your career? Which "simple" solution saved you from a big problem? Share in the comments, I'm curious to hear what you think about this.

Top comments (0)