平庸程序員的各類跡象

1.沒法從集合的角度思考

從命令式編程過分到函數式和聲明式編程,會馬上要求你思考將數據集合看成原語來操做,而不是做爲標量值。不管你在關係型 數據庫(而且不是做爲一個對象倉庫)中使用 SQL,仍是設計規模會隨多處理器線性變化的程序,亦或是你寫的代碼必需要在擁有 SIMD 能力的芯片(好比現代顯卡和電子遊戲機)上執行,都須要這種過渡。html

特徵算法

只有在帶有聲明式或函數式編程特性(程序猿應該知道這些特性)的平臺上看到這些特徵時,下面列出的纔算數。數據庫

  1. 在 for 或 foreach 的循環裏對集合中的每一個元素執行原子操做。
  2. 寫的 Map 或 Reduce 函數裏包含自定義的循環,在循環裏逐一重複執行數據集。
  3. 從服務器抓取大量數據集,並在客戶端上計算和,而不是在查詢裏使用匯集函數。
  4. 函數做用於一個集合中的每一個元素,並在函數開頭經過執行一次新的數據庫查詢來抓取一個關聯記錄。
  5. 寫的業務邏輯函數,例如更新一個用戶界面或執行文件 I/O,很不幸地爲了某種折衷而伴隨有反作用。
  6. 在實體類打開專屬的數據庫鏈接或文件操做符,而且在每一個對象的生命週期裏都保持鏈接狀態。

補救措施編程

很是有趣,想象着一個發牌的人經過手指在牌裏翻轉把一副牌切成兩堆交叉洗牌,就能讓大腦聯想到集合,以及如何成批地操做集合。激發人聯想的其餘想象還有:數組

  • 高速公路上的車流經過一系列收費站(並行處理)。
  • 泉水匯聚成溪流,溪流又匯聚成小河,最後再匯聚成江河(並行分解/彙集函數)。
  • 一個報紙印刷機(協同程序、流水線)。
  • 夾克上的拉鍊頭把拉鍊齒拉上(簡單的聯結)。
  • 轉移 RNA 加上氨基酸,並在一個核蛋白中加入信使 RNA,就變成了蛋白質(多階段函數驅動聯結,詳見 animation)。
  • 在一棵橘子樹中,數以億計的細胞裏同時發生着上述的過程,不斷地將空氣、水和陽光轉換成橘子汁(大型分佈式集羣上的 Map/Reduce)。

若是你正在寫一個處理集合的程序,思考一下全部的附加數據和記錄,你的函數須要操做它們的每個元素。而且在 Reduce 函數應用到每對數據上以前,使用 Map 函數把它們成對地聯結在一塊兒。安全

2.缺少批判性思惟

除非你能批判本身的思惟並從中找出缺陷,不然你會錯過那些能夠在敲代碼以前就能解決的問題。若是你也沒法評判本身曾經寫過的代碼,那你只能在不斷摸索中以龜速學習。這個問題同時來源於思考怠惰和以自我爲中心,所以,這個問題的特徵彷佛也來自兩個不一樣的方向。服務器

特徵網絡

  1. 自制「業務規則引擎」。
  2. 靜態工具類很冗餘且龐大,或者多學科的函數庫只用一個命名空間。
  3. 把各類應用糅合在一塊兒,或給當前的應用附加不相關的特性來避免啓動新項目的開銷。
  4. 程序架構開始須要創建 epicycle 模型。(譯者: epicycle 模型是天文學上使用的模型,用來解釋天體在運動過程當中出現的誤差等異常行爲。)
  5. 爲了很不相關的數據向表中添加字段(好比:在通信錄的表中放置「# cars owned」字段)。(譯者:通信錄的內容要記錄是否有車幹嗎,確實扯遠了。)
  6. 先後矛盾的命名規範。
  7. 處於「拿着錘子看什麼都是釘子」的心態,或者改變對問題的定義,這樣全部問題都能用某個特定的技術來解決。
  8. 編寫程序下降問題的複雜度。
  9. 從病理上冗餘地防護式編程(「企業級代碼」)。
  10. 用 XML 從新發明 LISP。

補救措施架構

從Paul 和 Elder 寫的《批判性思惟 | Critical Thinking》這樣的書入手,控制自我意識,在向朋友或同事發表本身的想法以此尋求評論時,練習抵制爲本身辯護的衝動。編程語言

一旦你習慣了別人來檢驗你的想法,你就會開始自我審視並練習想象這些想法的結果。另外,你也須要培養起區別輕重緩急的能 力(能直覺知道對這種規模的問題,須要花費多少精力比較合適)、用實踐驗證假設的習慣(這樣你就不會高估問題的大小)和麪對失敗的健康心態(就算艾薩克. 牛頓的地心引力說是錯的,但咱們依然愛他並須要他去嘗試)。

最後,你必須自律。意識到計劃裏有缺陷不會讓你更高效,除非你有足夠的意志力去改正缺陷,並重建手中正在進行的工做。

3.彈球式編程

若是你把面板傾斜得剛恰好,把曲柄拉回到恰好的距離,而且以正確的順序擊中那些凸起的按鈕,那麼程序就會像彈球同樣運行無誤:隨着指令的執行流程,從條件語句返回,跳過未選中的指令,轉向下一次的狀態轉換。

特徵

  1. 用一個 try-catch 代碼塊包圍 Main() 的整個函數體,並在 Catch 分句中重置整個程序(像彈球地溝,掉下去之後從新開始遊戲)。
  2. 在強類型的語言中,用字符串或整型來存儲那些擁有(能夠用)更合適封裝類型的值。
  3. 把複雜數據打包成帶分隔符的字符串,而後在使用它的每一個函數裏解析一遍。
  4. 對輸入有歧義的函數,不會用斷言(assertion)或方法協定(method contract)。
  5. 使用 Sleep() 來等待另外一個線程完成任務。
  6. 對非枚舉類型的值使用 switch 語句,並且分支語句中沒有「Otherwise」分句。
  7. 用 Automethods 或 Reflection 來調用在非法的用戶輸入中提到的方法。
  8. 在函數裏經過設置全局變量來返回多個值。
  9. 類裏有一個方法和幾個字段,經過設置字段來爲方法傳遞參數。
  10. 不用事務來更新多行數據庫內容。
  11. 背注一擲(好比,試圖不用事務和 ROLLBACK 來恢復數據庫的狀態)。

補救措施

把程序的輸入想象成水。它即將流過每個縫隙,灌滿每個容器。那麼你要想想,若是它流過的地方並無明確建立任何東西去呈接它的話,會形成什麼後果。

你要讓本身熟悉平臺的機制,這有助於寫出健壯且易擴展的程序。共有三種基礎機制:

  1. 當某種意外發生時,能在產生任何破壞以前中止程序,而後幫助你識別出是哪裏出錯了(類型體系、斷言、異常等)。
  2. 將程序的執行導向處理意外最佳的代碼塊( try-catch 模塊、多重分發、基於事件驅動編程等)。
  3. 暫停線程直到一切就緒(WaitUntil 命令、互斥鎖和信號量、同步鎖等)。

還有第四條,單元測試,你能夠在設計階段使用。

使用這些機制應該成爲你的次日性,就像在句子裏用逗號和句號同樣。爲了作到這些,每次瀏覽一遍上面介紹的機制(括號裏 提到的那些),並重構你的舊程序,把提到的這些機制塞到任何能塞的地方,就算最後發現這麼作並不合適(尤爲是在它們看似不合適的時候,至少那時你也開始明 白其中的原因)。

4.不熟悉安全原則

若是要說下述特徵並不很嚴重,但它們幾乎是大部分程序都存在的總體質量問題。意思是說,這些特徵不會讓你成爲一名很糟糕的程序猿,只是意味着你不該該從事網絡程序或安全系統的工做,直到你已經在這方面作了一些功課。

特徵

  1. 以明文形式存儲可利用信息(名字、卡號、密碼等)。
  2. 用低效的加密術存儲可利用信息(將密碼編譯在程序中的對稱加密算法;簡單密碼;任何「解碼環( decoder-ring )」、自創加密算法、專有的或未驗證的加密算法)。
  3. 在接受網絡鏈接或解釋來自非置信源的輸入信息以前,程序或設備沒有限制它們的權限。
  4. 不進行邊界檢查或輸入合法性驗證,尤爲是在使用非託管類的語言時。
  5. 把不合法或非轉義的輸入串接到字符串上來構建SQL查詢。
  6. 調用用戶輸入中指定的程序。
  7. 試圖經過搜索已知漏洞的簽名(signature)來阻止漏洞被利用。
  8. 用不加鹽的哈希值(unsalted hash)存儲信用卡卡號或密碼。

補救措施

下面只涵蓋了基本原則,但遵守這些原則會避免絕大多數臭名昭著的錯誤,那些錯誤可讓整個系統大打折扣。對於任何處理或存儲有價值信息的系統,不管是向你仍是其用戶,或是控制一個貴重資源的系統,它們一般都有一個安全專家來審查系統的設計與實現。

從審查程序開始,找出用數組或其餘配置內存的容器來存儲輸入的代碼,確保這部分代碼檢查了輸入的大小不會超出分配給它的 內存大小。沒有其餘類型的 bug 能比緩衝區溢出更能致使可利用的安全漏洞。從某個層面來講,在寫網絡通訊程序或任何安全第一的場合下,你應該認真考慮使用某種內存託管型的編程語言。

下一步,審查數據庫查詢操做。審查那些將未修改輸入串接到 SQL 查詢內容中的查詢操做,而且,若是平臺支持的話就切換爲使用參數化查詢,若是不支持就對輸入進行過濾或轉義。這麼作是爲了防止 SQL 注入攻擊。

在你清除了這兩類最臭名昭著的安全 bug 以後,你應該繼續將全部的的程序輸入視爲徹底不可靠,或是有潛在惡意。按有效的驗證規則來定義程序的輸入很重要,並且除非輸入能經過驗證,不然程序應該拒 絕它,這樣你就可以經過修復驗證方法並使其更加明確來修復可利用的漏洞,而不是經過掃描已知漏洞的簽名來修復漏洞。

進一步說,你應該老是在開始設計程序以前,思考程序須要執行的操做以及這些操做須要從 host 得到什麼樣的權限,由於這個時候是想出怎麼樣能儘量使用最少權限的最佳時機。這條建議背後的原則是,若是在你的代碼中找到一個可利用的 bug ,限制這個bug可能對系統其餘部分形成的損害。換言之:在你學會不信任輸入以後,你也應該學會不要信任本身寫的程序。

最後你要學會的是數據加密基礎,從《Kerckhoff’s principle》開始。這一點亦可表達爲「安全第一」,從中還衍伸出了一些有趣之處。

原則一,永遠不要信任一個密碼或其餘加密原語,除非它已經被公開發表,而且已經由更高級別的安全社區對其進行了全面的分 析和測試。從密碼學的發展來看,模糊晦澀的加密法、專有的加密法或是新出現的加密法都毫無安全可言。即便是可信的加密原語,其實現中也會存在缺陷,所以, 對於你不能肯定其已經獲得全面審查的加密算法(包括本身實現的版本),要避免使用。全部的新型加密系統都要通過一系列的詳細審查,這個過程可能長達十年之 久,或更長,而你只要關注那些最後經受住了審查而且全部已知錯誤都已修復的加密系統。

原則二,若是密鑰容易破解或存儲失當,那這和徹底不加密同樣糟糕。若是程序要對數據加密,但不須要解密或不多須要解密,那就考慮只把對稱加密密鑰對的公鑰給它,並讓解密階段和私鑰分開運行,用戶必須每次輸入一個好的口令來確保密鑰的安全。

越是處於危險之中,你須要作的功課越多,而且必須在程序的設計階段投入更多精力。這都是由於一旦你的程序部署下去,就會有成堆、有時候多是成千上萬的不速之客試圖去破壞它的安全性。

絕大部分可追溯到代碼問題的安全故障都歸因於一些很愚蠢的錯誤,其中大部分錯誤能夠經過篩選輸入、謹慎使用資源、利用常識、想清楚再寫代碼等方式來避免。

5. 代碼一塌糊塗

特徵

  1. 不遵循一向的命名規範。
  2. 不使用縮進,或縮進不一致。
  3. 不使用空格,例如在方法之間不加空格(或表達式裏不加空格,看「ANDY=NO」)。
  4. 有一大堆被註釋掉的代碼。

補救措施

程序猿在匆忙之下(或特殊狀況下)犯了上述全部毛病的話,會在以後返回來清理,但一個糟糕的程序猿真的就只是粗枝大葉。 有時,利用可經過快捷鍵來修復縮進和空格(「美觀的格式」)的 IDE 是很幫助的,但我發現程序猿老是把代碼搞得一團糟,極大地違背 Visual Studio 對適當縮進的堅持。

相關文章
相關標籤/搜索