好的程式碼很容易刪除!

banq發表於2018-12-24

程式設計是從浪費生命中學到的可怕教訓,編寫易於刪除但不易擴充套件的程式碼。

“每一行程式碼都是在沒有理由的情況下編寫的,有自己的弱點,並且偶然間會被刪除” Jean-Paul Sartre的ANSI C程式設計。

編寫的每一行程式碼都需要付出代價:維護。為避免大量程式碼成本消耗,我們構建了可重用的軟體,程式碼重用的問題在於它會妨礙以後改變主意

您擁有的API消費者越多,您引入更改時必須重寫的程式碼越多。同樣,您依賴第三方api的次數越多,您在更改時就會受到的影響越大。管理程式碼如何組合在一起,或哪些部分依賴於其他部分,是大型系統中的一個重要問題,隨著專案的老化,它會變得更加困難。

如果我們將“程式碼行”看作“每行的付出花費與成本”,那麼當我們刪除程式碼行時,我們就會降低維護成本。我們應該嘗試構建一次性軟體,而不是構建可重用的軟體。

我不需要告訴你刪除程式碼比編寫程式碼更有趣。

編寫易於刪除的程式碼:重複自己以避免建立依賴關係,但不要重複自己來管理它們。

對程式碼進行分層:從簡單易用但笨拙的部分構建簡單易用的API。拆分程式碼:將難以編寫和可能更改的部分與其餘程式碼隔離開來,並相互隔離。不要硬編碼每個選擇,也許允許在執行時更改一些。但是不要試圖同時做所有這些事情,也許不要在第一時間編寫這麼多程式碼。

第0步:不要編寫程式碼
最容易刪除的程式碼是您首先應該避免編寫的程式碼,只編寫重要的!
...

第1步:複製貼上程式碼
構建可重用程式碼是事後更容易做到的事情,在程式碼庫中使用了幾個例子,而不是你以後可能想要的遠見。從好的方面來說,你可能只是使用檔案系統重新使用了很多程式碼,為什麼要擔心這麼多?一點點冗餘是健康的。
最好複製貼上程式碼幾次,而不是建立庫函式,只是為了掌握如何使用它。一旦你建立了一個共享API,你就會變得更難。
呼叫您的函式的程式碼將依賴於其背後的實現的有意和無意行為。使用您的功能的程式設計師將不依賴於您記錄的內容,而是他們觀察到的內容。
刪除函式內部的程式碼比刪除函式更簡單。

第2步:不要複製貼上程式碼
當你複製和貼上足夠的東西時,也許是時候把它拉到一個功能了。

程式碼相對於某個應用程式或專案的具體細節程度越低,重用越容易,更改或刪除的可能性就越小。

您不會刪除的程式碼的好例子是列表,雜湊表和其他集合。不是因為它們通常具有非常簡單的介面,而是因為它們隨著時間的推移不會增加範圍。

第3步:編寫更多樣板檔案
編寫庫可避免複製貼上,但我們經常最終透過複製貼上編寫更多程式碼來使用它們,我們給它一個不同的名稱:樣板檔案。它很像複製貼上,不同的是每次貼上到不同的地方時會更改一些程式碼。

第4步:不要編寫樣板
構建簡單易用的API就是將樣板檔案轉換為庫。
構建令人愉快的API並構建可擴充套件的API通常彼此不一致。這種關注點的分離使我們能夠讓一些使用者滿意,而不會讓其他使用者滿意。
當你開始使用一個好的API時,分層是最容易的,但是在一個糟糕的API之上編寫一個好的API是非常困難的。良好的API設計時會對使用它的程式設計師表示同情,分層意識到我們不能立刻取悅所有人。
分層不是關於編寫我們以後可以刪除的程式碼,而是使用難以刪除的程式碼(不會因為業務邏輯而汙染它)。

第5步:編寫一大堆程式碼
對於更有趣或更具創造性的努力尤其如此。如果您正在編寫你的第一個遊戲:不要編寫引擎。同樣,在編寫應用程式之前不要編寫Web框架。第一次去寫一個爛攤子。除非你是通靈者,否則你不會知道如何將它分開。
Monorepos是一個類似的權衡:你不會知道如何提前拆分程式碼,坦率地說,一個大錯誤比20個緊密耦合錯誤更容易部署。有時刪除一個大錯誤比嘗試刪除18個較小的交錯錯誤更容易。

成為一名專業軟體開發人員是不斷積累一系列遺憾和錯誤。而不是說可以從成功中學到什麼(banq注:因為你從來沒有成功過,這個和你在學校中考試100分不斷成功的積累是不同的)。並不是你知道了什麼是好的程式碼,而是糟糕的程式碼造成傷痕在你的腦海中是新鮮的。(banq注:跌了跟頭知道好好走路)

無論如何,專案最終都會失敗或成為遺留程式碼。寫十個大便程式碼到時擦除起來還不如一次性擦一塊糞便更快更方便。
刪除所有程式碼比分段刪除它更容易。

第6步:將程式碼分解成碎片
大泥球是最容易建造的,但維護成本最高。(banq注:泥球的意思是所有泥土都粘在一起,代指依賴關係,蜘蛛網的依賴)。原理可以整體上更容易一次性刪除的東西現在不可能分段刪除。(因為都粘在一起了)
我們將程式碼分層以分離職責,從特定平臺到特定領域,我們需要找到一種方法來分離邏輯。

雖然單一責任原則暗示“每個模組應該只處理一個難題”,但更重要的是“每個難題只由一個模組處理”

當模組做兩件事時,通常是因為改變一部分需要改變另一部分。擁有一個簡單介面的可怕元件通常比兩個需要仔細協調的元件更容易。

一個你可以刪除部分同時不需要不重寫其他部分的系統通常才被稱為鬆散耦合。

即使對變數進行一次硬編碼也可以是松耦合,或者在變數上使用命令列標誌。松耦合是指能夠在不改變太多程式碼的情況下改變主意。

HTTP也有松耦合的例子:在HTTP伺服器前放置一個快取。將影像移動到CDN,只需更改指向它們的連結即可。沒有打破瀏覽器。

HTTP的錯誤程式碼是松耦合的另一個例子:Web伺服器的常見問題具有唯一程式碼。當您收到400錯誤時,再次執行此操作將獲得相同的結果。500可能會改變。因此,HTTP客戶端可以代表程式設計師處理許多錯誤。

在將軟體分解為較小的部分時,必須考慮軟體如何處理故障。這樣做說起來容易做起來難。

Erlang / OTP在選擇處理故障方面相對獨特:監督樹。粗略地說,Erlang系統中的每個程式都由一個主管啟動並觀察。當程式遇到問題時,它會退出。當程式退出時,由主管重新啟動。(這些管理程式由載入程式啟動,當主管遇到故障時,它由載入程式重新啟動)

關鍵的想法是,它快速失敗並重新啟動比處理錯誤更快。像這樣的錯誤處理可能看起來是違反直覺的,透過在錯誤發生時放棄來獲得可靠性,但是再次關閉事物具有抑制瞬態故障的訣竅。

錯誤處理和恢復最好在程式碼庫的外層完成。這被稱為端到端原則。端到端原則認為,在連線的遠端處理故障比在中間的任何地方更容易。如果你有任何處理,你仍然需要做最後的頂級檢查。如果每個層都必須處理錯誤,那麼為什麼還要在內部處理它們?
檔案系統和資料庫都是遠端儲存的更好例子。使用檔案系統,您可以使用一組固定的操作,但可以操作多個物件。
儘管SQL看起來像是一個比檔案系統更廣泛的介面,但它遵循相同的模式。集合上的許多操作以及要操作的大量行。雖然您不能總是將一個資料庫替換為另一個資料庫,但透過任何自制查詢語言更容易找到適用於SQL的資料庫。
鬆散耦合的其他示例是具有中介軟體或過濾器和管道的其他系統。例如,Twitter的Finagle使用通用API進行服務,這允許通用超時處理,重試機制和身份驗證檢查毫不費力地新增到客戶端和伺服器程式碼中。

首先,我們對程式碼進行分層,但現在其中一些層共享一個介面:一組通用的行為和操作,具有各種實現。鬆散耦合的良好示例通常是均勻介面的示例。

鬆散耦合的程式碼不一定容易刪除,但更容易更換,也更容易更改。

第7步:繼續編寫程式碼
能夠在不處理舊程式碼的情況下編寫新程式碼,可以更輕鬆地嘗試新想法。
谷歌瀏覽器是他們帶來的好處的一個壯觀的例子。他們發現,保持常規釋出週期最困難的部分是合併長期存在的功能分支所花費的時間。
設定功能標誌是以後改變主意的一種方法。雖然功能標記被視為實驗功能的方法,但它們允許您在不重新部署軟體的情況下部署更改。
透過能夠在不重新編譯的情況下開啟和關閉新程式碼,可以將更大的更改分解為更小的合併,而不會影響現有程式碼。由於新功能在同一程式碼庫中出現在前面,因此當長時間執行的功能開發會影響程式碼的其他部分時,它會更加明顯。
功能標誌不僅僅是命令列開關,它還可以將功能版本與合併分支分離,並將功能版本與部署程式碼分離。能夠在執行時改變主意變得越來越重要,因為推出新軟體可能需要數小時,數天或數週。詢問任何SRE:任何可以在夜間叫醒你的系統都是值得在執行時控制的系統。

你不是在迭代,而是有一個反饋迴圈。您正在構建可重複使用的模組,而不是將元件隔離以進行更改。處理變更不僅僅是開發新功能,而是擺脫舊功能。

我所討論的策略 - 分層,隔離,通用介面,組合 - 不是要編寫好的軟體,而是如何構建可以隨時間變化的軟體。

好的程式碼不是第一次就做好。好的程式碼只是不會妨礙遺留程式碼。

好的程式碼很容易刪除。
 

相關文章