Good Code, Bad Code
A couple of years after I started working as a software developer, I was surfing the Dunning-Kruger wave pretty hard. I was cocky, arrogant. I knew how good code looked like, and I was writing good code every day. Or, so I thought. And as every wave, mine eventually crashed.
The day it crashed, I was reviewing a merge request with a colleague. We had a file open, and I spotted a nasty piece of code. This piece of code was unrelated to the merge request, but it rubbed me–the seasoned professional that I was–the wrong way. A
git blame later, the idiot responsible for this atrocious mess was revealed: It was me.
I went home that day, feeling deflated. The worst part about this wasn't my colleague's laughter after I had so grandiosely trash-talked my own code; it was the realization I had written that code when I was already thinking of myself as a fantastic developer.
I realized a few things over the next couple of days. First, I thought my intrinsic definition of good code was fixed; a clearly defined set of rules. It wasn't. It was fluid and changed over time. With every project, every pair programming session, every code review, I unknowingly updated my convictions.
Second, I thought every measure for good code converges to a final, undeniable, well-defined point. It didn't. On some topics, I changed back and forth. Sometimes within a couple of weeks. When I talked about my idea of good naming conventions, I presented my opinion as an undeniable fact. After a couple of months, I changed my opinion. Luckily, a juicy dose of cognitive dissonance allowed me to still look in the mirror after fighting for this new opinion as vehemently as for the old one.
Since then, I've become more reserved and contemplative in discussions about good code. I still have strong opinions. I'm just much warier of them. I don't share them too readily anymore. And, above all, I don't use them to belittle other people's code.
Another outcome of this experience was that I had to redefine the assumptions on which I judge code. Clearly, I was fundamentally mistaken on a few things and in dire need of a more sound (and fair) worldview. What I was left with are only two measurements I use nowadays to judge code quickly:
- Does it work?
- Is it thoroughly tested?
That's it. Don't mistake this for some kind of reductionistic, holier-than-thou transformation to the next level of minimalist arrogance. I'm not saying there aren't any objective measures of good code. I'm just saying they might not be as objective as we believe them to be.
Most discussions about good code conflate personal preference with absolute truth. Everyone considers themselves an above-average developer, so mistaking one's personal style with good style is a common problem to which we are mostly oblivious. Once we understand that our personal style is subject to change, we realize that a snapshot of our assumptions is hardly a fair measure of other people's code.
Or, we read about a new way of doing things, intuitively like it, but then apply it outside of the context it was meant to be applied in. We become staunch supporters of some syntactical or architectural flavor not because it is objectively the best way but because we are intrigued by it, and how could we be wrong? We praise the fork as the one true utensil, even for people who want to eat soup.
It's so easy to become an arrogant asshole when we are too sure of ourselves. It sure was easy for me. What's harder is to be fair, kind, and mindful when discussing code, personal preferences, and "best" practices. Discussing how to improve our code is a fundamental part of our craft; we should just be humbled by the fact that good code vs. bad code is not binary, but a spectrum.
In the end, what constitutes good code is subjective, context-dependent, and volatile. In fact, I find it so volatile that I'm only left with a potentially incomplete yet fairly actionable definition of its opposite: Bad code doesn't work and isn't tested.