The Perfect System Doesn't Exist

November 15, 2021 4 minute read

I am yet to meet a software developer or a construction worker who is satisfied with the job of the person before them. After more than seven years in the industry, I’m used to the fact that most conversations in the first month on a new job start with guilty explanations about the state of the system.

I went from company to company, carrying with me the bitterness of all the anti-patterns I had faced. I read so much about clean code, design patterns, and well-designed distributed systems but I never got to see them in practice.

Somehow they were always short of perfect.

At one point I was genuinely angry at the people whose implementations I had to rework just to get the codebase to a good state again. When I joined a team created to build a new system from scratch I knew I finally had a go at making that excellent design from the books.

A year later, with a system nothing close to perfect, I realized that even armed with all the theory and context, the perfect system still remains a mythical creature. In other words - it doesn’t exist.

Why Does Perfection Escape Us

I don’t write this with cynicism in my heart, unhappy because things are not as advertised. I write it as a person who’s finally come to terms with the fact that perfect is the enemy of good.

A sense of unease used to overcome me each Monday morning when I opened the laptop to fight the race conditions, tangled inheritance hierarchies, and tight coupling. Now I get a sense of excitement, knowing that I have the chance to improve something.

I’ve found two main reasons for the problems I kept facing (and the ones I kept causing) - we rarely do the same thing twice and change is much faster than anticipated.

Never the Same Implementation Twice

If we kept developing the same functionality over and over again we would have established concrete practices that would work in any scenario. But each business domain is different and a small change in the business processes may mean a large change in system design.

I recently got to work with a startup developing messaging software for a business niche. A small implementation detail specific to the field threw all my understanding of chat applications out of the window.

When switching from company to company, we don’t carry over the implementation experience that we’ve gathered. We bring only the high-level abstract concepts and practices, but the details vary from business to business.

I had plenty of experience working with single-page applications, but when I started working for the Financial Times I saw that they used them in a completely different way. They didn’t do client-side routing because statistically people browse news websites atomically. They open at most a couple of pages before closing the website.

So I could only carry over my fundamental knowledge and ideas, the way those technologies were used was completely different. What is considered a best practice for one domain may be an anti-pattern for another.

There are best practices on the technology level, but not on the business level. There are no books or courses on building systems for every different field. We only have our previous experience and knowledge to rely on.

Things Change Even When They Don’t

This brings me to the second factor. Even if we somehow managed to crystallize fundamental patterns that are applicable across all (or at least most) domains, the environment in which we work won’t remain the same.

The startup we’re working for makes a turn or your product director leaves and the next one brings a different vision. Our system was focused on end-users, ensuring high availability but now you’re moving to a B2B business model and the largest requirement there is consistency.

Well, that throws our design out of the window. Now we’re only left with two options - significant technical debt or partial rewrites.

Even so, successful technical patterns do emerge. In fact, every few years when the current wave of technologies matures, we end up with high-level designs which have proven to be successful. They stick around for a while until the next wave emerges and we start the cycle again - experimenting until we find something that sticks.

It took the React community a couple of years to find a good-enough implementation of the Flux architecture. Redux proved itself to be the best solution and for a while became the de-facto state management standard for front-end development.

After a couple more years paradigms changed and we now move in favor of more granular solutions forcing us to rethink the existing patterns that we had in place. Just like that, the design of a whole lot of software became unlikeable to people who favor the new paradigms.

Good > Perfect

This is not a submission that good design is impossible. It is. Perfect design isn’t. Having a good codebase all the time is better than having a perfect system for a little while.

Perfection will be just out of reach. So instead of looking for it, we should be looking for continuous improvement. We should find and fix common anti-patterns, address the worst performance problems and worry less about duplication.

Aim to make an enjoyable project to work on.

In a realistic environment, you will never be able to fully pay off your technical debt. You won’t get the chance to ride off into the sunset. It’s a never-ending process of improvement and that’s part of the job.

The perfect system can be built only if it’s frozen in time. True mastery lies in maintaining a codebase as it gets shaken like a ship in a storm.

It will be perfect before a key library becomes outdated and we don’t have the time to update it. Before a framework gets a major release and we postpone the migration due to breaking changes. Before the business pivots making you duplicate code because it’s easier than reworking an abstraction.

You can have the ideal system until you are forced to say “it works as it is, let’s get the most pressing problems solved and we’ll figure out the rest later”. Having realized that, I’m fine with pushing the rock up the hill, knowing I won’t reach it.

As Camus says, if we’re put in the place of Sysiphus we should be smiling.

Tao of Node

Learn how to build better Node.js applications. A collection of best practices about architecture, tooling, performance and testing.