Bad code tends to creep up on us over and over again. We’ve all been there. We kick off a project, things start a little slow, but quickly pick up with lots of features getting developed and delivered. We have an excited team and a happy customer.
However, soon things start to slow down. We dismiss the slow down as a specific problem that directly caused the team to under deliver. We confidently state that we’ve addressed the problem and we will be back to our old high productivity again. Only the team does not bounce back. We again attribute the slow down so some other specific problem. Again we confidently state that we’ve addressed the problem and we will be back to our old high productivity soon. But that does not happen as more and more problems keep popping up. We are now missing delivery dates, adding new features or simple changes are taking forever, bugs are increasing.
What was once the dream project that everyone wanted to work on is now the project that everyone wants to roll off of before disaster strikes. The team is frustrated and there is a lot of grumbling going on. Our once happy customer is getting more and more disappointed, angry. They are now kind of fuming and wondering – Where are my features? Why are we not delivering? Things are not going well here.
These are all signs of bad code. Uncle Bob defines bad code as code that has the following attributes:
This refers to code being difficult to change. Code is tightly coupled. One change causes a cascade of subsequent changes in dependent modules. This results in managers fearing to fix non-critical problems because they do not know the impact of one change or how long it will take. This is the all too common. “No changes to the code unless it’s a critical fix that we need to make,” and thus the code becomes rigid.
Fragility is closely related to rigidity, but refers to software that tends to break in many places every time it is changed, even in areas that are conceptually unrelated. As this increases, software becomes impossible to maintain because every fix introduces 5 new bugs and now we are playing a game of whack-a-bug. This leads to clients and managers distrusting the teams as they have lost all credibility in building and maintaining a quality product.
Immobility refers to the inability to reuse software from other parts of the system. If we need a module that is similar to another one we already have, when we try to re-use it, however, the module has too much baggage around it. It is hard and risky to separate the desirable part from the undesirable part so we decide that is it easier to rewrite and duplicate the code we need instead of reusing it. I hope we all know the problems duplication leads us to.
Viscosity is the resistance against making a change. From a design point of view, whenever we need to make a change we think it through and solve it the right way, or we can find a quick and dirty hack. The environment we work in sometimes pushes us towards the hacking approach. If compile time takes too long, instead of applying a change where it belongs, we start thinking of a place to make the change that does not require a full recompile. If check-in takes a long time, we start thinking of ways to make changes that require touching the fewest files. If deployment is a hassle, we start making changes in the database instead of the code because running a database script might be easier and safer than re-deploying the entire application. So instead of making a change in the appropriate place, we end up making the change in the place that provides the least resistance.
In my next post, I’ll discuss the reasons we get in these situations and ways to solve them.
The Top 4 Symptoms of Bad Code is the first in a seven-part series on Technical Debt from Excella Software Development Lead Fadi Stephan.
Part 1: Top 4 Symptoms of Bad Code
Part 2: Top 5 Reasons Writing Bad Code Happens
Part 3: What is Technical Debt?
Part 4: Good Technical Debt vs. Bad Technical Debt
Part 5: The Vicious Cycle of Technical Debt