程序員爲什麼熱衷於重寫軟件?

軟件重寫可能是一項非常危險的工作——存在許多非常真實的成本和風險,甚至可能會讓最善意的努力付諸東流。然而我們確實重寫了!並且經常這麼幹。雖然明知道會有風險,業務和技術還是一起宣佈:“該死的魚雷,我們正在重寫這堆舊代碼,這次我們一定會成功的!”

我想在本文中探討的問題是“爲什麼”。考慮到所有的成本和風險,爲什麼我們還經常選擇重寫可運行的遺留系統,而不是局部重構呢?根據心理學、社會學和其他領域的分析,我想揭示一些微妙或不那麼微妙的力量,這些力量推動着我們每個人走向全面重寫的道路。

我想澄清一下。我絕對不認爲重寫總是錯誤的選擇——重寫有很多好的、合理的理由,我們將會在後面的文章中介紹。

創作的樂趣

開發人員喜歡創作的過程。我們天生就是構建者。儘管我們知道這項工作通常需要我們勇敢地冒險進入他人代碼的黑暗角落,以消除一些潛在的缺陷或調整一些深奧的計算,但這並不是吸引我們編程的原因。這只是一種必然的不幸。如果有機會,我們還是希望構建自己的系統。我們希望以一種對我們有吸引力的方式,以一種對我們有意義的方式,來制定解決方案。雖然冒着感情用事的危險,但編程是我們的藝術。同樣的,畫家寧願從一片空白的畫布開始作畫,而不願在另一幅畫像中作畫。

事實上,不僅是程序員喜歡自己創建的東西,所有人都是這樣的。Dan Ariely在其著作《回報:影響人們動機的隱藏邏輯》(The Hidden Logic That Shapes Our Motivations)一書中,提到了一個有趣的軼事,強調了人類的這種傾向:

早在20世紀40年代,當大多數女性都在家工作時,一家名爲P. Duff and Sons的公司就推出了盒裝蛋糕粉。家庭主婦們只需加水,在碗裏攪拌麪糊,將其倒入蛋糕盤中,烘烤半小時,然後調味,他們即可喫到一份美味的甜點。但令人驚訝的是,這種蛋糕粉賣得並不好。原因與味道無關,與流程的複雜性有關——但這不是我們通常認爲的複雜性。
Duff發現,家庭主婦們覺得這些蛋糕不像是她們自己做的;在賦予人們創造力和有意義的所有權方面,她們付出的努力實在太少了。因此,該公司將雞蛋和奶粉從蛋糕粉中取出。這次,當家庭主婦們添加新鮮的雞蛋、油和純牛奶時,她們會覺得自己參與到了製作過程中,並且對最終產品更加滿意了。

Ariely接着描述了一些科學研究,這些研究顯示了同樣的效果:事實上,我們天生就喜歡享受創作的樂趣,而且我們可能會過分重視自己創作的成果(與我們實際創建出的東西有多好無關)。

我認爲這就直接說明了我們作爲開發人員對維護遺留系統的感覺。當然,我們能夠完全按照某種規定的模式在報表中添加一個新列,或者在屏幕上添加一個按鈕,但這並沒有給我們帶來那種對工作的依戀感或意義。我們想要的是打碎我們自己的雞蛋,並加入我們自己的牛奶和油。換句話說,我們的大腦可能會自然而然地傾向於重寫而不是重構。

維護之苦

從創建過程中尋求樂趣的另一面,是可以避免因繁瑣的維護工作而感到沮喪。維護遺留系統總是會帶來許多挫敗感。隨着時間的流逝,軟件趨於衰退。業務需求發生了變化,迫使開發人員將原來的廚房改造成前門廊。關鍵Deadline迫使我們偷工減料,並使用TODO和複製粘貼的方式交付代碼。隨着時間的流逝,新老開發人員在系統中開發的東西會在系統架構中形成“沉積層”——“你可以從風格上看出來,這段代碼來自pre-IPO時期,依賴注入之前”。

在這樣的遺留代碼中工作可能令人沮喪。即使只是要修復最簡單的bug,也可能需要我們追蹤複雜的執行路徑,因爲它們會在不同的軟件貧民窟裏進進出出,在那裏雜草叢生,而破爛的窗戶尚未修復。將系統的邏輯(或非邏輯!)掌握在我們的腦海中可能是一項艱鉅的任務。當我們盡職盡責地接受我們的命運,修復這些缺陷,並添加那些小的改進時,我們一直在思考如何才能以不同的方式來做這件事。我們會想,如果能一把火把這個充滿複雜性和技術債的軟件堡壘燒掉,從頭開始,那該有多好呀。

然而,對於最終用戶,甚至對於企業而言,情況可能就不一樣了。當然,遺留系統看起來可能不那麼美觀,或者可能需要比首選系統更多的點擊或延時,但它還是可以工作的。該系統功能正常,相對可靠,並且圍繞它的業務流程雖然不是最佳的,但至少能被很好地理解。但是對於開發人員,尤其是那些不是原始創建者的開發人員來說,這個系統代表的不是舒適,而是挫折和約束。我們在裏面工作的時間越長,就越想逃離並重寫它。

利己主義與技術趨勢

除了尋求樂趣或避免挫敗感之外,作爲開發人員,我們也很清楚什麼工作纔對自己的職業生涯有實際好處,這通常都不會是維護遺留系統。 2019年的Stack Overflow 開發人員調查顯示,語言的新穎性與開發人員的收入直接相關。薪資排名前五位的語言,平均年齡只有15歲,而排名後五位的語言,平均年齡則爲39歲。

語言 年齡 平均工資
Clojure 13 $90k
F# 15 $80k
Go 8 $78k
Scala 17 $78k
Elixir 9 $76k
按平均工資排名前5的語言

語言 年齡 平均工資
HTML/CSS 25 $55k
VBA 27 $55k
Assembly 71 $52k
C 48 $52k
Java 25 $52k
按平均工資排名後5的語言

當然,還有很多其他因素也在起作用,但是在所有條件都相同的情況下,很顯然,如果我們不跟上新技術的步伐,那麼我們能賺的錢就會更少。對於最近進入就業市場的任何一個開發人員來說,這並不足爲奇。招聘人員、簡歷篩選機器人,甚至其他開發人員都在通過我們的經驗尋找最新的熱門詞彙:微服務、Kubernetes、漸進式Web應用程序或其他前沿技術。越新越前沿,越好。其邏輯大概是,與僅像僕人一樣從事遺留系統維護工作的人員相比,掌握最新技能的開發人員必然是更有動力也是投入了更多精力的(諷刺的是,從前僱主的角度來看,維護遺留系統可能纔是正確的)。

這樣的邏輯確實有一些道理。編程工作確實需要高度的探索和實驗,因此對學習的熱情真的很重要。但是,我認爲我們這個行業有一種趨勢,有時更傾向於追逐時髦的新技術而非實際需要的技術。當我們選擇一種語言、工具或框架時,我們當然希望選擇一種最適合這項工作的,但是我們也要考慮這個選擇對我們來說意味着什麼。我們是使用諸如Java之類的成熟但笨拙的語言的人,還是選擇使用Go或Kotlin之類的富有“遠見”和“創新性”的人呢?從某種意義上說,我們使用的技術就像我們穿的衣服一樣——它們訴說着我們是誰,我們代表了什麼,我們的價值是什麼。從某種程度上說,技術和時尚是一樣的。

當然,現在這個類比還遠遠不夠完美,但看看社會學家Herbert Blumer對時尚週期的描述,其中確實有一些相似之處:

精英階層試圖通過明顯的標記或標誌,比如獨特的着裝,來使自己與衆不同。然而,緊隨其後的階層成員採用這些標誌來作爲滿足他們爲獲得較高地位而努力的一種手段。它們依次被它們下一階層的成員所複製。

通過這種方式,精英階層的顯著標志就會在階層金字塔中逐漸向下過濾。但是,在這個過程中,精英階層失去了這些獨立身份的標誌。因此,又會導致設計出新的區別標誌,這些標誌又被下面的階層所複製,從而重複了這一循環。

簡而言之,無論是服裝、音樂還是編程,時尚都與階級分化相關。精英們不斷創新,大衆趕上來,所以他們可以與精英聯繫在一起。然而,一旦他們這樣做了,精英就不再是“精英”了,因此他們必須重新創新。大衆最終又會追隨而來,循環往復,永無止境。

當談到重寫還是重構的問題時,我認爲這可能是另一種微妙的力量,它促使作爲開發人員的我們朝着全面現代化的選擇邁進。維持現有系統的活力,就像穿十年前的衣服一樣。當然,它們仍然可以讓你保持溫暖和乾燥,但它們已經破舊了,可能無法向世人展示你想要展示的形象。對於那些不喜歡緊跟技術潮流的人,我們至少要知道,這確實會對我們的職業生涯產生明顯的影響。所以,要麼保持步調一致,要麼減少收入。

直覺錯誤與思維捷徑

拋開動機和個人利益不談,還有另一種強大的力量可以迫使我們相信,對於遺留系統而言,重寫是顯而易見的最佳決策,即使事實並非如此。那就是我們自己的直覺。

Daniel Kahneman在關於決策和認知偏見的著作《思考,快與慢》中解釋到,儘管我們很容易就所有類型的問題形成意見(並對這些意見充滿了信心),但這些問題通常是基於簡單的思維捷徑,而不是可靠的推理。對於利害關係不大的決策來說,這可能沒什麼問題,但是對於像重構或重寫整個系統(這可能要花費數百甚至數千小時的精力)這樣的問題,風險巨大。我們需要確認,問題到底能不能得到解決。

我們使用的一個微妙的思維捷徑是替代。當遇到需要深入思考和分析的問題(這是一項艱苦的工作)時,我們通常會把它替換成一個更簡單的問題,這樣我們就可以更快地解答了。例如,想要知道重寫與重構是否值得,我們應該要了解重寫的成本是多少,它能爲組織帶來多少價值,何時實現投資回報(ROI)等。但這太難了!因此,我們會用更容易回答的問題來替代這個問題,比如,“現有系統是否有問題?”或者“ UI是否過時了?”。答案自然是肯定的。這麼做的話,雖然看起來好像解決了原始(困難的)問題,但實際並沒有,我們只是將其替換成了更容易解答的問題。

多年來,我一遍又一遍地看到這種辯解。與其停下來試圖回答真正困難的問題(重寫的淨值是否大於重構的淨值),我們經常回過頭來討論更簡單的替代問題。我們會說“我們要重寫,因爲遺留系統難以使用”或者“因爲沒有人能理解它”。是的,這些答案確實相關,但還不夠。也許沒有人能理解代碼是真的,但是一個人要理解這些代碼需要多少成本呢?這個成本與從頭開始重寫整個系統相比,是更大還是更小呢?bug和技術債也是如此,成本和回報到底是多少?

當然,現在我們生活在一個瞬息萬變的世界裏——我們沒有無限的時間來分析、制定商業案例並發表意見。但是對於一個像重寫或重構這樣至關重要的問題,我們應該試着放慢速度,確保我們至少回答了真正的問題,而不是回答了一個更容易的替代問題。

“拋棄”的文化

推動我們重寫的最後一股力量並非植根於我們自身,而是植根於我們周圍的社會。我們生活在一種“拋棄第一”(throw-away)的文化中。我們的食品和飲料裝在一次性容器裏;購買的產品都是用過多的塑料和紙進行包裝的;我們的電器和設備都是爲淘汰而設計的,因此它們經常連修都沒法修,即使我們想修也不行。換句話說,我們已經習慣了不加思索地丟掉一些東西——不管它是有了瑕疵、破裂了、故障了,還是隻是稍微有點舊或有點磨損,我們都會選擇扔掉它。

因此,我認爲這種“拋棄第一”的態度也有可能影響我們對軟件的看法。當一個系統老化時,我們的傾向不是拔出膠帶和WD40來延長它的使用壽命,而是將它扔掉,出去買(或重寫一個)新的型號 。實際上,“遺留”(legacy)一詞似乎總能引起開發人員的同情。“真遺憾,你不得不爲此工作。”他們會問,“你打算什麼時候重寫呢?”

公平地說,軟件開發是一個不斷改進和創新的行業。我們一直在學習更好、更簡潔的做事方式,然後將它們整合到我們的工具、框架和語言中。當他們可以使用Java 8 的流(stream)和Lambda表達式,或更簡潔的函數式語言(如Kotlin或Scala)時,沒有人會再刻意去使用Java 7。此外,新的平臺和設備不斷髮布,而我們現有的系統可能無法移植到這些平臺和設備上。因此,我並不是說,以舊換新一定是個輕率的選擇。

但我確實認爲,意識到這種潛在的偏見是有幫助的。當我們過快地放棄一個可能還算可靠的遺留系統(儘管它有些舊或複雜)時,組織可能會付出代價。在接下來的文章中,我將討論遺留系統現代化的三個不同“R”:“修復”(Repair)、“重用”(Reuse)和“再利用”(Recycle)。與其把我們現有的系統看作是需要敬而遠之的東西,我們應該理解並利用好那些目前仍在起作用的組件和元素,並只嘗試重寫那些需要重寫的組件和元素。

原文鏈接:

http://rewriteorrefactor.com/chapter-3-why-we-rewrite-even-when-we-shouldnt.php

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章