With Halloween nearly upon us, it seems appropriate to discuss a widespread problem in software development: zombie code. Nearly every codebase I work with is littered with small chunks or large swaths of commented out code. This is zombie Code.
//Turning this feature off for now. Jimmy was clearly drinking when he wrote this.
//Imagine horrible crimes against code here...but at least it's commented out...
So why call it zombie code? Well, zombies aren’t really dead. As horror movies have taught us, though zombies appear to be dead, they’re still alive enough to haunt us. In the same way, zombie code straddles the line between alive and dead…just waiting for a chance to ruin your day. Commented out code is alive because it’s in the current codebase. Programmers interact with it during maintenance and refactoring, often by simply scrolling quickly past or stumbling across it in a keyword search. But the code is also dead because it’s not executed in production. Thus, it’s a zombie that should be buried, pronto.
Today’s Code Never Really Dies
I propose there are two root causes for the ongoing scourge of zombie code: Laziness and risk aversion. Lazy developers get attached to code. They lack the strength of conviction and sense of purpose required to delete unnecessary code, so they hoard it in comments instead where it can live to haunt another day. Code must be deleted regularly because great developers know that code is a liability. Less is more. And yes, commented out code is still code.
Lazy developers may argue that they comment out code “just in case” it might be useful to someone later. This does us all a disservice. It speaks to risk aversion and a lack of appreciation for the benefits of source control. With source control, deleted code never really dies. It’s merely buried alive. Thus, commenting out code is weak sauce.
Commented out code is just as useless to the application as deleted code. Straddling the fence creates technical debt in the form of zombie code that will haunt your team later. Be decisive. Delete it.
Improved Signal To Noise
When writing code, we must strive to keep our signal to noise as high as possible. This aids in comprehension, speeds reading, and helps protect us from creating buggy code due to misunderstanding. Zombie code is directly opposed to comprehension. It slows reading and maintenance because less actual production code is on the screen at any given time. It’s visual noise because it’s unclear if one should read it at all. For some reason we often accept this compromise as developers, but we’d never accept such sloppiness in the real world. Imagine if the New York Times looked like this.
Notice how reading the text doesn’t flow? The increased noise hurts comprehension. And it’s difficult to ignore the commented section even though it’s likely irrelevant, or worse misleading and incorrect. One could argue that source code isn’t the finished product, so a comparison to a finished publication is apples and oranges. But we must remember that every line of code written will be read an average of ten times. So yes, our readership is smaller than the Times, but it’s an important readership with a loyal following. It’s us. Knuth sums up this concern perfectly.
“Programming is the art of telling another human what one wants the computer to do.” Donald Knuth
Zombie code makes the story unclear. Should a programmer spend time reading the commented out code or not?
Ambiguity Hinders Debugging
Commented code creates ambiguity about whether the code should have been commented at all. Imagine you’re a maintenance programmer who stumbles across a swath of zombie code around an area where a bug has been reported. The programmer’s job is now much harder. The commented code must be read and comprehended to determine its potential impact. Was the code accidentally commented out for testing and never reverted? Perhaps the person who commented it out can be of help. Who was that? An investigation ensues. This additional ambiguity takes time to resolve and adds mental weight to what could otherwise be a simple debugging process.
Keyword Search Optimization
In larger code bases, grep/find in files can be a lifesaver for hunting down specific pieces of code. However, if the code base is littered with zombie code, chances are many hits will contain commented code. It’s just noise. And wasted time.
Simpler Refactoring
Refactoring is good for the soul. We should be honoring the boy scout rule and regularly leaving the code a little better than we found it. However, when a class or method contains a chunk of zombie code, things get tricky. If I refactor this section, do I need to consider this commented out code? Will it be turned back on soon? How will it interact with my new implementation? These are questions maintenance programmers shouldn’t have to ask.
Furthermore, integrated refactoring tools won’t make corresponding changes to commented out code at all. Thus, as methods, variables, and classes are renamed and signatures are changed, the commented code falls behind. When commented out code is resurrected, it’s highly likely the app won’t even compile.
Any Exceptions?
Nope. See that was easy. And definitive. One could argue “I’m commenting this section out for now because I plan to uncomment it again soon.” Okay, say you’re house-sitting. You walk in the living room and see this:
Imagine your inner dialogue. It’s a nice house, but that’s ugly and odd. I need to turn on the light, but what’s with the tape? What would happen if I removed the tape and turned it on? You’ll likely decide to call the owner. “Oh, I put in a ceiling fan but it wobbles and crashes to the floor when I turn it on. I’ll fix it…at some point.” Yeah, sure you will. Until then, the bizarre taped switch remains. We don’t accept disabled half-baked features in our homes, why should we do so in the code we support?
To clarify, commented out code is zombie code that should deleted, regardless of quality. Code is either in production, or it’s not. Zombie code sits at some scary point in between. If code is commented out, then it’s likely not done. Often a configuration switch or logic fork is needed so the code is only exercised when appropriate. Delete the code and open a ticket to assure the necessary work is completed. In that ticket, include a reference to the commit where the code was deleted. Alternatively, move the work to a dedicated branch where it should stay until fully fleshed out. In the meantime, maintenance work will not be impeded by the ambiguity.
A Mental Checklist
If you’re about to comment out code, ask yourself:
- When, if ever, would this be uncommented?
- Can I delete this and simply get it from source control later if necessary?
- Is this incomplete work that should be rolled back and worked via a branch?
- Is this a feature that should be enabled/disabled via configuration?
- Did I refactor out the need for this code?
Let’s make this the first annual Halloween zombie code hunt.
Interested in learning more about clean coding principles? Check out my new Pluralsight Course, Clean Code: Writing Code for Humans.
- I removed the paragraph on compilation times/page loads. The points were valid. Many seemed to misunderstand it as a broader mandate to remove all comments which wasn’t intended. It was a distraction from the stronger core points above.
- I am all for commenting code where it makes sense. This post is about the problems caused by lazily commenting out buggy, incomplete, or no longer relevant source code rather than doing the right thing. Comments are helpful. Commented out source code is not.
- Example code is not zombie code. It’s inline documentation. But yes, unit testing/external documentation are arguably preferable alternatives.
Great post!
Until today, when I came across commented code I just thought “Don’t this people know how to use source control?”.
Now I know I should think much worse about them, they are actually doing serious damage around!
You make some good points, but you never really address any valid reasons for commenting out code. Sometimes, one does want to comment out a section of code and possibly uncomment it later. You even hint at this possibility:
“When commented out code is resurrected, it’s highly likely the app won’t even compile.”
Do you have any data or anecdotal evidence to suggest that commented out code does not get uncommented often enough to have to consider that against your arguments?
Hi Andres. I see your point that I glossed over the reasons for commenting out code.
If a feature is no longer necessary, obviously deletion is the proper approach.
If the current implementation is buggy and must be disabled, or work was started but never finished, the code should be removed and worked in a branch.
Finally, if the code shouldn’t be run now but is complete and needs to run later, then a configuration setting should be created and used to enable the feature when desired.
There is a reason to leave some code in place but commented out: a basic implementation of an algorithm whose much more complicated optimized version appears alongside it.
I totally agree Phil. Example code is not zombie code. It’s inline documentation.
Even in that case the tests should be able to tell you what the algorithm does. But yes, concerning the fact that you write your code for an average of 10 people that read it in its lifetime, but that you also have to use a highly optimized version that uses up as less computing power as possible, it might be okay to show the ‘human readable’ version in a comment above the highly optimized code.
Really good point! Thank you Phil.
Good article Cory. We’re working with Git: whenever I see commented out code lines, I just delete them and commit it as ‘cleanup’. I also, once in a while, grep for ‘//’ and ‘/*’ and remove everything I find. Nobody ever complained, so I guess nobody seems to miss the code. Well, my tires are flat from time to time but… nah, just kiddin’. 😉
Woah, so you flat remove all comments in code? To clarify this post isn’t against comments. I’m a believer in comments when they make sense. Or are you saying you review each search hit and only remove commented code?
Of course I’m not removing all comments. If I’d like to do that, I’d use awk and remove them automatically, but that wouldn’t make much sense.
Most of the time people here use the uncomment feature of their IDE to swiftly uncomment code. Detecting this is easy, because the comment has a wrong indentation. So first of all I explicitly grep for those comments (‘ //’) and delete them.
Then I grep for comments in general (‘//’ and ‘/*’) and, if they’re used to comment out code, I delete them as well.
In case you’re interested in what I think of comments, read on:
It’s pretty easy to state ‘comments are good, when they make sense’. Of course they are, but what makes sense – for me, for you, for other devs? It differs widely.
I believe in a strict approach for such things: Comment with code, comment with tests. If you did that and still think that you need a comment, refactor your code and try to be more expressive with your code. If you still think that you need a comment, the code might really need a comment, but it should be a comment describing WHY the code does what it does, not WHAT it does (this can be read from the code).
So comments should be an exception and whenever you’re writing a comment you should have a feeling of having failed expressing with code and tests.
Bingo. I couldn’t agree more, especially regarding your general view on comments. I’d sum up the whole of your argument as: Strive for programming style as documentation. I agree comments are a smell and I’m currently finishing a blog post helping clarify what I mean by that. Thanks for clarifying!
Awesome zombie. I just found my new pic for random embedding in long email threads.
Great article, it’s a practice I’ve been following since I joined my current company in April and inherited an existing ASP.NET codebase to work on.
I’ve stumbled across blocks of commented-out PHP pasted into the .vb files…a true wtf.
While I agree about being decisive and clear in code, your whole “signal vs. noise” argument breaks down because often (especially when working with legacy code) the commented out code provides clear signals like:
// This code is a less than ideal hack/work-around but it really should be something like this…
// Here’s alternative/legacy code in case the new stuff introduces problems. It’s here so you don’t have to hunt for it in version control. (You’re welcome)
You’re right that you don’t need many worthless snippets of commented out code lying around but deleting them all, everywhere? No, that’s more OCD and arrogant than smart.
Regarding your first example, I actually appreciate and share your concern. As I clarified in the edit above, example code isn’t zombie code. It’s documentation. However, I disagree with your second example. Imagine how low the signal to noise becomes if no code is ever deleted and instead is commented out with a note that says “here is the old way in case you need it.” Source control is where history belongs. Cooking in a kitchen full of garbage isn’t easy or pleasant.
Also, I’m definitely not advocating blindly deleting commented out code everywhere. We’re all professionals and need to use our best judgement. I’m merely outlining some guiding principles. That’s why I outlined helpful questions to ask.
Great post!
Whenever I find a lot of zombie code and the original author is not about to ask why it’s there, I replace it with a one line comment like this:
//KO 27/07/2015 Removed commented out function DoStuff
This way if it is actually needed it can be retrieved from source control. If I see one of these comments which is several months old, I delete it because I’ll be confident that the zombie won’t need resurrecting.