作者|蒂娜,核子可樂
(資料圖)
任何大型項目都必然會積累“死代碼”,即不再使用的模塊,或者在早期開發(fā)期間存在但多年未運行的程序。事實上,很多項目創(chuàng)建后運行了一段時間,然后就沒有人關(guān)心它們了。
這些死代碼將繼續(xù)產(chǎn)生成本:自動化測試系統(tǒng)不知道哪些代碼不再需要測試,負責(zé)大規(guī)模清理的人員將移動大量不再運行的代碼。因此,雖然這段代碼的生成成本很高,但維護起來也需要大量時間。這類維護工作是不能輕易跳過的,否則將來肯定會造成更大的追溯管理成本。
那么,是否可以通過減少代碼量來降低維護成本呢?代碼倉庫中的內(nèi)容真的有必要存在嗎?
我們通常不清理代碼。清理它們需要大量的時間和精力,而且要證明它是否仍然有用就更麻煩了:我們不能僅僅依靠“切斯特頓柵欄”規(guī)則,即“看不出這有什么作用”。 “使用它,清除它”,因為一些災(zāi)難警報和閏年觸發(fā)代碼閑置時間較長,如果清除可能會帶來大麻煩。
在谷歌,代碼清理更加困難。
谷歌與業(yè)內(nèi)其他公司不同。它只有一個代碼倉庫,整個公司的代碼都放在這個倉庫里。 20 多年來,數(shù)以萬計的軟件工程師向同一個包含數(shù)十億行的代碼存儲庫提交了貢獻。這個代碼存儲庫存儲在Piper系統(tǒng)中,其中集中了與編碼相關(guān)的共享庫源代碼、生產(chǎn)服務(wù)、實驗程序、診斷和調(diào)試工具等。
這種開放的方法非常強大。如果工程師不確定如何使用庫,他們可以搜索以查找示例;善意的貢獻者可以對整個代碼存儲庫進行重要更新,包括轉(zhuǎn)向更新的API、引入Python 3 或Go 泛型等語言功能等。
編寫代碼對應(yīng)著極高的成本,因此代碼往往被公司視為重要資產(chǎn)。然而,不再使用的代碼將繼續(xù)花費時間和精力進行維護和清理。一旦代碼庫達到一定規(guī)模,投入工程時間來自動清理就開始有意義,特別是如果您像Google 一樣擁有數(shù)十億行代碼。
但對于這樣一個單一的代碼庫,最糟糕的情況就是“源代碼”被意外刪除。 Google SRE 首席軟件工程師表示,這種情況“意味著Google 使用的每個數(shù)據(jù)中心、每個數(shù)據(jù)中心”。每個工作站都會突然停止運行——,不僅僅是關(guān)閉,甚至存儲也將變得無法使用(盡管這只發(fā)生在世界末日期間)?!?
那么,他們?nèi)绾吻謇磉@些死代碼呢?谷歌最近在其博客上介紹了Sesenmann“自動代碼刪除”項目,該項目旨在自動識別無效代碼,然后發(fā)送代碼審查請求(更改列表)以將其刪除。
Sesenmann在德語中的意思是“死亡”。據(jù)Google 稱,該項目非常成功,每周都會提交1,000 多個更改列表以供刪除,迄今為止已刪除了Google 所有C++ 代碼的5%。
Google 的構(gòu)建系統(tǒng)Blaze(Bazel 的內(nèi)部版本)是實現(xiàn)這一目標(biāo)的關(guān)鍵:它將通過以一致且可訪問的方式表示二進制目標(biāo)、庫、測試和源文件之間的依賴關(guān)系來幫助維護人員。依賴圖已構(gòu)建。這樣,可以找到未鏈接到任何二進制文件的庫,并可能將其作為刪除目標(biāo)。
但這只是問題的一小部分:如何處理這些二進制文件?所有一次性數(shù)據(jù)遷移程序和已棄用的系統(tǒng)診斷工具又如何呢?如果不清理的話,相應(yīng)的依賴庫也會被保留。
了解程序是否有用的唯一完美方法是檢查它們是否正在運行。因此,對于內(nèi)部二進制文件(即在Google 數(shù)據(jù)中心或員工工作站中運行的程序),程序運行時會寫入日志條目,記錄時間并對應(yīng)于特定的二進制文件。通過聚合,獲得Google內(nèi)部使用的每個二進制文件的活動信號。如果某個程序長時間未使用,項目將嘗試發(fā)送相應(yīng)的刪除變更列表。
當(dāng)然,也有例外:有些程序代碼只是使用API的示例;有些程序運行在根本沒有相應(yīng)日志信號的位置。在這種情況下,刪除代碼必然會造成大麻煩。鑒于此,建立一個阻止列表系統(tǒng)非常重要,該系統(tǒng)允許每個人標(biāo)記異常,并避免用錯誤的更改列表打擾已經(jīng)忙碌的軟件工程師。
在Google的博客上,Google工程師Phil Norman舉了一個簡單的例子。
假設(shè)您有兩個二進制文件,每個二進制文件都依賴于不同的庫,并且還共享第三個庫。忽略源文件和其他依賴關(guān)系,我們將這種關(guān)系繪制為以下結(jié)構(gòu):
如果main1 正在使用,但main2 最后一次使用是在一年多前,您可以構(gòu)建一個樹傳播活動信號來將main1 及其依賴的所有內(nèi)容標(biāo)記為活動狀態(tài)。其余的可以去掉;由于main2 依賴于lib2,這次我們希望在一次更改中刪除兩個目標(biāo):
到目前為止一切順利,但真正的生產(chǎn)代碼需要進行單元測試,構(gòu)建目標(biāo)由正在測試的庫確定。這使得整個遍歷結(jié)構(gòu)變得更加復(fù)雜:
測試基礎(chǔ)設(shè)施運行所有測試,包括lib2_test,但lib2 從未“實際”執(zhí)行。也就是說,我們不能簡單地依賴測試運行作為“活躍”信號:在這種情況下,可能會誤認(rèn)為lib2_test 保持活動狀態(tài),導(dǎo)致lib2 永遠存在。只有未經(jīng)測試的代碼才能被清理,這嚴(yán)重阻礙了有效的清理工作。
基本目標(biāo)是每個測試共享其正在測試的庫的使用,因此我們可以通過使庫和測試相互依賴來實現(xiàn)此目標(biāo),并相應(yīng)地在圖中創(chuàng)建一個循環(huán):
通過這種方式,每個庫及其測試都被轉(zhuǎn)換為強連接組件??梢允褂门c之前相同的方法來標(biāo)記“活”節(jié)點,然后找到要刪除的“死”節(jié)點集合。不同的是,這次使用了Tarjan的強連通分量算法來處理循環(huán)。
這樣做很容易,但前提是您可以輕松查看測試與其測試的庫之間的關(guān)系。不幸的是,事情并不總是那么美好。在上面的示例中,遵循簡單的命名約定可以讓您快速將測試與庫匹配。但這種方法在實際生產(chǎn)系統(tǒng)中往往行不通。
例如以下兩種情況:
左邊的是LZW壓縮算法的實現(xiàn),有獨立的壓縮器和解壓縮器庫。這個測試實際上測試了兩者,以確保壓縮和解壓后數(shù)據(jù)沒有損壞。右邊的web_test負責(zé)測試Web服務(wù)器庫,它使用URL編碼器庫來提供支持,但并不實際測試URL編碼器本身。這希望將左側(cè)的LZW 測試和兩個LZW 庫視為相同的連接組件,而在右側(cè),我們希望排除URL 編碼器,僅將web_test 和web_lib 視為連接組件。盡管它們需要不同的處理方法,但兩種情況的基本結(jié)構(gòu)是相同的。在實際應(yīng)用中,可以建議工程師將url_encoder_lib等庫標(biāo)記為“純粹用于測試”(即僅用于支持單元測試),這樣就可以解決Web測試的需求。
除此之外,Phil 表示,目前Google 的方法是利用測試和庫名稱之間的編輯距離來選擇最有可能與給定測試匹配的庫。至于如何識別像LZW這樣一個測試對應(yīng)兩個庫的情況,這可能需要測試覆蓋率數(shù)據(jù),谷歌還沒有討論過這種方法。
自動代碼刪除對于很多工程師來說可能是一個陌生的概念,就像20年前單元測試剛剛誕生時,很多人都對它抱有抵觸一樣。
雖然刪除死代碼最終對軟件工程師自己有幫助,而且大家當(dāng)然都希望自己管理的代碼項目能夠保持干凈,但在“Sesenmann”的運營過程中,谷歌也發(fā)現(xiàn)很多工程師并不愿意頻繁收到刪除代碼的指令。自動更改列表。這是該項目的社會工程部分,其重要性不亞于軟件工程。
改變?nèi)藗兊南敕ㄐ枰獣r間和精力,也需要大量細致的溝通。森森曼的溝通策略分為三個主要部分。
最重要的是變更的描述,這是審閱者首先看到的。對變更的描述必須簡明扼要,同時確保為審閱者提供足夠的背景信息以做出正確的判斷。這樣的平衡其實很難實現(xiàn):如果內(nèi)容太短,很多人就找不到自己需要的信息;如果內(nèi)容太短,很多人就找不到自己需要的信息;如果內(nèi)容太短,很多人就找不到自己需要的信息。如果內(nèi)容太長,屏幕可能會被文字填滿,讓人頭疼。事實證明,包含清晰標(biāo)記的支持文檔和常見問題解答鏈接極大地提高了變更描述的可讀性和可接受性。
第二部分是支持文檔,也采用了簡潔明了的措辭和良好的導(dǎo)航結(jié)構(gòu)。不同的人需要不同的信息:有些人需要確保源代碼控制系統(tǒng)中的刪除可以回滾,其他人想知道如何處理更改的負面影響,例如修復(fù)構(gòu)建系統(tǒng)的誤用。通過仔細考慮和迭代用戶反饋,支持文檔將成為滿足這些需求的寶貴資源。
第三部分是處理用戶反饋。有時,這也可能是最困難的部分:由于負面反饋多于正面反饋,因此通常需要冷靜的頭腦,甚至需要一點外交手段。總而言之,重要的是要記住,這種反饋通常反映了改進系統(tǒng)的最佳方式,盡量讓用戶更加滿意,并避免將來出現(xiàn)類似的負面反饋。
Phil在谷歌博客上表示,根據(jù)谷歌的業(yè)務(wù)規(guī)模,估計自動代碼刪除給他們帶來了數(shù)十倍的投資回報,大大節(jié)省了維護成本。
自動刪除代碼需要解決技術(shù)和文化挑戰(zhàn)。他在博客中總結(jié)道:“雖然我們在這兩方面取得了重大進展,但我們?nèi)匀徊荒苷f我們已經(jīng)徹底解決了這個問題。但是,隨著改進的不斷進行,自動刪除將被越來越多的人所接受,其積極影響也將隨之而來。”產(chǎn)生的影響也會越來越大,這個投入的價值因人而異,如果你還控制著一個巨大的單一代碼倉庫,你可能要認(rèn)真考慮減少總的C++代碼的維護負擔(dān)。在谷歌,5% 已經(jīng)標(biāo)志著一個巨大的勝利?!?
如果刪除代碼也能帶來巨大的好處,這是否意味著是時候為刪除代碼行設(shè)置KPI 了?
參考鏈接:
https://testing.googleblog.com/2023/04/sensenmann-code-deletion-at-scale.html
https://news.ycombinator.com/item?id=35755841
本文轉(zhuǎn)載自:
https://www.infoq.cn/article/0NoTQLqQh4GfIF9cfSLf