It happened again yesterday.
I was completely focused on some code when my manager jumped out of nowhere and started shouting: "The cyclomatic complexity is fine and the coupling between packages is acceptable! Stop splitting that class! Don’t extract a new method! And for god’s sake, don’t you dare to write another test!"
Or maybe it didn't happen yesterday, or the day before, or any day in my 20 years of professional experience. In fact, I have never seen it happen to anybody else.
One reason is because no manager knows what cyclomatic complexity is, they cannot name any refactoring, and they cannot distinguish test code from production code. And why should they? It is none of their business.
Another reason could be because good managers, even if they know our jargon, also know their limits and wisely stay away from the code. It is none of their business.
So how come even if none of my dozens of managers have ever implicitly or explicitly forbidden any technical practice, I still have seen plenty of poor codebases, bug-ridden applications and unmaintainable systems?
Could it be our fault?
Of course it is never our fault. If we had just been given the time to write clean code, to do some refactoring here and there, to write more tests, to follow all those "best" practices …
But no, your manager keeps on pushing for more features and arbitrary deadlines. So we are forced to cut some corners, and which corners do we choose to cut?
It is you that decides to not clean up, it is you that decides that each one should work on its own, it is you that decides to not write any tests. All those practices to improve quality have an extra cost.
And quality is expensive.
Quality is value to some person
Application quality, without question, takes time: a pretty UI, an awesome UX, fast, slick, scalable, always available, feature
rich correct... All those things are going to impact your clients’ perception of the quality of your application.
But what about code quality?
The state of your codebase and your systems, whether you use tabs or spaces, is completely irrelevant to your users. It has zero value to them.
Code quality is what developers value in a codebase.
And all the attributes that we value in a codebase can be distilled into one: how easy it is to change without negatively impacting the application quality.
And by easy to change, we mean that it is fast to make the change.
Code quality allows us to go faster, contradicting our perception of quality being expensive.
The reason code quality is perceived as expensive is because we are investing time on things that actually do not increase quality, that do not make us go faster.
Some of those things are:
- Beautiful code
- Design for no change
- Best practices
In my early years as a software developer, I read plenty of books and articles that made me believe that software development was like:
This perception, coupled with the fact that even for the simplest work we were treated as geniuses, gave us the license to behave like Michelangelo:
But software development is not an art, code does not need to read as a novel, and your codebase is not a zen garden.
I have spent far too much time on pointless debates, endless refactorings and polishing sessions for the sake of beautiful code. It was mentally very satisfying but it had little actual business value.
Maybe not as sexy, but more in line with what we actually do.
Remember that the most beautiful thing that you can do with any code is delete it.
Change is a constant in software development.
The common wisdom to cope with change is to add enough hooks, extension points and interfaces in our design so that when change comes, we do not need to change the existing code.
We cope with change by designing for no-change.
And how beautiful it is when a new requirement fits in the design and allows us to add new functionality without touching the existing code, without the fear of breaking existing functionality and with minimum effort.
This is the Open-Closed principle at its best.
And of course this is extremely useful and you will be crazy of not trying to design for no-change, but how do you arrive at this design?
One approach is to look at the future and make a bet on what the system will need. Unless your precognition skills are particularly good, this usually leads to over-engineering.
The other approach is to aim to be Captain Hindsight, so that when it is obvious what should have happened, we are in a position to make it happen.
What would you need to do in order to implement Captain Hindsight’s hindsight without the fear of breaking existing functionality?
Humans are notoriously good at finding patterns anywhere, even where there are none, and when coding we are no exception.
When we find such patterns in code, we are strongly compelled to codify them in an abstraction, which very often leads to the wrong abstraction.
And I could not explain better than Sandi Metz how expensive wrong abstractions are:
Duplication is far cheaper than the wrong abstraction
And worst of all, how beautiful we find are our own abstractions.
As a profession, we have managed to make a dogma of all the practices and processes that we have been discovering, transforming something that can be useful in some context, into something that must be universally applied, disregarding our context and failing to understand the context where the practice came from.
As I said in The tragedy of 100% code coverage:
Once a "good practice" becomes mainstream we seem to forget how it came to be, what its benefits are, and most importantly, what the cost of using it is.
Instead, we just mechanically apply it without too much thought, which usually means that we end up with at best mediocre results, losing most of the benefits but paying all (or even more) of the cost.
Because deep inside us, we still believe that none of the good practices makes us go faster. They slow us down for the sake of quality.
But application quality is different from code quality.
Code quality is not expensive, it is the only way to go fast.
It is up to you and your team to decide which practices to follow, which practices increase the quality of your code and systems, which practices make you go fast. And I am sorry but you will need to measure it.
And please, be open. Code and practices are just tools. Don’t be proud of a hammer.
Be proud of your job, but remember that your job is a client problem solved in the most efficient and effective way.