Writing software is hard.
Often, several forces are at play when we write software: the constraints of time, cost, and quality push and pull against the needs of users, the complexities of security, and the effects of other software. There’s no magic bullet that will solve for the optimal balance point between these forces: it is our responsibility as professionals to apply our judgement to this particular knotty problem.
I’ve been contemplating this fairly recently; in particular, a conversation in our local community Slack about a particular situation between a dev and a former CEO made me reflect on whether quality can be sacrificed, particularly in the short term, to optimize the other variables, namely user features and time. Do you allow the quality of code to slip in a sprint or release to slip, in return for getting a feature shipped to users early?
In that particular situation, one of the developers on the team had shipped code of questionable quality, in return for getting a feature out the door in time. The CEO, weeks later, had questioned the inclusion of code of such quality. One of the other developers pointed out that this was the first time that developer had shipped subpar quality code, and that there was probably good reason to do so: to get a feature out in time; give him the benefit of doubt.
Of course, I’m glossing over several other details shared to us in the Slack, but the gist is there: is code quality something that should never be compromised?
Earlier in my career, I was a little more dogmatic about code quality, especially as I gained more responsibility. In the trenches, I was only concerned with my code; as I got higher up the stack, so to speak, I saw the need to keep standards.
But I also learned that being dogmatic about anything is generally a bad idea. Sure, maybe Standards Must Be Met — however, I also think not all battles are worth fighting.
The edges are never ever well-defined in the messiness of the real world — and we in the business of writing software should know that. Our code is the way we interface and describe the mess that is the Real World to the austere and precise universe of math and computing, and it’s inevitable that the disorder and chaos should be reflected in one way or another in our code.
It is definitely our responsibility to tame that beast, and maybe an ideal state does exist for our code where those forces are well-balanced. Time is, however, a harsh mistress (to borrow a phrase). We don’t have the luxury of time to search the entire solution space for the ideal; we need to seek trade-offs and compromise, inevitably.
When we are told that there are no valid reasons for not doing TDD, that they are mere excuses, that is doing a disservice to our profession: we need to take a look at our practices in the harsh light of day. There are no panaceas in my book, no silver bullets. There are always trade-offs. Double-entry bookkeeping and the practices surrounding it certainly solved a lot of problems, but it did not make single-entry bookkeeping completely obsolete — there are places where single-entry bookkeeping is a fit.
We make compromises every day, because we know what we gain and what we lose. So why shouldn’t we ask the same questions when we write code? I for one advocate TDD, but I ask the question: is it appropriate? Is it the right tool for this job? In most cases, yes it is. But to use it and push it dogmatically is, I think, incorrect. We must understand why we’re using it, and what we gain, and what we lose.
In the end, I fall back to what I’ve said earlier: communication is key: we must communicate with our stakeholders. And dogma is antithetical to communication.