My fellow Elegant Coder Jan Van Ryswyck wrote about Craftmanship Over Crap. I generally agree with his point of view. Writing good, clean code is tremendously important for the long term survival chances of any software project. But i also believe that we sometimes need to be a bit more pragmatic about how we solve a problem.
A software developer’s primary goal should be to create value for the users of a system. Value can mean a lot of things here. First and foremost, it should be about things that users actually experience. Features, ease of use, performance, etc. You can get all of those with crappy code, but that leads to a situation where you won’t be able to sustain that value in the long term. A system can be very useful to its users, but if the code is in such bad shape that it can’t easily be maintained and extended with new features over time, the value of the system will slowly reduce. New features will introduce new bugs. Bug fixes will introduce new bugs. Eventually, the system starts to collapse under its own rot and the dreaded rewrite commences. Nobody really wants this, do they? If you write good, clean code from the beginning, you can usually avoid these problems.
A lot of opinionated developers claim that a true craftsman developer will never write a piece of bad code. In an ideal world, i would agree. These same developers also look down on people who implement a quick fix instead of looking for the proper solution. Again, in an ideal world i would agree with that. In the real world however, i’m not so sure if that’s always the best thing to do.
Those of you who’ve been reading my posts for a while know how important clean code is to me. I generally hate crappy code and i generally hate quick fixes that don’t fix the real problem. Then again, i do realize that we sometimes need to put our principles aside in order to be able to deliver value to our users.
Allow me to use an example to defend my position on this. I recently had to fix a nasty bug in NHibernate. Once i found the true reason for the bug’s existence i had two options to fix it. The correct solution would have required me to modify some crucial parts in NHibernate. The easiest fix required me to modify one non-essential class in a manner that couldn’t impact anything else.
Certainly, a true craftsman would go for the best solution, right? Well, let me tell you something about the NHibernate code base… it’s pretty big, it’s quite complex, and it’s been worked on by a lot of people over the years. There are parts of the code where you really don’t feel comfortable making drastic changes. Sure, we have our test-suite, but it unfortunately doesn’t cover every possible thing that could go wrong.
I chose to implement the easier fix. The bug was fixed, which means that the fix created value for everyone who’s using the trunk (which is more people than you’d think) and it means that this bug (which was a show-stopper IMO) won’t delay our 2.1 release. The correct fix would’ve meant making a risky change at this point, which could’ve led to more bugs that we don’t have tests for yet. The correct fix will be implemented right after the 2.1 release, when we have more room to make drastic changes to core parts of the code.
Was i wrong to implement the easier fix? I don’t think so… although i’m still not happy about that fix. But we have a responsibility to release software. If we would always stick to our principles, that could sometimes lead to having to delay or postpone a release. While there are many valid reasons for delaying or postponing a release, i don’t think merely sticking to your principles about a piece of code is always the best thing to do.
Of course, you do have to have the discipline to pay back the technical debt you’ve incurred when you do this, but sometimes the cost of that technical debt is less than that of having users losing faith in your product. And that, in general, is something we as software developers need to keep in mind as well. Writing good, clean code is great. We should all strive to do that. But we first and foremost need to create value for our users.
Absolutely. Though I’m sure a few purists would consider me an uncouth hacker, my first priority is delivering what the customer needs, when they need it.
I think one additional analogy to consider before we start thinking about the hippocratic oath of doctors: Take the following scenario. A patient has a serious infection in their foot. If antibiotics aren’t working the “best” solution would be to develop a better antibiotic, or maybe they know of a new experimental treatment that hasn’t been approved yet? And while it may look like a “hacky” solution to the problem, the only realistic & proven option available to save the patient’s life is to cut off the leg. A Dr’s oath is to do no harm but they don’t gamble with life.
96% (a laymans guess) of Software development is likely never to be life & death, but when it comes to delivering a product on-time at quality that you’re 75% happy with, or significantly late & 90% happy with? Sorry, I’ll choose 75% and live with it, but seek to improve that quality every chance I get. I strive to do no harm, but I do not gamble with other people’s commitments and money by insisting on unproven tools & technologies, or risk delaying deadlines with potentially risky re-factors. TDD helps, but it is by no means a safety net for big changes, tests are only as good as the developer that writes them. I am but 1 person working on a product used by thousands; I’m never going to think of every combination their devious minds come up with. I have minimum standards that I will fight to never drop below, and insist of with other developers I work with. Everything above that is icing on the cake that I propose to the people paying the bills to make the product better, but ultimately it’s their choice.
This is the Pragmatic Programmers “Broken Windows” analogy.
Sometimes, the immediate problem is that your tenants are freezing to death in the winter. It’s more important to board the hole than to fix it right. You _could_ live without replacing the glass, but you realise that it is a temporary fix and make an appointment with the glazier for later.
If you have an appropriate test in place to ensure that the bug does not reoccur, then you are free to refactor to the better solution once the immediate pressure has been relieved.
I believe it’s been said before, ‘Get it working, then get it right.’