記錄一下工作半年之后發現的現象和對應的思考。
- 項目最終一定會成為“屎山”
- 百分之八十的代碼是特殊或異常情況處理
- 參數校驗是一把雙刃劍
項目最終一定會成為“屎山“
大學畢業之前就知道程序員最頭疼的事是維護老項目,尤其是代碼質量很差的”屎山”。很幸運的是,工作后遇到的第一個項目就是維護一個屎山,這個項目是龐大復雜系統中的一個模塊,由於大版本的更新迭代導致現有功能已無法使用,必須進行重新適配。更幸運的是,領導給了我極大的自由度和很長時間自由發揮。
剛接手時,整個項目真是一團亂麻,定義最主要業務邏輯的類文件有四千行、其中還有一個九百多行的 run 函數,另外還有一半的類在迭代過程中已經不再使用但是沒有刪除。截止到第三輪系統測試、整個項目持續了兩個月,全程由我一個人完成,經歷了從日志文件和主要代碼中倒推出業務邏輯狀態機、通讀所有源碼刪除無用部分、調整混雜在一起的各層代碼、優化代碼等過程。下方兩張圖分別是剛接手是代碼掃描結果和目前的代碼掃描結果,可以看出來改進了很多。
和無法違背的熱力學第二定律一樣,我認為只要一個項目還有維護的價值、還在迭代中,它的代碼質量就一定會越來越差。有以下兩個主要原因:
- 有維護需要的項目,很可能會不斷的加入新的功能,而再有經驗的架構師在最初設計時也不可能考慮到所有的情況。從這個角度來說,設計上的缺陷是一定存在的,沒有人敢篤定自己的設計萬無一失,設計之初只能盡可能的提高設計質量。
- 軟件開發團隊自身就是復雜的,每個人水平參差不齊、會有人離開有人加入,老員工寫下一段代碼時經歷了什么樣的思考和辯證,可能永遠也沒機會讓后來加入的新人了解。這種情況下,維護老項目的新員工就可能破壞原本清晰的代碼結構。
百分之八十的代碼是特殊或異常情況處理
理想情況下,用戶會用最主流的瀏覽器、把正確賬號密碼填在正確的位置然后登陸。實際情況中,用戶可能會用 IE 瀏覽器、可能會把用戶米密碼填反中,甚至訪問應用的都不是活生生的用戶。
於是,前端需要做表單驗證以驗證用戶的輸入,做多瀏覽器適配以方便用戶;而后端需要做入參校驗防止后台程序被前端同事和爬蟲搞崩。下面是作為一個 Java 語言后端開發者對於一些可能的異常情況的總結。
- Java 的基礎數據類型含有默認值,因此 DAO 類中的基本數據類型要使用包裝類,避免數據庫中相關值實際為 null,查詢結果卻為 0 的情況發生。
- 除了考慮數據類型自身的范圍限制避免溢出的情況以外,在特定的業務場景中,數據類型的值可能含有特定范圍,例如用於計數時 int(或 long)不可能為復數等。
- 字符串類型的特殊值有 null 和空字符串,很多情況下這兩個特殊值會觸發 Bug,但有些特殊情況時又是業務邏輯息息相關的,需要謹慎處理。
- 字符串中的轉義字符處理:前后端數據交互時,如果使用 JSON 格式需要對用戶輸入的
{
、}
、"
、,
等特殊符號進行轉義;如果使用 XML 格式需要對用戶輸入的<
、>
、"
等特殊符號進行轉義。
參數校驗是一把雙刃劍
在維護前文提到的項目時,有一個后端接口查詢經常超時,導致前端總是彈出“連接超時“的彈窗警告。這是一個統計服務器資源使用量的功能,前端調用接口查詢到結果后會將數據渲染為一個折線圖,展示過去一段時間內相關資源的占用情況。經過分析,發現該接口調用最耗時的過程是和主控節點進行 MQ 通訊進行參數校驗。與產品經理溝通后認為這個接口具有調用頻繁、入參固定、需要快速返回的特點,而用戶使用時關心的是某一時間段的資源占用的變化情況、非特定時間點的瞬時數據。后來解決方案是刪除了一大段的條件判斷,使用 try-catch 捕獲了一個通用異常,發生異常時直接返回上一次的統計結果。
由此可見,參數校驗也是一把雙刃劍,提高程序可靠性的代價就是執行效率變慢,很多時候需要根據實際情況在性能和安全性之間做出取舍。下面關於參數校驗的規范摘自《阿里巴巴 Java 開發手冊》。
【參考】下列情形,需要進行參數校驗:
- 調用頻次低的方法。
- 執行時間開銷很大的方法。此情形中,參數校驗時間幾乎可以忽略不計,但如果因為參數錯誤導致中間執行回退、或者錯誤,那得不償失。
- 需要極高穩定性和可用性的方法。
- 對外提供的開放接口,不管是RPC/API/HTTP接口。
- 敏感權限入口。
【參考】下列情形,不需要進行參數校驗:
- 極有可能被循環調用的方法。但在方法說明里必須注明外部參數檢查要求。
- 底層調用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數錯誤不太可能到底層才會暴露問題。一般 DAO 層與 Service 層都在同一個應用中,部署在同一台服務器中,所以 DAO 的參數校驗,可以省略。
- 被聲明成private只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參數已經做過檢查或者肯定不會有問題,此時可以不校驗參數。