Code Necrosis

Federico Alvarez
Ordergroove Engineering
7 min readApr 30, 2021

I must warn you that as your code base can be healthy and it can also suffer from illnesses. The one I’m going to cover in this post is “Code Necrosis” aka the “Dead Code Disease” and it’s one of the most common cases of tech-debt.

Causes

Version 1 amnesia

Sometimes it’s tech driven, other times it’s product but we’ve all been in that position where a new version of feature X is required. This new solution it’s going to be THE solution, the best and only in the future.

You’ve gotta start somewhere and you cannot break the existing product until the new version is ready, so you just create the v2 directory. You’re a great engineer so you finish v2 in time and cautiously roll it out to all your clients seamlessly. You feel great, you’ve accomplished your goal! Congrats!

New exciting project comes to your plate and you move on. Without you noticing, you left behind that dead v1 version in your code base…

Extra features overdose

One of the aspects that makes engineers great at what we do is that we always tend to think of all the possible cases and future improvements for the feature we’re working on, it’s in our essence. Sometimes we consider that’s a good idea to tackle them from the get go because “when X is required, it would be much easier” or “the customer will be able to customize feature Y to do something specific for Z use case”.

Time goes by and those blocks of code are still sitting there waiting to be executed…

Zombie features

In the lifetime of every platform there’re features or sub-products that are put into sleep because the market changes, the user needs changes, etc. Turning off a feature is really easy, you can just remove it from the UI and voila, not available anymore… But… The code supporting that functionality is still pretty much alive in the backend, and even sometimes, still in the frontend code base.

Symptoms

Debugging headaches

Engineers that have been working in the same code base for a long time have trained their brains to ignore this symptom but it pops to the eyes of new ones the very first time they have to apply backwards engineering to trace a bug.

Imagine that your monitoring system sends an alert to your support channel saying that the error percentage of an app has gone above the threshold, the engineer in rotation would go to see the error messages coming from an exception raised in “FunkyClass”. What if “FunkyClass” also exists in the original v1 version of the module? It’s still there, technically, it can still be called by other parts of the platform, so when reading the code you must not treat it as deprecated but as another branch of execution.

Trust me that at 3 am when your phone rings with the alert, even if you’re the expert that wrote the “FunkyClass” v2, you’d not know where to look at.

Refactoring nightmares

Refactoring is always a complex task, it deserves it’s own post and there are even books dedicated to this process. The fact of having multiple versions sharing the same code or even worse, having duplicates doesn’t make it anyway easier.

Let me use a personal experience to illustrate the idea. We were working on a project to refactor the way the system rendered some templates, we already coded the new renderer and it was time to start replacing the old one, it was supposed to be an easy task considering that the output shouldn’t change. We ran a grep command to list out all the places we needed to touch and to our surprise the same block of code was duplicated on v1 and v2.

Knowing that the old version wasn’t used anymore, what do you do there? Would you do that extra mile and update v1 as well?

New features lethargy

This one is quite evident, dead code eventually leads to spaghetti code making the development of new features harder and harder over time. Engineers with hacker mentality will always find clever workarounds to inject the minimum amount of code necessary enough to hook in the new functionality. While this is great for the business, it’s not healthy for your code base.

Onboarding complexity

The onboarding process is also harder when your code has death traps that are only known by senior engineers. This will not only affect new hires but also those devs that have to work on a part of the platform they’re not familiar with and need to first learn the basics.

Aging tests

It’s obvious to say that you should always write tests for your stuff, test the hell out of it. We all agree on that, right? (I hope…)

So, if your code base can have dead pieces, your test suite will have them as well, that’s a direct relationship. In fact, you could even have more dead tests than dead code.

One could argue that those tests don’t hurt that much, but you should treat your tests as you treat your code, you should apply the same principles. Also, those tests are being run every time you deploy and that’s a waste of time and resources affecting your team’s productivity.

Treatments

Rely on your version control system

Deleting code is one of the most satisfying tasks an engineer can experience, but why do we do it so rarely? It’s not that you’re throwing it away, if you ever need that code you can always go back in history and bring it back again.

It can be argued that new devs will never know what usable removed code is in the version control, so they could just write it again, their own version with possible new bugs instead. But the same thing could happen when dead code is hidden somewhere else and they weren’t aware as well.

To keep your version history clean, you should separate your cleaning commits from those adding new features. Following this pattern will not only result in a tidier and readable version history, but also it will make it much easier to revive old code.

YAGNI

“You Aren’t Gonna Need It” is a mantra from Extreme Programming that’s often used in agile software teams. It feels like common sense to not write code until you really need to but sometimes we need to make an effort to not write those extra lines, even when we think we need this hypothetical feature, it’s likely that we’re wrong.

The reason why we usually write those extra features is because we think it will be cheaper to build now rather than later. But we don’t consider the cost of refactoring we would incur if the feature we build now is not what’s expected at that time, you cannot foresee now the requirements of the future.

DRY new features

Again another mantra that we should always keep close: “Don’t Repeat Yourself”, in The Pragmatic Programmer DRY is detained as: “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”. Piece of cake!

For example, in the use case I explained above where we had v1 and v2 duplicating code, it would have been easy to call out in the pull request that the engineer was duplicating lines of code, but it’s harder and takes more time to think about the intent of v2 and how it should be developed without duplicating the knowledge or expressing the same thing in a totally different way.

Green grass

In my first years I was told many times that “you should always leave the grass greener than when you arrived”, and in my opinion it’s a great analogy. Maybe you don’t have enough time to refactor an entire module but you should at least leave it a bit cleaner than it was before. This iterative process will improve the quality of your code base eventually.

From my experience, senior engineers should mentor others with this motto because when you start it’s not as clear, and sometimes scary, to touch code beyond the feature you’re implementing and “grown up’s” help is vital in such process.

Conclusion

It’s worth mentioning that it’s not an engineering problem only, there needs to be alignment with management and product folks. Of course the business comes first and features need to go out ASAP, but all of us should be conscious about dead code when planning new projects and try to include a clean up iteration. The benefits are not clear in the short term, but if you measure the time you’ll save in the future, the mid/long term cost analysis will impress you.

Deprecation timelines are also useful to keep your clients on the latest and greatest so your engineers are more confident at the time of deleting old code. Client services skills are crucial here to convince them to move from deprecated versions/features and feel that’s best for all parts.

Dead code happens and will still happen even in the cleanest code bases, and that’s fine, it’s not a measure of product quality or the skills of the people involved. We just need to keep our eyes open and try to catch these cases as soon as possible to prevent real damage to the platform that you’ll regret in the future.

Wash your hands, wear a mask and delete code :)

--

--