On Code Citizenship
Before I knew I wanted to become a coder, I thought I wanted to become a lawyer, or at the very least, a policy researcher. And before that, I spent far too many waking hours living a double life as a competitive debater. As a result, I spent a lot of time reading political science and sociology texts in university and grad school. I’ve been thinking a lot lately about the idea of what it means to be a good citizen, and how that applies to software development.
Katrina Owen spoke at Bath Ruby a few weeks ago about the challenges of working on legacy code. The second half of her talk, which was probably the twenty minutes of the entire conference that resonated the most with me, offered an explanation as to why developers might write Bad Code. What, exactly, is Bad Code? Bad Code is code that is shipped out with major flaws in terms of readability, scalability, maintability. When another developer has to add a new feature to existing Bad Code, the new developer must pay a toll of time and energy to decipher the code before getting to work. Bad Code is not synonymous with technical debt, although Bad Code is a very, very significant contributor to the accumulation of technical debt on a project.
The thing is, Bad Code isn’t necessarily written by Bad People. Good developers ship Bad Code when deadlines are too tight, when requirements are poorly defined, or perhaps even when they’ve inherited a code base that is so littered with Bad Code already. The decision to ship Bad Code, which takes less time to write, less time to refactor, and (probably) zero time to test, is the Nash Equilibrium of a Prisoner’s Dilemma situation. Straight from Katrina’s talk, here is a diagram to illustrate the available choices and consequences:
In every software development project, there are crossroads where a developer can either choose to write Good Code, which will be more energy-intensive, or to churn out some Bad Code and get on with the next feature. However, if you’re the only person on a project team who is taking time to write Good Code, your efforts will eventually be diluted by all of the tendrils of Bad Code. Combine Bad Code with Bad Design, Bad Communication, and Bad Requirements… at the end of the day, it seems it’s futile for a sole developer or pair to even bother with coding standards. Nobody wants to be that sucker. So, people have pushed, and will continue to push, Bad Code.
It’s not because they’re bad people. It’s because they’re only rational. On the individual level, it makes very much sense to choose the self-preserving path. However, on the group level, everybody has lost, because the net gain is greater when people collectively choose to cooperate, and write Good Code.
I’m from the state of New Jersey, which contains some very yuppie universities, some very orange shore-goers, and some very dangerous cities. In the 1970s, Newark had record levels of violent crime. Two sociologists, James Wilson and George Kelling set out to figure out why people in some neighborhoods felt safer than those in other neighborhoods, despite crime rates being comparable on paper. In the subjectively safer neighborhoods, police walked regular beats at night. The regular presence of police officers created the perception of civil order. On the other hand, in the absence of this perception, crime and disorder symbiotically fueled each other. I’ve oversimplified it a bit here, but this essentially is the Broken Window Theory: “If a window in a building is broken and is left unrepaired, all the rest of the windows will soon be broken.”
Rational decisions made from a desire for self-preservation and contextual evidence that Nobody Cares About The Code together form a chimeric recipe for disaster. As a junior developer who has only started working on Rails in the wild very recently, I have had the good fortune to not have encountered too much Bad Code, in the grand scheme of things. But, excepting the software craftsmanship houses out there that pride themselves on writing Good Code, Bad Code is pervasive. I don’t know how to make all the Bad Code disappear (I wish I did – my bank account could use a multi-million dollar consulting contract!), but as I think about Bad Code and why it exists, I also find myself thinking a lot about the role of citizenship in software development.
What is citizenship? Citizenship is the recognized status of belonging to some type of community. It is a set of two-way contractual principles that are usually implied. A citizen does not behave in ways that endanger the nation to which she belongs, and likewise, the state does not arbitrarily renege on its citizens. There are certain ways that the concept of citizenship is already present in the software development community. Open Source is probably the most concrete example: When you create an open source project for others to freely download, it is implicitly agreed that you will not secretly lace your software with malware or other harmful scripts.
Regardless of whether your project is open-source, there should be a constant expectation of good citizenship among all development teams. When you push Bad Code, you are directly saying to your colleagues that their time is less valuable than yours. It is exactly the same thing as leaving the communal office sink cluttered with dirty dishes. It doesn’t make you a bad person – it just means that on this occasion, you’ve been a bad citizen.
Good citizenship is, among other things, choosing to cooperate, even when you personally have more to gain from cheating. It means investing an extra half hour on writing good tests as documentation, to save your colleague one hour of untangling further down the line. It is assuming ownership of code on a project you’re working on, even if your name appears nowhere on the commit logs.
I have faith that good code citizenship is possible. Maybe it’s not, and maybe software development is just like everything else (in that case, I’ve made a very silly decision to give up law!). But the wonderful thing about coding is that we’re given new chances all the time. New projects, new language APIs, new frameworks, better testing tools. Occasionally, someone decides to update an extremely popular framework and make it backwards-incompatible. Software development gives us lots and lots of opportunities to start fresh. But even with the legacy code, it can feel monolithic oftentimes, but nothing is truly, permanently broken (except IE 6).
I hope that with each new start, more teams have discussions about shared responsibility for the code, and one by one, we get closer to a world where Good Developers are also Good Citizens. I leave you with this gem from Geek & Poke: