干掉你程序中的僵尸代碼
隨著萬(wàn)圣節(jié)越來(lái)越流行,我感覺有必要跟大家討論一下一個(gè)在軟件開發(fā)中非常普遍的問(wèn)題:僵尸代碼。幾乎所有我接觸過(guò)的代碼庫(kù)里都四散著很多小段的,甚至大片大片的被注釋掉的代碼。這就是僵尸代碼。
//目前禁用這項(xiàng)功能。Jimmy在寫這段代碼時(shí)肯定是喝醉了。 //你可能以為這里發(fā)生了恐怖的代碼兇手案…不,不,我只是把它們注釋掉了…
為什么稱它們?yōu)榻┦a?你知道,僵尸不并不是真的死的。就像恐怕電影里告訴我們的,盡管僵尸看起來(lái)是死人,但它們?nèi)杂心芰λ奶幊鰶](méi)襲擊我們。相同的道理,僵尸代碼也是處于不生不死之間…它們?cè)谒艡C(jī)搞砸我們的工作。注釋掉的代碼還活著,它們就存在我們的代碼庫(kù)中。程序員在維護(hù)和重構(gòu)代碼時(shí)會(huì)和它們?cè)庥?,通常是滾動(dòng)屏幕時(shí)和它們擦肩而過(guò),或是在進(jìn)行關(guān)鍵詞搜索時(shí)和它們撞個(gè)滿懷。但這些代碼也確實(shí)是死的,因?yàn)樗鼈冊(cè)谲浖a(chǎn)品中并不執(zhí)行。因此,這些僵尸就應(yīng)該被燒掉,立刻。
僵尸代碼不死之軀
我認(rèn)為,有兩個(gè)原因?qū)е铝私┦a的肆虐:懶和害怕風(fēng)險(xiǎn)。懶程序員對(duì)代碼有收藏癖。他們?nèi)狈Υ_信的勇氣和清楚的認(rèn)識(shí)去刪除無(wú)用的代碼,于是他們就把它們隱藏在注釋里,期望有朝一日它們能復(fù)活來(lái)再次禍害人。代碼需要經(jīng)常的、有計(jì)劃的刪除,因?yàn)閮?yōu)秀的程序員都知道:代碼就是債務(wù)。越少越好。當(dāng)然,被注釋掉的代碼仍然是代碼。
爛程序員也許會(huì)爭(zhēng)辯說(shuō),他們注釋掉這些代碼是為了“萬(wàn)一”以后有人會(huì)需要它們。事實(shí)上,這好心反而是害了大家。這實(shí)際上說(shuō)的是害怕風(fēng)險(xiǎn),缺乏對(duì)版本控制系統(tǒng)作用的信任。有版本控制系統(tǒng)在,刪除的代碼永遠(yuǎn)不會(huì)真正的死掉。它們被埋到棺材里但卻活著。所以,注釋代碼的方法沒(méi)有多大實(shí)際效用。
對(duì)于程序來(lái)說(shuō),注釋掉的代碼跟刪掉的代碼一樣,不起任何作用。讓代碼半死不活,以僵尸的形態(tài)存在,造成技術(shù)債務(wù),最終會(huì)讓你的團(tuán)隊(duì)受害。要果斷,刪掉它們。
僵尸代碼降低信噪比
當(dāng)寫程序時(shí),我們一定要努力使代碼里有效信息的比率越高越好。這有助于人們理解程序,更快的閱讀代碼,防止我們因?yàn)檎`解而寫出有問(wèn)題的代碼。僵尸代碼直接的對(duì)抗代碼的可理解性。它拖延我們閱讀和維護(hù)代碼的速度,因?yàn)樗刮覀冊(cè)谄聊簧峡吹礁俚挠行Тa。它們就是視覺噪音,干擾人們的正常閱讀。處于某些原因,有些程序員會(huì)接受這種妥協(xié)的做法,可是在現(xiàn)實(shí)中,誰(shuí)會(huì)接受這種亂糟糟的畫面。想象一下,如果紐約時(shí)報(bào)看起來(lái)像這個(gè)樣子:
如何閱讀這斷斷續(xù)續(xù)的文字?噪音的增加就是對(duì)可理解性的損害。對(duì)這些被注釋掉的部分,盡管它們毫不相干,甚至?xí)`導(dǎo),但你卻無(wú)法對(duì)它們視而不見。有人會(huì)說(shuō),這不是最終發(fā)布的產(chǎn)品,這些代碼存在于開發(fā)過(guò)程中,拿它們跟發(fā)布的產(chǎn)品做對(duì)比,這就像拿蘋果比桔子。但是請(qǐng)記住,被寫出的每行代碼平均都要被閱讀10次。沒(méi)錯(cuò),你的代碼的閱讀人數(shù)沒(méi)有紐約時(shí)報(bào)多,但是,你擁有的是一個(gè)最重要的忠實(shí)的閱讀群體。就是我們。 Knuth對(duì)此關(guān)切進(jìn)行了精辟的總結(jié):
“編程是一種一個(gè)人告訴另一個(gè)人他想讓計(jì)算機(jī)做什么的藝術(shù)。” Donald Knuth |
而僵尸代碼讓你講話講不清楚。一個(gè)程序員需要去閱讀被注釋掉的代碼嗎?
僵尸代碼造成歧義妨礙調(diào)試
注釋掉的代碼會(huì)帶來(lái)歧義,人們會(huì)懷疑這些代碼是否該注釋掉。試想一下,你是一個(gè)來(lái)維護(hù)程序的程序員,突然看到了一片注釋掉的代碼,而程序就在這附近出了問(wèn)題。這個(gè)程序員的任務(wù)會(huì)變得更棘手。他需要閱讀和理解這些注釋掉的代碼,了解注釋它們帶來(lái)的影響。是因?yàn)闇y(cè)試而注釋這些代碼但忘了恢復(fù)嗎?也許注釋這些代碼的人可以提供幫助,但他是誰(shuí)?調(diào)查行動(dòng)開始。多余的歧義會(huì)消耗你的時(shí)間,增加你的思考負(fù)擔(dān)——本來(lái)可以是一次輕松的調(diào)試過(guò)程。
僵尸代碼影響關(guān)鍵詞搜索
在大型程序庫(kù)中,grep/find命令將會(huì)是你鎖定某些特定的代碼片段的雷達(dá)。然而,如果程序庫(kù)里到處散布著僵尸代碼,很有可能你捕捉到的目標(biāo)都是被注釋掉的。這是干擾。浪費(fèi)時(shí)間。
僵尸代碼影響代碼重構(gòu)
反省(重構(gòu))能修復(fù)我們的靈魂。我們應(yīng)該以小孩scout的做事原則為榮,永遠(yuǎn)把代碼收拾得比你想象的要整潔。然而,當(dāng)一個(gè)類或方法包含有大量的僵尸代碼時(shí),事情就不好處理了。如果重構(gòu)這段程序,我是否還要參考注釋掉的代碼?它們近期將會(huì)被重新使用嗎?它會(huì)影響我的新版的實(shí)現(xiàn)嗎?這些問(wèn)題對(duì)于維護(hù)的程序員來(lái)說(shuō)本該不需要回答的。
此外,集成重構(gòu)工具根本不會(huì)考慮這些注釋掉的代碼。因此,當(dāng)方法,變量,類被重命名或修飾符改變時(shí),這些注釋掉的代碼就不會(huì)同步做修改。當(dāng)你再想把注釋掉的代碼復(fù)活時(shí),它們很可能根本不能編譯。
有例外嗎?
沒(méi)有。很明確。有人會(huì)說(shuō)“我現(xiàn)在注釋它們是因?yàn)槲疫^(guò)會(huì)兒就要恢復(fù)它們。”OK,假設(shè)你是個(gè)家庭婦男,你走到起居室,看到:
想想你內(nèi)心的對(duì)話。這是個(gè)漂亮的房子,但這個(gè)東西又丑且怪異。我想開燈,但怎么會(huì)有膠帶?如果我撕掉膠帶去開燈,會(huì)發(fā)生什么事情?你很可能最終決定找貼膠帶的人。“哦,我想打開吊扇,但它啟動(dòng)時(shí)來(lái)回?fù)u擺,掉了下來(lái),我想修理它….”當(dāng)然,這是應(yīng)該的。而在你沒(méi)修好它之前,膠帶一直貼在開關(guān)上。我們當(dāng)然不該讓這些只修了一半的東西存在屋內(nèi)。同樣,我們也不接受這樣的代碼。
說(shuō)的更明白些,任何被注釋掉的代碼都是僵尸代碼,都應(yīng)該被刪掉。不管有多少。不管是在發(fā)布的產(chǎn)品中還是在開發(fā)環(huán)境中。僵尸代碼有時(shí)會(huì)在生死之間搖擺。如果代碼被注釋掉,這很有可能有東西沒(méi)有完成。經(jīng)常是配置需要來(lái)回切換或邏輯分支左右搖擺。注釋代碼可能會(huì)做實(shí)驗(yàn)性的來(lái)回切換,刪除這些代碼,建一個(gè)記事貼,記錄下需要做的事情。在記事貼中記下哪次提交版本時(shí)刪除了這些代碼。或者,新建一個(gè)版本分支專門做這事,合并時(shí)刪除它們。這樣,維護(hù)工作就不會(huì)受到干擾。
心里的核對(duì)表
如果你打算要注釋一段代碼,請(qǐng)先問(wèn)問(wèn)自己:
- 如果有可能的話,什么時(shí)候會(huì)取消注釋?
- 是否能刪掉它,如果日后有需要,從版本控制系統(tǒng)里找回?
- 對(duì)這些未完成的、有可能會(huì)回滾的代碼,能否用版本分支來(lái)處理?
- 這種需要來(lái)回切換注釋的功能可否通過(guò)配置實(shí)現(xiàn)?
- 重構(gòu)時(shí)也需要重構(gòu)這些注釋掉的代碼嗎?
讓我們開啟***次年度萬(wàn)圣節(jié)僵尸代碼大清剿。
本文英文原文鏈接:Kill the Zombies in Your Code