Discover Azul's high-performance Java platform providing faster speed, startup, & efficiency without code changes
Support

Dead Code

What is Dead Code?

Dead code resides in an application’s codebase but is not used by the application. Removing dead code significantly reduces the burden of maintaining the codebase and makes it easier to understand. Developers want to remove dead code but are terrified of doing so because the process of identifying and removing dead code is manually intensive and can break the application. Traditional tools like IDEs do not provide a complete view of code used in production over time due to their static nature. Profilers are so overhead intensive they cannot practically be used in production settings.

Dead code and “unreachable code” both refer to the presence of code within the application or system that don’t serve any useful purpose. “Dead code” is executed but its results are never used anywhere else, while “unreachable code” is literally code that is never actually executed during the application’s run. The two are often considered synonyms despite the (slight) technical difference between them.

What are the most common types of dead code?

Dead code is executed, so it could generate a result, but that result is never used after being calculated. It could yield a side effect (perhaps generating an exception, creating a file on disk, or modifying the contents of a database) without anyone realizing it, since the result of the execution is never used.

Consider, for example, a method that takes two integers, x and y, and divides x by y before throwing away the result and continuing to do other work. This might seem to be a harmless waste of CPU cycles, but it harbors a class of bugs (what happens when y is 0?) that could change the execution path drastically.

Why is it important to identify dead code?

Dead code increases the cognitive burden for developers to understand the code, makes onboarding new developers difficult, and makes it difficult to update libraries and upgrade from one Java version to the next. Dead code impacts overall developer productivity and slows down the velocity of delivering new features. If unaddressed, dead code becomes harder to deprecate and can lead to zombie code that reawakens with unintended consequences.

Any line of code in a system is a line of code that must be tested, debugged, and maintained. If this code serves no useful purpose, then each second spent testing, debugging, and maintaining is a wasted second.

More importantly, when developers are onboarding to a new system, they assume that all the code in the project serves some useful purpose, and thus spend the time and energy required to understand it. If dead code is present in the project, they will be trying to wrap their head around code that is never executed, which means (at best) they are wasting their time and energy, or (at worst) building a false perspective of the code based on incorrect assumptions suggested to them from the presence of the dead code.

Who is responsible for eliminating dead code?

While compilers and runtime environments often take steps to eliminate dead code as part of their optimization effort. However, they often take a conservative approach since they do not have a holistic picture of the entire system by which to make judgements. For example, in both the JVM and CLR environments, the use of Reflection can construct objects and/or methods by name, which is impossible for any static code analysis to spot. Dynamic language environments (Python, Ruby, and Javascript, to name a few) have an even more difficult time with this, since much of their execution model is based entirely around runtime evaluation.

This leaves the primary responsibility for dead code elimination to the development team, which carries its own share of risks. Time must be allocated for the team to “chase down” code which might be dead and examine all possible places where and how it might be invoked before removing it. For safety’s sake, the team will also very likely want to put some safeguards in place “just in case” that code ever does get called. This adds to the work required to refactor the dead code (and leaves more work to be done in a future sprint, when it’s finally decided to remove the guarding code).

What risks are involved with eliminating dead code?

One risk is leaving dead reachable at the end of some unidentified path where it can still be executed. If the code isn’t present, it could generate an exception, cause the system to crash, the company to crash, and the world to end. After all, because developers are often working with only a portion of the entire system at a time, it is easy to imagine that “somewhere, out there” there is code that depends on this class or this function that otherwise seems unused. Coupled with this is the realization that leaving it in “doesn’t hurt anything” but removing it “could get me fired” if it turns out that it is, in fact, still used somehow.

A second risk, more common and more difficult to quantify, is that a developer struggles to understand code with dead code in it. Because dead code is really about that code which is executed at runtime, developers could build – and continuously maintain – an entire unit test or integration test suite against the dead code without realizing that it never sees use at runtime. This is, at best, a waste of time and energy, and can in some cases lead to bad or suboptimal decision-making around future growth of the code.

How does Azul help find dead code?

Azul Code Inventory, a feature of Azul Intelligence Cloud, is the only solution that precisely catalogs what code runs in production across all of an enterprise’s Java workloads. It slashes the time and burden of maintaining and testing unused code, significantly improving developer productivity and ultimately saving money. 

Code Inventory is a cloud service that uses information inside the Java Virtual Machine (JVM) to provide a comprehensive view of what code runs in production with no performance penalty. It lowers the burden of maintaining and testing unused and dead code. 

A majority of software development is spent on maintenance. Maintaining unused and dead code wastes time and budget. Traditional tools like IDEs with static analyzers do not provide a complete view of the code used in production over time due to their static nature. Profilers are so overhead-intensive they cannot practically be used in production settings. Manually-intensive log line insertions provide only a partial view of unused and dead code. 

Azul Intelligence Cloud

Boost DevOps Productivity with Actionable Intelligence from Production Java Runtime Data from any JVM.