I have been thinking recently about an aspect of software development which I feel can often be overlooked. The catalyst that triggered the thoughts in question was a post to Erlang mailing list from Joe Armstrong (the author of the Elang programming language). The email is titled with the subject ‘Why do we need modules at all?’. Joe’s post starts with a bold opening statement:
I’m proposing a slightly different way of programming here The basic idea is
- do away with modules
- all functions have unique distinct names
- all functions have (lots of) meta data
- all functions go into a global (searchable) Key-value database …
On it’s surface this completely flys in the face of an idea that as developers we get told is this axiomatic truth; global variables are bad. Although Joe’s post wasn’t really about code quality, it did get me thinking about it. We often have these ideals that we hold dear and hold others too in social rituals such as code reviews and meetup talks.
Yet for a profession that is steeped in correctness, exactitudes and objectivity, there are many elements of software development that are an aesthetic or cognitive preference. The classic case of this being tabs vs spaces. Indeed, it feels the growth and adoption of linters/formatters across languages and projects is not so much a pursuit to make a codebase more ‘correct’ but to sideline aesthetic and preferential ideals.
The longer I write code it seems to be more apparent that writing code is a near endless series of trade offs. The binary of ‘good’ and ‘bad’ code seems to break down when taking into account the writers constraints and the near limitless characteristics you could optimize for. To pick a few common ideals:
Not to mention that the meaning of these words is somewhat subjective, differ from (programming) language to language and are also transient. Is it possible to have code that is uniformly agreed to be ‘testable’ for example? Of course some of these characteristics are arguably more quantifiable or objective than others (e.g. performance), but even for these there will always be questions of which metrics and which results are acceptable.
So what does that mean in practice? I think it means accepting that no code will ever be ‘perfect’, that coding is a series of trade-offs, and that we need to decide what qualities we want to optimize for. If you’re writing a quick script at the weekend to rename some files, would you optimize for modularity and testability? Perhaps not. However, if you were writing production code in a team, the answer might be a little different. Maybe, as Joe did, sometimes it’s healthy to question beliefs that we hold strongly and ask if they always hold true (or if they are true at all). For example, is it always necessary to be DRY? Are their times it’s okay for things to be tightly coupled? What if we didn’t have 100% test coverage?
Do I think that the strive for code quality is redundant? Absolutely not. However, as I have grown as a developer it feels a fair portion of the things I was taught as universal truths about ‘good’ and ‘bad’ code seem to actually be socially reinforced norms. From a personal standpoint, I think it’s important to strive towards having a team outlook and agreement on what the meaning of ‘good’ code is. Taking this a step further maybe this means having it written down in a guide or even codified in linters/tool configs where possible. Here it feels that being empathetic and open-minded to others input are key to being successful in software, as the understandings of what constitutes good code differ from person to person. Perhaps the goal is treating coding collaboratively as an opportunity to learn from others and teach them, and less as an opportunity to assert an ideal of perfect code.