I recently re-read this great article by Martin Fowler about Technical Debt. I feel shy about having a partially dissenting opinion but I’m taking courage and I will blog about it.
The part that caught my attention is the following sentence:
If I have a terrible area of the code base, one that’s a nightmare to change, it’s not a problem if I don’t have to modify it… So crufty but stable areas of code can be left alone.
I guess that different experiences with different code bases could lead to different conclusions about it. Mine is substantially different. Here is what I experienced with old messy pieces of code.
I worked in environments with a certain amount of resistance to change in some areas. Applications life expectancy easily exceeded 10 years and many hands (and hopefully many minds) worked on the codebase while the years passed by.
My experience in such environments is of applications with average quality but occasional peaks of really messy code that noone dared to even look at for years.
While thinking about it, today, I identified the following 4 points:
Understandability and Unpredictability – We’re losing time
We kept losing time, over and over again, trying to understand what that area of the code was doing and what was supposed to do originally. And we had to do it! To be sure that touching other parts of the codebase would not affect that one.
This is a hidden cost in my opinion! And a noticeable one.
It was not only that. It was also true that not all the developers could figure it out in the timeframe available. Business rules changed over time and the code did not reflect any more neatly the operational needs, if ever it did.
At that point in time we needed deep knowledge of the business requirements’ evolution over time and of codebase’s evolution as well. Precisely the two things we missed.
In such a situation all you have is:
- Codebase hard to reverse engineer and the authors run away aeons before
- Knowledge loss because of turnover (you’re left with distributed partial knowledge with uncertain reliability)
- Unpredictable side effects due to layers of code built around a “stable and working messy piece of code”: write it now, it will break somewhere, somehow in the future
About the last point: rarely a cog of an engine works in isolation. Any choice has consequences: if you have an untouchable piece of code, the codebase will evolve around it, influenced by its existence.
Is it Core? – We’re taking a big risk!
Maybe I’ve been really unlucky but the old messy pieces of code I stumbled upon, were…core areas of the application!!
I think it was not by chance:
- a messy unimportant piece of code is one that more people are eager to fix;
- a messy important piece of code has, let’s say, less chances to find brave developers.
When I say core I’m referring to any piece of code containing algorithms from which depends the good working of a part of the application, let’s say a functionality.
If I care about that functionality, when is the last responsible moment for deciding to remove the risk of not being able to modify it?
In my opinion the stake here is: will the company be able to stand the worst case scenario in the future? (Having the application broken for a long time? Not being able to deliver a functionality? Giving up new opportunities? Messing up customer data?)
If you owned a company, would you leave this decision to an application owner that could work elsewhere in the future?
Considering a piece of code messy but not in need to be touched is somehow a self-fulfilling prophecy.
Two factors play a role here as far as I saw:
- Developers get used to find workarounds and to build cruft somewhere else in order to leave the old messy piece of code untouched (and usually they are not aware that they are doing it)
- Users get used to workaround their issues in other ways and to not even express their needs once they realize the risks inherent with development.
The complex human system interacting with that piece of code learns and adapt to the constraints it imposes. The range of acceptable behaviors is influenced. And the prophecy is even more easy to become true when you start rejecting requirements because they are ‘unfeasible’.
Hail the Business Case
The older and unknown a piece of code becomes, the less probable is that a business case will ever consider worth to rewrite it.
Usually the best gain resides in giving it up with fulfilling the need and drop the requirements.
Expecting that, when we need to touch that piece of code, we will start thinking about it…well, in my experience, it simply does not happen.
What I saw happening is that some developer decides to take the risk to have all the blame put upon him and with courage and bravery do one of the following:
- either starts rewriting the old messy piece of code secretly piece by piece in a parallel effort with no feedback
- or he starts advocating a rewriting project for months or years
…but this is a story for a future post.
Conclusion – We were bad developers after all
We could argue that I described a situation that smart developers never find involved in. If you hire only smart, brave developers, they will always be able to reverse engineer any piece of code. They will always be in control of their application and will always find a way to not add additional debt elsewhere…but if this were the case…how would that old messy pieces of code ever appear on earth?
In a following post, I expand on an article by Henrik Kniberg that I think is the best about lifetime of technical debt.