After 10 weeks, it’s time to wrap up our series about code smells. Let’s go out with imprinting the acronym YAGUI in our collective minds! In this post, we will discuss dead code and how we can remove it, resulting in a cleaner and more readable code base.
In this series:
- Sharpen your sense of (code) smell
- The simple case of special string types
- Join data items that want to go together
- Easy conversions and readability
- Put it down in layman’s terms
- Every method begins with “new”
- Dependency injection doesn’t strictly require frameworks
- Super SuperClasses
- Null pointers: an opportunity, not an exception
- You ain’t gonna use it!
One of the first things I do when starting a new .NET project, even for a quick demo project, is clearing out all the unused portions of code I can find. If the project uses one of the standard Visual Studio templates, chances are that I spend a few seconds removing a bunch of unused
using directives and unnecessary assembly references.
You may ask why I do this. After all, none of this unused code will make my application run slower. Detecting unused code, sometimes more creatively referred to as dead code, is one of the primary tasks of the optimization module of language compilers. No dead code will ever affect the final performance, as it is “compiled out”.
But as long as the code remains checked in the version control repository, the development team may be confused seeing code that is not needed. It is also too easy to bring dead code back and revive it, getting a possible source of new problems. Therefore, dead code is not a good thing to have, even though it isn’t harmful most of the time.
Another task I complete as soon as I start a new project, and then repeat periodically, is getting rid of all unused assembly references. Again, listing a hundred of assemblies in the References folder of a .NET project doesn’t make your code slower. Compilers will only process references that are effectively being used.
using directives and references, however, may affect the compilation step and make the IDE slower as it has to scan many cross-links, files and binaries that may not be needed. A good example would be when the IDE is building its code completion index.
You can get rid of unused directives and references fairly quickly using a tool like ReSharper or Rider. And if you happen to remove one too many, bringing it back is easy as well. From the References node in a project, we can use the Refactor | Remove Unused References context menu action to clean up unneeded project and assembly references.
Dead code, instead, is a slightly trickier code smell to tackle. Dead code is skipped out during compilation, but it remains in our code base. To be picky, there are two different types of dead code:
- Code that is never reached by any execution paths, and
- Code that is reached, runs, but does nothing or has no use.
Compilers and code analysis tool can easily spot the portions of the code that are never reached. A simple example would be this code:
public void SomeMethod()
// More code here...
As you can deduce, the
Console.WriteLine will never get executed. ReSharper and Rider will spot this and suggest removing the statement altogether.
Another case could be this one, where the second
return statement is not needed:
public void SomeMethod()
// More code here...
Why is this unreachable code there in the first place?
Most likely, unreachable code is a remnant of some deep refactoring session or of some confused and overly intricate coding session. It could be the result of a merge in source control.
Dead code can be the symptom of a bug not yet fixed or not yet discovered. In general, having an automatic tool to signal dead code is immensely helpful because any such findings should sound like an alarm bell. While not every alarm means there’s a fire, it’s always a good idea to check it out, just in case. In order to improve readability, dead code should be investigated and eliminated if it’s dead beyond any reasonable doubt.
Any dead code that is left inside the code base is code that requires attention and time, and thus costs effort (or money). A double
return statement or an
if statement that is always true will not weigh too much, but hundreds of lines of dead code make for a more substantial cost.
Dead code also creates noise and entropy, in much the same way it happens with commented lines of old code being left over in a method. On the first development team I joined over two decades ago, we had an empiric approach at things summarized by the slogan Commented code always works. That was because one of us once claimed to have run into an error after removing a commented line of code. Funny? Probably, but the same guy also claimed that the code compiled again once he restored the comment! Older and wiser, today I would say that one should not be afraid of deleting commented code because an original copy of it is silently maintained by the source control tool.
The typology of unreachable code is not limited to unused branches, but also includes unused parameters in used methods, unused variables, and private methods never invoked. In all these cases, ReSharper and Rider’s quick-fixes (Alt+Enter) are a great help, and at any rate, the compiler will optimize those lines out.
And then there are public methods with no traceable references. If it’s public, then it means it can be called from outside the solution. If no traceable reference exists, then it means any of the following:
- Dead code
- Invoked indirectly, e.g. via dependency injection
- Part of an API invoked from external and/or internal clients
This may not be obvious at first glance. Tools can be of great help here. When static analysis is possible, e.g. in one project, automatic code inspection in ReSharper and Rider can inform us of unused code branches and other dead code. With a code coverage tool like dotCover, it is possible to analyze the parts of code that get executed at runtime, to make a more informed decision about removing potentially dead code.
After investigation you should be able to determine if a piece of code is used or not, and act accordingly. It could be that you ain’t gonna use it. YAGUI!
Have a look at our code analysis series for more tips and tricks on automatic code inspection with ReSharper and Rider.
Download ReSharper 2018.1.4 or Rider 2018.1.4 and give them a try. They can help spot and fix common code smells! Check our code analysis series for more tips and tricks on automatic code inspection with ReSharper and Rider.