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.
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.
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.