用麵包工廠理解「乾淨」#

Joel 以他在以色列麵包工廠的工作經驗開場:外行人無法判斷工廠是否乾淨,但待了兩個月後你就能一眼看出問題。程式碼也是一樣的——只有熟悉之後,你才能看待「乾淨」的含義。

程式設計師的四種境界#

  1. 分不清什麼是乾淨的代碼、什麼是不乾淨的
  2. 對乾淨代碼有膚淺的認識,主要看是否符合代碼書寫規範(coding style)
  3. 能找出隱藏的不乾淨代碼,在乾淨的表面之下發現蛛絲馬跡
  4. 精心構建代碼,發揮洞察力,將它們寫得清晰易懂、不容易出錯

第四種境界是一門真正的藝術——透過人為發明一套書寫規範,使得錯誤在顯示屏上變得易見,從而讓代碼更健壯。

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 強烈建議制定代碼書寫規範,讓錯誤的代碼更容易被發現——每一次程式設計師的眼睛掃過一行代碼,就能檢查和防止某些特定的錯誤。