魔鬼在細節中

2021-06-28 18:36:09 字數 2726 閱讀 9651

-------------------------------

轉於自己在公司的blog:

最近一直擔心dubbo分布式服務框架後續如果維護人員增多或變更,會出現質量的下降,

我在想,有沒有什麼是需要大家共同遵守的,

根據平時寫**時的一習慣,總結了一下在寫**過程中,尤其是框架**,要時刻牢記的細節,

可能下面要講的這些,大家都會覺得很簡單,很基礎,但要做到時刻牢記,

在每一行**中都考慮這些因素,是需要很大耐心的,

大家經常說,魔鬼在細節中,確實如此。

1. 防止空指標和下標越界

這是我最不喜歡看到的異常,尤其在核心框架中,我更願看到資訊詳細的引數不合法異常,

這也是乙個健狀的程式開發人員,在寫每一行**都應在潛意識中防止的異常,

基本上要能確保一次寫完的**,在不測試的情況,都不會出現這兩個異常才算合格。

2. 保證執行緒安全性和可見性

對於框架的開發人員,對執行緒安全性和可見性的深入理解是最基本的要求,

需要開發人員,在寫每一行**時都應在潛意識中確保其正確性,

因為這種**,在小併發下做功能測試時,會顯得很正常,

但在高併發下就會出現莫明其妙的問題,而且場景很難重現,極難排查。

3. 盡早失敗和前置斷言

盡早失敗也應該成為潛意識,在有傳入引數和狀態變化時,均在入口處全部斷言,

乙個不合法的值和狀態,在第一時間就應報錯,而不是等到要用時才報錯,

因為等到要用時,可能前面已經修改其它相關狀態,而在程式中很少有人去處理回滾邏輯,

這樣報錯後,其實內部狀態可能已經混亂,極易在乙個隱蔽分支上引發程式不可恢復。

4. 分離可靠操作和不可靠操作

這裡的可靠是狹義的指是否會丟擲異常或引起狀態不一致,

比如,寫入乙個執行緒安全的map,可以認為是可靠的,

而寫入資料庫等,可以認為是不可靠的,

開發人員必須在寫每一行**時,都注意它的可靠性與否,

在**中盡量劃分開,並對失敗做異常處理,

並為容錯,自我保護,自動恢復或切換等補償邏輯提供清晰的切入點,

保證後續增加的**不至於放錯位置,而導致原先的容錯處理陷入混亂。

5. 異常防禦,但不忽略異常

這裡講的異常防禦,指的是對非必須途徑上的**進行最大限度的容忍,

包括程式上的bug,比如:獲取程式的版本號,會通過掃瞄manifest和jar包名稱抓取版本號,

這個邏輯是輔助性的,但**卻不少,初步測試也沒啥問題,

但應該在整個getversion()中加上乙個全函式的try-catch列印錯誤日誌,並返回基本版本,

因為getversion()可能存在未知特定場景異常,或被其他的開發人員誤修改邏輯(但一般人員不會去掉try-catch),

而如果它丟擲異常會導致主流程異常,這是我們不希望看到的,

但這裡要控制個度,不要隨意try-catch,更不要無聲無息的吃掉異常。

6. 縮小可變域和盡量final

如果乙個類可以成為不變類(immutable class),就優先將它設計成不變類,

不變類有天然的併發共享優勢,減少同步或複製,而且可以有效幫忙分析執行緒安全的範圍,

就算是可變類,對於從建構函式傳入的引用,在類中持有時,最好將字段final,以免被中途誤修改引用,

不要以為這個欄位是私有的,這個類的**都是我自己寫的,不會出現對這個欄位的重新賦值,

要考慮的乙個因素是,這個**可能被其他人修改,他不知道你的這個弱約定,final就是乙個不變契約。

7. 降低修改時的誤解性,不埋雷

前面不停的提到**被其他人修改,這也開發人員要隨時緊記的,

這個其他人包括未來的自己,你要總想著這個**可能會有人去改它,

我應該給修改的人一點什麼提示,讓他知道我現在的設計意圖,

而不要在程式裡面加潛規則,或埋一些容易忽視的雷,

比如:你用null表示不可用,size等於0表示黑名單,

這就是乙個雷,下乙個修改者,包括你自己,都不會記得有這樣的約定,

可能後面為了改某個其它bug,不小心改到了這裡,直接引爆故障。

對於這個例子,乙個原則就是永遠不要區分null引用和empty值。

8. 提高**的可測性

這裡的可測性主要指mock的容易程度,和測試的隔離性,

至於測試的自動性,可重複性,非偶然性,無序性,完備性(全覆蓋),輕量性(可快速執行),

一般開發人員,加上junit等工具的輔助基本都能做到,也能理解它的好處,只是工作量問題,

這裡要特別強調的是測試用例的單一性(只測目標類本身)和隔離性(不傳染失敗),

現在的測試**,過於強調完備性,大量重複交叉測試,

看起來沒啥壞處,但測試**越多,維護代價越高,

經常出現的問題是,修改一行**或加乙個判斷條件,引起100多個測試用例不通過,

時間一緊,誰有這個閒功夫去改這麼多形態各異的測試用例?

久而久之,這個測試**就已經不能真實反應**現在的狀況,很多時候會被迫繞過,

最好的情況是,修改一行**,有且只有一行測試**不通過,

如果修改了**而測試用例還能通過,那也不行,表示測試沒有覆蓋到,

另外,可mock性是隔離的基礎,把間接依賴的邏輯遮蔽掉,

可mock性的乙個最大的殺手就是靜態方法,盡量少用。 

魔鬼在細節中

最近一直擔心dubbo分布式服務框架後續如果維護人員增多或變更,會出現質量的下降,我在想,有沒有什麼是需要大家共同遵守的,根據平時寫 時的一習慣,總結了一下在寫 過程中,尤其是框架 要時刻牢記的細節,可能下面要講的這些,大家都會覺得很簡單,很基礎,但要做到時刻牢記,在每一行 中都考慮這些因素,是需要...

魔鬼在細節中

最近一直擔心dubbo分布式服務框架後續如果維護人員增多或變更,會出現質量的下降,我在想,有沒有什麼是需要大家共同遵守的,根據平時寫 時的一習慣,總結了一下在寫 過程中,尤其是框架 要時刻牢記的細節,可能下面要講的這些,大家都會覺得很簡單,很基礎,但要做到時刻牢記,在每一行 中都考慮這些因素,是需要...

魔鬼在細節中

今天,寫程式的時候,一執行,機子便癱了 我寫了迴圈,覺得肯定是因為寫成死迴圈才這樣的,可怎麼找也找不到 重啟了兩次之後,我打算把那一段 切下來,重寫 寫著寫著,才發現竟然把乙個if寫成了while,鄙視了自己半天 有時候,嚴重的後果,不一定源於乙個顯眼且重大的錯誤,很多時候是由於乙個小小疏忽,不起眼...