軟件系統的熵——軟件系統進化論


生存需要投入更多的思想和精力。宇宙的熵在升高,有序度在降低,像平衡鵬那無邊無際的黑翅膀,向存在的一切壓下來,壓下來。可是低熵體不一樣,低熵體的熵還在降低,有序度還在上升,像漆黑海面上升起的磷火,這就是意義,最高層的意義,比樂趣的意義層次要高。要維持這種意義,低熵體就必須存在和延續。——《三體》

大千世界,無奇不有,但大都逃不出規律、哲學的範疇。儘管軟件開發幾乎不受任何物理定律的約束,熵(entropy)對我們的影響卻很大!熵是一個來自物理學的概念,指的是某個系統中的“無序”的總量。遺憾的是,熱力學定律保證了宇宙中的熵傾向於最大化!

比如說:宇宙中的任何溫度高的物質,總會一直慢慢的趨向於絕對零度,這是不可逆的,它普遍存在於很多領域!再比如說,我把耳機線整理整齊,然後放入口袋。但是,當我拿出來的時候,它又呈現出混亂的局面。自然界的力量總是想讓耳機線趨向於混亂狀態。



當軟件中的無序增長時,程序員們稱之爲“軟件腐爛”(software rot)。

導致軟件腐爛的原因有很多。

首要的就是破窗戶!

一扇破窗戶,只要有那麼一段時間不修理,就會漸漸給建築的居民帶來一種廢棄感——一種職權部門不關心這座建築的感覺。於是又一扇窗戶破了。人們開始亂扔垃圾...一段時間後,廢棄感變成了現實。

管束破窗戶、混亂塗畫和其他輕微違法事件減少了嚴重罪案的發生。

不要留着“破窗戶”(低劣的設計、錯誤決策、或是糟糕的代碼)不修。發現一個就修理一個。如果沒有足夠的時間進行適當的修理,就用木板把它釘起來。你可以添加註釋、或者顯示“未實現”消息,或者用虛設的數據加以替代。採取某種行動防止進一步的損壞,並說明情勢處在你的控制之下。整潔、運行良好的系統,一旦窗戶破裂,就相當迅速的惡化。還有其他的原因,但是與其他原因相比,置之不理都會更快的加速腐爛的進程。



你也許在想,沒有人有時間到處清理項目的所有碎玻璃。如果你繼續這麼想,你最好計劃找一個大型垃圾罐,或是搬到別處去。不要讓熵贏得勝利!

也就是說,每個人都有責任去補窗戶,發現一個,要麼釘起來,要麼補上。

名詞解釋:熵的本質是一個系統“內在的混亂程度”。它在控制論、概率論、數論、天體物理、生命科學等領域都有重要應用,在不同的學科中也有引申出的更爲具體的定義,按照數理思維從本質上說,這些具體的引申定義都是相互統一的,熵在這些領域都是十分重要的參量。



軟件熵(Software entropy)是指軟件的無序程度。軟件熵可用來說明軟件在經過不斷修改後,無序程度提高的現象。


儘管軟件開發幾乎不受任何物理定律的約束,熵(entropy)對我們的影響卻很大。熵是一個來自物理學的概念,指的是某個系統中的 “無序” 的總量,遺憾的是,熱力學定律保證了宇宙中的熵傾向於最大化,當軟件中的無序增長時,程序員們稱之爲 “軟件腐爛(software rot)”

很多元素可以崔進軟件腐爛,其中最重要的一個似乎是開發項目時的心理(或文化),即使你的團隊只有你一個人,你開發項目時的心理也可能是非常微妙的事情。儘管制定了最好的計劃,擁有最好的開發者,項目在其生命週期中仍可能遭遇毀滅和衰敗,而另一些項目,儘管遇到巨大的困難和接連而來的挫折,卻成功的擊敗自然的無序傾向,設法取得相當好的結果。

是什麼造成了這樣的差異?




在市區,有些建築漂亮而整潔,而另一些卻是破敗不堪的 “廢棄船隻”,爲什麼?犯罪和城市的衰敗,領域的研究者發現一種迷人的觸發機制,一種能夠很快將整潔、完整和有人居住的建築變成破敗廢棄物的機制:破窗戶

一扇破窗戶,只要有那麼一段時間不修理,就會漸漸給建築的居民帶來一種廢棄感 ---一種職權部門不關心這座建築的感覺。於是又一扇窗戶破了,人們開始亂扔垃圾,出現了亂塗亂畫。嚴重的結構損壞開始了,在相對較短的一段時間裏,建築就被損毀的超出業主願意修理的程度,而廢棄感變成了現實。

“破窗戶理論” 啓發了紐約和其他大城市的警察部門,他們對一些輕微的案例嚴加處理,以防止大案的發生。這起了作用:管束破窗戶、亂塗亂畫和其他輕微的違法事件減少了嚴重罪案的發生。

Don't live with Broken Windows

不要容忍破窗戶

不要留着 “破窗戶”(低劣的設計,錯誤的決策或是糟糕的代碼)不修。發現一個就修一個,如果沒有足夠的時間進行適當的修理,就用木板把它釘起來。或許你可以把問題的代碼加入註釋(comment out),或是顯示** “未實現”** 消息,或是用虛設的數據(dummy data)加以替代。採取某種行動防止進一步損壞,並說明情勢處在你的控制之下。




我們看過整潔、運行良好的系統,一旦窗戶開始破裂,就相當於迅速地惡化,還有其他一些因素能夠促生軟件腐爛,但與其他任何因素相比,置之不理都會更快地加速腐爛的進程。

你也許在想,沒有人有時間到處清理項目中的所有破玻璃。如果你繼續這麼想,你就最好計劃找一個大型垃圾罐,或者搬到別處去。不要讓熵贏得勝利。




滅火

作爲參照,讓我們講述Andy的一個熟人的故事。他是一個富的讓人討厭的富翁,擁有一所完美、漂亮的房子,裏面滿是無價的古董,藝術品,以及諸如此類的東西。有一天,一幅掛毯離他的臥室壁爐太近了一點,着了火。消防員衝進來救火 -- 和他的房子,但他們拖着粗大、骯髒的消防水管衝進房間門口卻停住了 -- 火在咆哮 -- 他們要在前門和着火之間鋪上墊子,他們不想弄髒地毯,這的確是一個極端的例子,但我們必須以這樣的方式對待軟件,一扇破窗戶、一段設計低劣的代碼、團隊必須在整個項目開發過程中加以忍受一項糟糕的管理決策 - 就足以是項目開開始衰敗。如果你發現自己在有好些破窗戶的項目裏工作,會很容易產生這樣的想法:“這些代碼的其餘部分也是垃圾,我只要照着做就行了”。項目在這之前是否一直很好、並沒有什麼關係:在最初得出 “破窗戶理論” 的一項實驗中,一輛廢棄的汽車放了一個星期,無人理睬,而一旦有一扇窗戶被打破,數小時之內車上的設備就被搶奪一空,車也被翻了個底朝天。

按照同樣的道理,如果你發現你所在的團隊和項目的代碼是否漂亮:編寫整潔、設計良好,並且很優雅,你就很可能會格外注意不去把它弄髒,就和那些消防員一樣,即使火在咆哮(最後預期、發佈時間、會展演示等等),你也不會想成爲第一個弄髒東西的人。

挑戰

通過調查你周邊的計算 “環境”,幫助增強你的團隊能力,選擇兩或三扇 “破窗戶”,並與你同事討論問題何在,以及怎樣修理它們。

你能否說出某扇窗戶何時破的?你的反應是什麼?如果它人的決策所致,或者是管理部門的指示,你能做些什麼?



熵增定律

熵的概念最早起源於物理學,用於度量一個熱力學系統的無序程度。熱力學第二定律,又稱“熵增定律”,表明了在自然過程中,一個孤立的系統總是從最初的集中、有序的排列狀態,趨向於分散、混亂和無序;當熵達到最大時,系統就會處於一種靜寂狀態。

通俗的講:系統的熵增過程,就是由原始到死亡的過程。“熵”是“活躍”的反義詞,代表負能量。

非生命,比如物質總是向着熵增演化,屋子不收拾會變亂,手機會越來越卡,耳機線會凌亂,熱水會慢慢變涼,太陽會不斷燃燒衰變……直到宇宙的盡頭——熱寂。

在軟件開發、維護過程中。軟件的生命力總是從最初的理想狀態,逐步趨向於複雜、混亂和無序狀態發展,直到軟件不可維護而被迫下線或重構。這種損壞軟件質量的因素的逐步增長,叫做軟件的熵增現象。



系統複雜性

表象

代碼混亂、新人不易上手

代碼高度冗餘,複用性低,開發效率低

擴展和修改困難,牽一髮動全身

業務數據錯亂

程序性能低下

系統難以移置

BUG率居高不下

深層原因

變更放大

認知負荷

未知的未知



複雜性的原因

複雜性是由兩件事引起的:依賴性和模糊性。

1、依賴關係

依賴關係是軟件的基本組成部分,不能完全消除。實際上,我們在軟件設計過程中有意引入了依賴性。每次編寫新類時,都會圍繞該類的 API 創建依賴關係。但是,軟件設計的目標之一是減少依賴關係的數量,並使依賴關係保持儘可能簡單和明顯。

2、模糊性

當重要的信息不明顯時,就會發生模糊。

一個簡單的例子是一個變量名,它是如此的通用,以至於它沒有攜帶太多有用的信息(例如,時間)。或者,一個變量的文檔可能沒有指定它的單位,所以找到它的惟一方法是掃描代碼,查找使用該變量的位置。

晦澀常常與依賴項相關聯,在這種情況下,依賴項的存在並不明顯。例如,如果向系統添加了一個新的錯誤狀態,可能需要向一個包含每個狀態的字符串消息的表添加一個條目,但是對於查看狀態聲明的程序員來說,消息表的存在可能並不明顯。

不一致性也是造成不透明性的一個主要原因:如果同一個變量名用於兩個不同的目的,那麼開發人員就無法清楚地知道某個特定變量的目的是什麼。

3、依賴性和模糊性的積累

複雜性不是由單個災難性錯誤引起的;它堆積成許多小塊。單個依賴項或模糊性本身不太可能顯着影響軟件系統的可維護性。之所以會出現複雜性,是因爲隨着時間的流逝,成千上萬的小依賴性和模糊性逐漸形成。最終,這些小問題太多了,以至於對系統的每次可能更改都會受到其中幾個問題的影響。

降低複雜性的方法

1、日常開發留出一點戰略規劃時間

大多數程序員日常以戰術編程的心態來進行軟件開發。例如新功能或錯誤修復。乍一看,這似乎是完全合理的:還有什麼比編寫有效的代碼更重要的呢?但是戰術編程幾乎不可能產生出良好的系統設計。

與之相對應的是戰略規劃,成爲一名優秀的軟件設計師的第一步是要意識到僅工作代碼是不夠的。儘管代碼當然必須工作,但不應將“能跑通的代碼”視爲主要目標。戰略設計的主要目標必須是製作出出色的設計,考慮後續的可維護性及擴展性。

戰略性編程需要一種投資心態。儘管前提投入會比戰術編程花費更多的時間,但隨着系統的迭代,戰略編程的優勢就開始逐漸顯現。

當然既然是投資,就要考慮投入產出比,不應該吹毛求疵,只要發現一點不合理的地方就整體大重構。推薦的方式小步快跑的方式,在日常開發中留出5%-10%的時間來做戰略設計。

2、模塊的設計

開發一個新模塊,如果有不可避免的複雜性。兩種設計思路哪個更好:1、應該讓模塊用戶處理複雜性,2、應該在模塊內部處理複雜性?如果複雜度與模塊提供的功能有關,則第二個答案通常是正確的答案。

作爲開發人員,很容易以相反的方式行事:解決簡單的問題,然後將困難的問題推給其他人。如果出現不確定如何處理的條件,最簡單的方法是引發異常並讓調用方處理它。這樣的方法短期內會使您的生活更輕鬆,但它們會加劇複雜性。大多數模塊擁有的用戶多於開發人員,因此此模塊還會有許多人來維護。作爲模塊開發人員,您應該努力使模塊用戶的生活儘可能輕鬆,即使這對您來說意味着額外的工作。另一種更好的方法是,模塊具有簡單的接口比簡單的實現更爲重要。

模塊是設計應該是深的,最好的模塊是那些其接口比其實現簡單得多的模塊。這樣的模塊具有兩個優點。1、一個簡單的接口可以將模塊強加於系統其餘部分的複雜性降至最低。2、如果以不更改其接口的方式修改了一個模塊,則該修改不會影響其他模塊。如果模塊的接口比其實現簡單得多,則可以在不影響其他模塊的情況下更改模塊的許多方面。

3、如何編寫註釋

編寫註釋的原因是,使用編程語言編寫的語句無法捕獲編寫代碼時開發人員想到的所有重要信息。註釋記錄了這些信息,以便後來的開發人員可以輕鬆地理解和修改代碼。註釋的指導原則是,註釋應描述代碼中不明顯的內容。

註釋的最重要原因之一是抽象,其中包括許多從代碼中看不到的信息。抽象的思想是提供一種思考問題的簡單方法,但是代碼是如此詳細,以至於僅通過閱讀代碼就很難看到抽象。註釋可以提供一個更簡單,更高級的視圖(“調用此方法後,網絡流量將被限制爲每秒 maxBandwidth 字節”)。即使可以通過閱讀代碼推斷出此信息,我們也不想強迫模塊用戶這樣做:閱讀代碼很耗時,並且迫使他們考慮很多不需要使用的信息模塊。開發人員應該能夠理解模塊提供的抽象,而無需閱讀其外部可見聲明以外的任何代碼。

4、重視命名

名稱是一種抽象形式:名稱提供了一種簡化的方式來考慮更復雜的基礎實體。良好的名字是一種文檔形式:它們使代碼更易於理解。它們減少了對其他文檔的需求,並使檢測錯誤更加容易。相反,名稱選擇不當會增加代碼的複雜性,並造成可能導致錯誤的歧義和誤解。



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