一道題識別優秀的程式設計師

ruben發表於2016-06-02

在我的面試工具箱中,有一把錘子是我的最愛。一旦在這道面試題上捕捉到積極訊號,無論在其他問題上的反饋多麼不靠譜,我都會想法設法把候選人收進來。

期望的結果只有一個,而錯誤的可能性卻有千千萬萬

這道題是關於濫用防衛性程式設計的。和大部人的直覺相反,生產中絕大部分所謂防衛性程式碼(錯誤處理程式碼)自身都是垃圾,根本沒有存在的必要,而且錯誤成堆,一旦不幸執行就會製造災難!繞過這個坑的工程師寫出來的程式碼通常比一般程式碼要少三分之一,圈複雜度會下降一半(不是瞎掰,我是通過度量前後版本的差異得到這個資料的),而可靠性反而會有驚人的提高。

關於防衛性程式設計,絕大部分軟體工程師的成長經歷是這樣的:

階段一, 沒有防衛

這個階段很快就會過去,因為有無數的書本和”前輩”在提示防衛性編碼的必要性。

階段二, 瘋狂防衛

無論是模組之間,還是模組內部,處處充滿檢查,首先是檢查引數,其次是檢查返回值和出參;最後,給自己寫的函式通通加上指示成功失敗的返回碼。

如果讀者諸君有幸參加過程式碼評審會,對階段二的程式碼應該不會陌生,看看到底哪裡不對勁?

只管彙報,不管處理,不了了之

這些氾濫的防衛性程式碼有一個共同的特點–只管檢查彙報,不管處理。所謂的處理就是一層一層的return錯誤碼,典型的懶政行為,老子眼界不夠高,做不了決定,把皮球丟給上級機關。上級機關面對來自不同下屬的千奇百怪的錯誤碼,他也沒轍,只好再丟給上上級機關。這樣層層上報,一直彙報到習大大。你覺得習大大要維護多大的隊伍才能搞得定?人手再多也搞不定,因為用來分析錯誤的上下文資訊已經在層層彙報中丟失了。所以別看費了好大力氣彙報錯誤碼,最後除了一死了之或者睜隻眼閉隻眼也沒有更好的辦法。

內外不辨,親疏不分

防衛是應該的,但不應該處處為營。國界線上要有邊防軍巡邏,但是省界呢?縣界呢?設防是有成本的,在軟體上就體現為交複雜度稅。同一個模組內部,假設A呼叫B,如果B認為A傳給它空指標(空物件)是一個錯誤,那麼A應該設計為永遠不會把空指標傳遞給B,而不是在B處設防。模組內部設防,就好比兩口子同床異夢,精力都內耗了。

人格分裂,自相矛盾

設計這種程式碼的人一方面認為自己思維縝密,步步設防,另一方面認為自己白痴到底,所以要步步設防,左手要防著右手。左右手互搏的程式碼散佈得紛紛揚揚,幾乎無法辨認有限的功能程式碼。

顧頭不顧尾,屁股擦不乾淨

只要評審得足夠仔細,總能發現這些防衛性程式碼的漏洞,要麼是檔案沒關閉,要麼是記憶體沒釋放。這是必然的,因為一個正常人根本沒腦力搞定這麼多細節(你要考慮錯誤處理程式碼也是層層巢狀,充滿了排列組合的)。考慮到天量的路徑組合和邏輯的自相矛盾,你也沒辦法設計測試用例來驗證它。這就是為什麼真的一不小心命中的話,結局總是很悲慘的原因。寫到這裡,我想起歷史上重大的安全事故調查報告,事故之所以能發生,是因為多道安全閘門同時失效,而軟體故障會更加可怕,因為後面的閘門本身就是炸彈。

可惜的是,無數工程師一腳踏入階段二這個大坑後再也沒有爬出來。

我每次評審程式碼,上手第一刀就是檢查錯誤處理。邊界處的錯誤是否都被檢查?錯誤處理是否遵從系統規定的一致策略?模組內部是否存在周伯通?一旦作者把錯誤處理的評審意見處理完畢,剩下的程式碼基本上就身段苗條、脈絡清晰了,因為用來實現正常功能的程式碼真的不會很多。

說了這麼多,那麼階段三到底又是什麼樣的呢?

邊界清晰,禦敵於國門之外

在模組的邊界處,無論別人呼叫你還是你呼叫別人,一定要明確約定呼叫規範,一旦違反規範,立即啟用錯誤處理流程(注意不是防衛流程),不要讓錯誤在自己的模組內部流竄。

不給錯誤做二傳手

如果發現錯誤,讓錯誤到此為止,非必要不接力傳遞錯誤碼

當斷則斷,早死早超生

所有程式的正確執行都是依賴於一定的前提條件的,如果你發現外部模組不工作了,遵從系統的錯誤處理策略,該報異常就報異常,該立即退出就退出,重要的是蒐集好現場的證據。一味的容錯處理既不能解決問題還會掩蓋問題,導致錯誤擴散,變形,拖延定位問題的時機。

和諧社會,簡單單純

在模組內部,只有不言自明的約定,沒有周伯通似的左右手互搏。好比烏龜,龜殼堅硬,但龜殼內部是一個柔軟的世界。

這樣的程式碼,模組內部幾乎沒有用於實現需求以外的程式碼,很容易閱讀,僅通過黑盒測試就能實現程式碼覆蓋。

階段三的程式設計師靠譜在哪裡?

有些工程師似乎天生就跨過了階段二,我遇到一個,當時他還是在校研究生,現在已經創業有成了。我發現這些工程師一般有如下特點:

  • 刨根究底,不滿足於似是而非,對自己程式所依賴的外部環境和自己模組的職責有清晰的認識
  • 有全域性觀念,站在整個軟體的角度設計單個模組,站在整個系統的角度設計軟體
  • 傾向於寫”簡單而明顯沒有錯誤的程式”

正確的錯誤處理設計需要對軟體和系統,模組和整體軟體之間的職責劃分有清晰的認識,階段三是一個合格軟體架構師的起點。

發現這樣的候選人也不是太麻煩,讓他面試的時候隨身帶一份程式碼來就可以了(不是每一個工程師都有在github上留程式碼的習慣)。不過,最有價值的實踐是通過程式碼評審把工程師從階段二推向階段三。

相關文章