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 then things quickly pick up and a lot of features are 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 to 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 selves and high productivity. Only the team does not bounce back. We again attribute the slow down to some other specific problem. And 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 regularly missing delivery dates; Adding new features or making simple changes take forever; Bugs are increasing; Morale is low. What was once the dream project that everyone wanted to work on is now the dreaded 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, and is 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 technical debt and bad code. Uncle Bob1 defines bad code as code that has the following attributes:
Rigidity – 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 – 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 the teams have lost all credibility in building and maintaining a quality product.
Immobility – Immobility refers to the inability to reuse software from other parts of the system. We might need a module that is similar to another one we already have, so 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. And duplication of course leads to problems of it’s own.
Viscosity – 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 ourselves into technical debt and ways to solve them.