在軟體開發的世界中,術語「亡靈程式碼(Dead Code)」可能含有多重定義。作為一種程式碼氣味,我們也可以將亡靈程式碼稱之為「未執行的程式碼(Unreachable code)」,這是指因為沒有辦法從任何程式碼流程中觸及到,所以永遠不會被執行的程式碼。亡靈程式碼還可能指雖然能夠被執行,但是對最終行為或產出結果沒有影響的程式碼。
在2019年的RubyConf中,Noah Matisoff介紹了「挖掘Ruby中的程式碼墳墓(Digging Up Code Graves in Ruby)」這個題目。他強調了亡靈程式碼是「程式碼衛生(Code Hygiene)」的一部分,而「程式碼衛生」又是技術債的一環。「程式碼衛生」包括與程式碼庫內與健康和專案壽命相關的各種實踐,程式碼氣味與亡靈代碼只是其中一部分。
接下來,我將解釋幾種類型的亡靈程式碼。
這是指永遠不會被使用的程式碼,它超出了控制流程路徑並且無法從程式的其餘部分到達。有時,您可能會遇到一種情況,即使條件語句存在,但條件永遠為真或為假。無法達到的程式碼則隱藏在永遠不會發生的條件中。
另一個常見的情況是,當您刪除或重構程式碼時,導致某些部分在更改後永遠不會被觸發或呼叫。然而,在提交程式碼審查期間,卻沒有人注意到這一點。
無意義的程式碼是指能夠被執行但卻對最終結果或行為毫無影響的程式碼片段。它通常也一樣被稱為「Dead Code」,但在這裡我使用了不同的名稱以避免概念混淆。
功能開關是一種軟體開發設計典範,用在不修改程式碼的情況下啟用或關閉軟體應用程式中的特定功能。它提供了一種以靈活和動態的方式控制特定功能的可用性和行為的方法,通常在運行階段而不是通過靜態程式碼更改或不同的程式碼分支進行操作。
然而,如果不小心管理,功能開關可能會在程式碼中引入亡靈程式碼的問題。《重構》的作者Martin Fowler曾分享過他對功能開關的看法。
精明的團隊將他們的功能開關視為庫存,並意識到這些庫存會產生成本,因此努力將庫存保持在最低限度。 — Martin Fowler
功能開關固然是有用的設計典範,但如果沒有謹慎使用,很可能會帶來亡靈程式碼這種氣味,不可不慎。
令人驚訝的是,「亡靈程式碼」這種程式碼氣味在由Joshua Kerievsky於2005年創建的「程式碼氣味到重構速查表」中並未出現。不過,我們仍然可以在其他指南中找到一些相應的重構技巧,例如Refactoring Guru網站。
亡靈程式碼可以指我們程式碼專案中的任何程式碼片段,諸如變數、參數、屬性、方法,甚至不會再使用的整個類別。因此,進行重構所需的技巧也因對象不同而異。
當我們發現某個類別幾乎沒有作用時,我們可以考慮將該類別中的剩餘所有功能移至另一個類別中。
當我們發現某個類別的功能非常有限,同時它又是一個子類別時,我們可以將其與其父類別合併。
當我們注意到某個參數未被使用時,應該考慮將其刪除。
In computer programming, the term "dead code" has multiple definitions. As a code smell, we can also refer to dead code as "unreachable code," which is code that can never be executed because there is no control flow path to reach it from the rest of the program. Dead code can also refer to code that is executed but has no effect on the final behavior or result.
At RubyConf 2019, Noah Matisoff discussed the topic of "Digging Up Code Graves in Ruby". He emphasized that dead code is a part of code hygiene, which in turn is a component of technical debt. Code hygiene encompasses various practices related to the health and longevity of a codebase.
Next, I will explain several types of dead code.
The code that would never used, it’s out of the control flow path and unreachable from the rest of the program. Sometimes, you may encounter a situation where you have a conditional statement, but the conditions are always true or false. The unreachable code is hidden within a condition that will never occur.
Another common case is when you delete or refactor code, causing some parts to never be triggered after the change. However, during the code review, no one notices this.
Pointless code refers to code that is executed or run but has no impact on the final result or behavior. It is also commonly known as dead code, but I am using a different name to avoid confusion here.
Feature toggles, also known as feature flags or feature switches, are a software development technique used to enable or disable specific features or functionalities in a software application without modifying the codebase. They provide a way to control the availability and behavior of specific features in a flexible and dynamic manner, often at runtime, rather than through static code changes or separate code branches.
However, if not managed carefully, feature toggles can introduce the "Dead Code" smell in your codebase. Martin Fowler, the author of "Refactoring", once shared his thoughts about Feature Toggles.
Savvy teams view their Feature Toggles as inventory which comes with a carrying cost, and work to keep that inventory as low as possible. — Martin Flower
It is a powerful design that is beneficial in most cases, but we should also be concerned about the code smell it might cause.
Surprisingly, the "Dead Code" smell does not appear on the Smells to Refactorings Cheatsheet created by Joshua Kerievsky in 2005. However, we can still find some matching refactoring skills from other guidelines, such as the Refactoring Guru website.
Dead code can refer to anything in our codebase - a variable, parameter, field, method, or even an entire class that is no longer used. Therefore, the skills required for refactoring also vary depending on the format of the dead code.
When we find that your class is doing almost nothing, we can definitely consider moving all the features from the class to another one.
When we find that a class is doing very little and it is a subclass, we can merge it with its superclass.
When we notice that a parameter is not used, we should consider removing it.
Digging Up Code Graves in Ruby by Noah Matisoff at RubyConf 2019
https://en.wikipedia.org/wiki/Unreachable_code
https://refactoring.guru/inline-class
https://refactoring.guru/collapse-hierarchy