They say that in some disciplines consistency matters more than talent in the long run. I think that in software development, consistency is a virtue.
As a project moves forward, it’s normal for the design to change (preferably in small ways), and at the same time the quality of the code can go up and down.
Having little knowledge of the problem, getting the approach to the solution wrong and many other factors can lead us to a very common situation: we’re working on a user story and we run into a part of our code that’s hard to understand, poorly approached, hard to extend; in general, it’s keeping us from moving smoothly toward our goal, delivering value.
Kicking the can down the road
One attempted solution I’ve seen often is “kicking the can down the road”: hacking something together to get the story out as soon as possible and then, every so often, sneaking a technical story into the sprint to pay off technical debt, and done. Ron Jeffries describes this problem really well in Refactoring not on the backlog
I won’t deny that sometimes it’s a perfectly valid solution in circumstances where meeting a deadline really matters. The problem I see is when it becomes the norm. If your project constantly needs you to kick the can down the road, maybe you should rethink the dates, the scope and/or the way you’re approaching the development of your software.
The boy scout rule
Another option is to make sure that with every user story we deliver and every bug we fix, the code is better than we found it. Uncle Bob calls it applying the “boy scout rule”, Fowler calls it opportunistic refactoring, but in essence they’re saying the same thing.
One of the big advantages of applying this rule is that the areas that get touched the most get the most care, which helps us avoid ending up with a big ball of mud and an unmaintainable mess.
Some might think “but then you’re leaving parts of your software without ‘cleaning’”. Indeed, but they’re parts that aren’t causing problems (they have no bugs) and that we don’t need to touch (there are no user stories), so, for now, there’s no need to spend a single minute on them!
Little by little
No matter what state our software is in, if we consistently end our day with it a bit better than when we started, in the long run we’ll see that our flow of value delivery is much, much better than before.
There’s no need to take three times longer on a user story because we embark on a refactor of monstrous proportions; leaving it a little better is enough.
The time excuse
I often hear the excuse “we just don’t have time for that, we have to deliver the user stories as soon as possible”. That statement is a double-edged sword, because if we don’t give our code the attention it needs, the time it takes us to finish user stories will keep growing and the number of bugs will very likely go up.
Sticking to a simple solution
Resorting to overly complex architectures and designs tends to create rigid software that loses room to maneuver in the future. Let’s not forget that it’s precisely in the future when we’ll know more about the problem and, therefore, be better qualified to offer better solutions. Anchoring ourselves to straitjacket architectures from the start can feel very “enterprise”, but it becomes a problem down the road.
Along these lines, I recommend reading Understanding the Four Rules of Simple Design.
The cost of undoing
We often only take into account the cost of doing, but it’s very important to also take into account the cost of undoing.
Using third-party code isn’t bad; it can save us lots of hours of work on things that probably have nothing to do with our business. But when we do it, we have to choose very carefully how we do it.
This is one of the reasons people often talk about preferring libraries over frameworks. The cost of undoing with a library can be relatively low, especially if we’ve also created the necessary abstractions. When we choose to couple ourselves to a framework, the cost of undoing is higher.
Of course, it’s a matter of balance, and there are many times when the price of using a framework is completely worth it.
Although it’s not exactly about this topic, I really like Greg Young’s talk “8 Lines of Code”, where he talks about the cost of tying ourselves to magic solutions.
Conclusion
We should always try to make our code a little bit better. The only way to make our code better is to get better ourselves. If we notice that we make bad decisions too often, that we have to undo a lot of what we do, that we can’t reach a proper delivery flow, maybe we should consider investing a bit of time in improving. There are tons of resources that can help us develop better: communities and community events, online and in-person courses, insanely good blogs… what we don’t have is an excuse.