用麵包工廠理解「乾淨」#
Joel 以他在以色列麵包工廠的工作經驗開場:外行人無法判斷工廠是否乾淨,但待了兩個月後你就能一眼看出問題。程式碼也是一樣的——只有熟悉之後,你才能看待「乾淨」的含義。
程式設計師的四種境界#
- 分不清什麼是乾淨的代碼、什麼是不乾淨的
- 對乾淨代碼有膚淺的認識,主要看是否符合代碼書寫規範(coding style)
- 能找出隱藏的不乾淨代碼,在乾淨的表面之下發現蛛絲馬跡
- 精心構建代碼,發揮洞察力,將它們寫得清晰易懂、不容易出錯
第四種境界是一門真正的藝術——透過人為發明一套書寫規範,使得錯誤在顯示屏上變得易見,從而讓代碼更健壯。
XSS 漏洞範例#
以 Cross-Site Scripting(XSS)漏洞為例,說明為什麼需要讓錯誤的代碼一眼就能被看出來:
- 使用者輸入的字串必須經過
Encode()編碼後才能輸出 - 方案 #1:讀取時立刻編碼——但如果要儲存到資料庫,編碼後的 HTML 會造成麻煩
- 方案 #2:輸出時才編碼——但有些 HTML 片段(如
<br>)不能被編碼
這兩個方案都有缺陷。如果找不到有效的書寫規範,代碼中就會散布著不知道是否已被編碼的字串變數,根本無法一一追蹤。
正確的解決方案#
制定變數命名規範:
- 來自使用者的不安全字串 → 變數名以
us開頭(unsafe string) - 已經 HTML 編碼或已知安全的字串 → 變數名以
s開頭(safe string)
us = UsRequest("name")
sName = SFromUs(recordset("usName"))
WriteS sName有了這套規範,只要檢查等號兩邊的前綴是否一致,就能知道是否出錯。看到
Write usXXX立刻可以判斷出這行代碼有問題。做上三個月,你的眼睛會產生自動識別能力。
匈牙利命名法的真正含義#
應用型匈牙利命名法(Apps Hungarian)#
- 發明人是微軟程式設計師 Charles Simonyi,他也領導了世界上第一個 WYSIWYG 文字處理軟體 Word 的開發
- 變數前綴表示的是資料的種類(kind),而非編譯器的資料類型
- 例如:
us= 不安全字串、s= 安全字串、rw= 行(row)、col= 列(column)、ix= 索引值、c= 計數器、d= 差額
系統型匈牙利命名法(Systems Hungarian)#
- 後來被 Windows 團隊文檔作者誤解,前綴變成了表示變數的資料類型(如
l= long、dw= double word) - 這完全曲解了 Simonyi 的原意,因為編譯器本身就能做類型檢查
- 最終導致「大暴動」——程式設計師們覺得這種命名法煩得要死又毫無用處
微軟在 .NET 發布時正式表態「不推薦使用匈牙利命名法」,但他們指的是已被誤解的系統型。應用型匈牙利命名法仍然極其有價值——它加強了代碼之間的聯結,讓錯誤的代碼容易現形。
一條通用規則#
讓錯誤的代碼能被一眼看出,前提是正確的東西在顯示屏上必須緊挨在一起:
- 盡量將函數寫得簡短
- 變數宣告的位置離使用的位置越近越好
- 不要使用巨集(macro)來創建自己的程式語言
- 不要使用
goto - 不要讓右括號與對應的左括號之間距離超過一個顯示屏
關於異常處理#
Joel 也對異常處理(exception)提出批評:異常處理使得代碼關聯(collocation)消失了——為了知道某一行代碼是否運行正常,你不得不去其他地方尋找答案。
讓錯誤的代碼顯而易見是一種很好的實踐,但未必是所有安全問題的最佳解決方案。Joel 強烈建議制定代碼書寫規範,讓錯誤的代碼更容易被發現——每一次程式設計師的眼睛掃過一行代碼,就能檢查和防止某些特定的錯誤。