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.
Application Quality vs Code Quality
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.
Why code quality is perceived as 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:
Image attribution: Uri Tours (uritours.com) / CC BY-SA.
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.
And as much as the romantic vision of craftsmanship is very appealing, I wish that the craftsmanship books’ covers showed the kind of craftsman that I now identify the most with:
Image attribution: Pintor de brocha gorda by https://oficiossite.wordpress.com
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.
Design for no change
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.
We do not believe in clean code
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.
Top comments (25)
In a more serious tone. I think we always have to trade off quality of code vs quality of app + speed of delivery.
One month you release something, other month you fix stuff and organize your code, refactor here and there.
We should care about best practices and patterns and wonderful stuff but what really matters is that we are able to deliver (not necessarily on time), be able to do future changes without being a PITA AND be able to work in a project we care and feel proud of because of the previous reasons.
If you deliver but the codebase is always a mess, one day or another you'll be exhausted and frustrated.
If you do not deliver but your codebase is a wonderland, what's the value if no one is using it?
It's better to deliver and enjoy. It might be a mess today but if you can fix it tomorrow, it'll be great.
As somebody else summarised: "clean code is not that clean".
BTW, I loved your "Pintor de brocha gorda" image 🤣
A good manager should be able to either provide technical guidance or at least ensure that it's coming from somewhere.
I've seen teams complain that management rushed them. So management stopped rushing them. The quality of the code didn't change at all. Not even a little bit. The developers had no idea what to change, and neither did management.
From my experience teams achieve better results when they have complete responsibility over the piece which they produce, from gathering requirements to post-go-live maintenance, an can make decisions on their own. With all respect for managers, very often I saw ones who do more harm than good for the projects through micromanagement. Team also work better knowing the long-term vision and plan and don't jump from project to project every quarter but have enough time to learn the project. I know companies which work in silos where no team really owns the product, one team works on features, other work on bugfixing yet another do deployment. When work is spread like that no one really feels the impact of doing good work from the very beginning till the end and no one feels responsible for failures. In most cases like that I saw a poorly designed solution, discouraged employees and high turnover. Personally I prefer to work in small self-organizing independent teams but this works when at least a few members have experience, I definitely would not risk it with all junior devs in a team, even though I saw some excellent junior teams in action, but general experience is that juniors have too much fantasy to experiment with technologies and tools ;) while seniors tend to find a good balance and prefer battle tested solutions.
Wow! Awesome awesome write-up! I was hoping to write something like this in some time, but as I began to read your remarks, I was saying to myself, "this sounds like some ideas from Sandi Metz"! And bam! You hit me with a remark by her. You pretty much nailed it! It brings to light that sometimes the overuse of DRY paints us into a corner where we don't yet know the right abstraction. Man, it'd be cool to work with someone like you. And funnily enough, you write Clojure, so I take it that you're also a fan of Rich Hickey's talks, like "Simple Made Easy" :D
Rich has been a huge influence!
Quality writing. Thanks for sharing.
Typo to fix Dan: Highsight -> Hindsight :)
Well said though!
Same "typo" three times.
Thanks for the correction!
Too true, and very well written.
I've made some similar points before...
Clean, DRY, SOLID Spaghetti
Jason C. McDonald ・ Jul 24 '18 ・ 9 min read
I had more than a laugh readying it. Very much in agreement with all of it!
Thanks for sharing!
Great post! I really like the closing sentence.
Likely outside of the scope of this article, but I've begun to find "the most effective" way, for me, being guided by Domain-Driven Design for anything "non-trivial". Have you used it or other patterns to help guide you?
I have the feeling that my last 15 years of career, I have mostly written "trivial" integrations, and for this I have found youtube.com/watch?v=iLMiPhYVG8w to be the most effective.
Nice sentence! A painter is not proud of his brush; he/she is proud of the art they create with it.
"Why" we follow best practices is an excellent point. We should always be reflecting on whether a specific technique or process is useful for us.
I personally don't follow best practices or read up on code design principles or whatnot. I won't remember them, and most of the time I find that I already practice them even if I can't put a name on it. If you ask me what SOLID or DRY means I likely will stumble.
For example, only a few years into my coding did someone look at my code and say "hey that's some nice dependency inversion" and I had no idea what he was talking about until he explained the concept and I was like "oh, is that what people call it". To me it was just something I thought of after getting bitten by new feature requests time and time again and then it just sort of worked out even if it was rough and didn't follow "standard practice" that people might have learned from somewhere.
Most of the time I'm just coding to spec and trying to push out a prototype in matter of minutes or hours so that I can get a greenlight from the client, and any abstractions and other design considerations are based on my own experience of whether it's useful or not. Now, I sometimes do read up on patterns and best practices because I think I have enough experience to be able to say whether it's something I want to adopt.
We should stop saying “best practices”. They are just practices, that is some contexts they are the best to get the job done. In other contexts, they are not.
Always reminded of this image whenever threads like this come up...
Great post 👏
Managers should be coders. I don't believe there are exceptions to that rule, at least I've never seen a good exception.
I thinks that works up to some team size.
But a manager’s job is different than a developer, so I would not expect a manager to be the best developer, as she should be learning management skills. That is why “good managers ... also know their limits and wisely stay away from the code.”
You should come here, compare the project made by the guy who understood clean code and the one who doesn't... my legs shiver when I have to change the second one.