Categories
程式開發

如何產出規範、安全、高質量的代碼?


對於一個軟件開發團隊,可以通過哪些代碼質量指標和掃描方法讓團隊產出規範、安全、高質量的代碼?讓開發團隊運行的安全、透明、可靠?本文總結了其中一些實踐和工具,包含常見代碼質量掃描工具、代碼質量指標、第三方依賴管理、安全運維等幾個方面,主要適用於Java/JavaScript 技術棧的web 項目,希望對於想要規範化自己的項目的Tech Lead 有所幫助。

對於一個軟件開發團隊,可以通過哪些代碼質量指標和掃描方法讓團隊產出規範、安全、高質量的代碼?讓開發團隊運行的安全、透明、可靠?

本文總結了其中一些實踐和工具,包含常見代碼質量掃描工具、代碼質量指標、第三方依賴管理、安全運維等幾個方面,主要適用於Java/JavaScript 技術棧的web 項目,希望對於想要規範化自己的項目的Tech Lead 有所幫助。

代碼掃描和常見質量指標

“禍患常積於忽微”,往往一些奇怪的bug 都是一些不規範的小問題造成的。德國飛機渦輪機的發明者帕布斯·海恩提出的一個在航空界關於飛行安全的法則,法則指出: 每一起嚴重事故的背後,必然有29 次輕微事故和300 起未遂先兆以及1000 起事故隱患。應用於軟件開發中,如果項目中代碼混亂不堪,必然會在某個時候最終爆發大量的問題。

如何產出規範、安全、高質量的代碼? 1

這裡整理了一些常見的掃描工具和代碼質量指標,可以在搭建項目基礎設施時引入,用於自動化的檢查代碼中潛在的問題,達到控制代碼產出質量的目的。

掃描工具

格鬥風格

checkstyle 是常用於java 項目的掃描工具,檢查源代碼是否與代碼規範相符,檢查項目主要包括:Javadoc 註釋、imports、過長的類和方法、空格、重複文件、圈複雜度等,默認使用sun 的代碼規則,也可以配置自定義的代碼規則,例如阿里就發布了相應的檢查規則。

蟲子

通過Bug Patterns 的概念,尋找代碼中可能出現的bug,檢查項目主要包括:不良編程習慣導致的問題、性能問題、安全問題、線程問題等。例如,應使用equals 判斷相等,而不是“ =” 操作符、流需要關閉、線程資源需要釋放等問題。 findbugs 的模式庫對編程經驗也有較好的提升作用。還可以導入和編寫自己的Bug Patterns 完善檢查機制。

猿猴

simian 是一個用於檢查重複和相似代碼的工具,它的重複檢查類似於論文查重,會提示一定的相似度。可以單獨運行,也可以作為checkstyle 插件來使用,相對來來說比較小眾。

PMD

pmd 是一款跨語言的通用靜態掃描工具,具備一部分checkstyle、findbugs 的功能,不再贅述。

ESlint / TSlint

前端界的checkstyle , TSlint 設計用來做TypeScript 類型檢查,ESlint 作為代碼風格檢查工具。不過現在ESlint 也提供了TypeScript 類型檢查功能,基本上ESlint 能整合這兩個功能。由於性能問題, TypeScript 也採用了ESLint 作為TSlint替代的檢查工具。

聲納

SonarQube 是一款用於代碼質量管理的開源工具,它主要用於管理源代碼的質量。 SonarQube 和上面的工具不太一樣,SonarQube 設計目的是提供一個平台,通過插件的方式提供對各個語言進行支持,也可以和checkstyle、pmd、simian 等工具進行集成。 SonarQube 一般需要單獨部署成一個服務,提供數據庫,可以記錄掃描結果等信息。

海拔審計

npm audit 是npm 6 之後的版本自帶的一個前端安全掃描工具,可以掃描npm 依賴中的潛在的漏洞威脅。這些引入的漏洞可能威脅用戶開發的機,另外也可能被帶入bundle 文件發佈到線上,帶來安全問題。目前npm audit 會在npm install 完成後自動執行,需要留意安全威脅報告。

強化SCA

Fortify SCA(Source Code Analyzer) 是一款非常優秀的代碼安全掃描工具,用於分析代碼中潛在的安全問題。通過調用語言的編譯器或者解釋器把代碼(Java、C、C++等源代碼)轉換成一種中間媒體文件NST(Normal Syntax Trcc),然後通過模式匹配相關的方式抓取存在於漏洞庫中的漏洞。例如,上傳的文件沒有做檢查等XSS 攻擊。

OWASP依賴性跟踪

開放式Web 應用程序安全項目(OWASP)是一個非營利組織,提供了很多安全標準、數據庫、社區和培訓。其中一個工具就是OWASP Dependency-Track,可以對第三方依賴包中的知名漏洞進行檢查,掃描結果受到漏洞數據庫的更新影響。

archunit 架構規範檢查

前面的檢查是代碼層面,archunit 可以用於代碼架構檢查,可以定義規則檢查每個包中的實現是否符合規範。例如,controller 包中的類不能實現service 的接口,repository 下的類必須實現Repository 接口。通過archunit 可以減少codereview 的工作量,避免項目的結構被破壞。

如何產出規範、安全、高質量的代碼? 2

統計工具

sloccount、sourcemointor 這兩個工具可以用於統計代碼數量,包括行數、文件數、註釋等。除了在項目中掃描bug 之外,配置代碼統計工具可以對項目有一個整體的認知。

其他的掃描工具還很多,例如coverity、codemars、binscope、synk、appscan、retire.js 等工具,不再一一列舉。

最佳搭配

這幾款工具之間的功能有所重疊,在實際工作中,我們可以根據上面推薦的關注的點,重點清除這些問題。這些掃描工俱全部用上除了會帶來團隊壓力和維護成本之外,代碼質量不會隨著引入的插件增多。除開有質量團隊的大廠提供這些掃描平台外,敏捷團隊往往不會太大,團隊持續關註一個精簡的掃描組合更好。

Java 後端

  1. 格鬥風格 Java 代碼風格守護,Java 項目至少應該配置一個默認的checkstyle 規則。至少讓項目乾淨,沒有無用、重複的代碼,以及超大的類和方法。建議做到每次提交代碼前檢查。
  2. 蟲子 常見不規範的代碼檢查,一些空指針、equals 檢查非常有用,而且IDE 的插件也很好用。

前端

  1. 吊索 守護JavaScript 代碼風格,eslint 搭配一個.editorconfig ,可以方便的讓編輯器保持同eslint 一致的代碼風格。
  2. 海拔審計 項目中第三方包的威脅掃描,npm 自帶無需額外安裝,npm 6 以後自運行,需要關注並修復報出的安全問題。

安全

  1. 強化 掃描代碼中的漏洞,用它檢查出來的大部分安全問題都是注入攻擊、XSS 等攻擊,這些問題明顯可以在開發過程中避免。可以作為Jenkins 插件配置,和單元測試作為同一階段運行。
  2. OWASP 插件 用來掃描第三方依賴漏洞,因為項目中的依賴不會像源代碼一樣頻繁變化,推薦使用Jekins 插件,定期執行即可。

為什麼不用SonarQube 呢,SonarQube 是一個非常優秀的代碼質量開放平台,需要單獨的配置安裝,需要花費額外的時間維護,對於小團隊來說成本較高,如果有專門的質量團隊可以考慮維護一套。

常用代碼質量指標參考

  1. 編譯告警數,大部分程序員基本上忽略warning,但是編譯器出現了告警是一種不好的體現,意味著軟件可能工作,但是存在不好的實踐,而這種不確定性,會帶來不確定的bug 最終讓人一頭霧水。編譯過程中的告警,盡量消除掉,編譯告警的值推薦消除到0。
  2. 平均函數代碼行數,過大的函數會導致閱讀困難,而且往往過大的函數職責不夠單一,一般將一個方法代碼行數控製到30 – 50 行。
  3. 平均文件代碼行,和平均函數代碼行一樣,過長的文件一樣難以維護,一般一個文件10多個方法,因此文件的代碼行數一般控製到300 – 500 行。
  4. 冗餘代碼,有時候我們代碼中可能存在未使用的方法、變量等代碼,這讓維護者一頭霧水,通常需要清零。
  5. 總文件重複率,出現重複文件的次數。除了編寫單元測試的情況下,業務代碼不應該出現重複代碼,推薦值為0。
  6. 總代碼重複度,代碼的重複度檢查,限於掃描工具的識別模式,需要有一定的容忍度,推薦值在5% – 10%
  7. 平均函數圈複雜度,圈複雜度用來衡量一個模塊判定結構的複雜程度。如果一個方法內部有大量的if 語句嵌套,意味著這個方法的實現質量低下,且程序複雜度高不利於維護,推薦值小於5%。
  8. 安全告警,如果配置了安全掃描工具,例如Fortify,安全威脅應該被清零。
  9. 代碼缺陷,如果配置了缺陷掃描工具,例如Findbugs,需要清零。

如何產出規範、安全、高質量的代碼? 3

常用代碼質量指標參考

第三方依賴規範化

軟件開發過程中,不可避免的需要引入第三方或者開源軟件包作為庫或者框架引入。 “第三方” 其實不是一個軟件工程術語,現今在軟件行業裡面的理解是:第一方為自研的軟件,第二方為內部發布的軟件,第三方為從社區或者外部商業途徑引入的軟件包。

對於個人開發者而言,面向“搜索引擎”編程往往將來源不明的代碼片段和程序包引入到項目中。對於企業來說,考慮到的不僅僅是功能是否能實現,還要考慮引入時帶來的成本和問題,例如是否需要授權、開源協議是否合理、是否會帶來安全威脅。

企業對於第三方依賴的引入分為幾種情況:

  1. 作為開發工具引入 ,例如gcc、Jenkins,基本沒有開源協議問題,但是需要注意開發機、CI 會有安全風險。 Jenkins 曾出現過漏洞,CI 服務器被當做遠程礦機使用。
  2. 作為服務部署使用(SaaS ),部分開源協議會限制這種使用方式,第三方依賴的安全問題會威脅服務器。
  3. 通過軟件包再發布 ,大部分開源軟件對這種使用方式有較多要求,例如GPL 開源協議具有傳染性,要求使用了GPL 的項目也要開源。
  4. 拷貝源代碼引入項目 ,非常不推薦這種方式,盡量通過包管理的方式引入。

引入第三方依賴需要充分考慮,盡可能最小成本的引入。在一個React 的前端項目中,有不熟悉的工程師,為了使用一個簡單的手風琴效果,引入了整套bootstrap。不僅破壞了使用React 的最佳實踐,而且讓輸出的bundle 文件大小激增數倍,造成首屏加載的性能問題。

常見商業友好的開源協議

商業用戶常用的開源協議實際上只有6種左右,即LGPL、Mozilla、GPL、BSD、MIT、Apache,另外還有極其寬鬆的The Unlicense,但採用的開源軟件不多。

GitHub 提供了一個license 清單的列表 https://choosealicense.com/licenses/

我根據開源協議的寬鬆程度,整理了一個列表,方便查看:

如何產出規範、安全、高質量的代碼? 4

開源協議

幾乎所有的開源協議有一個共同的注意事項: 採用該開源協議的軟件項目,不提供任何責任轉移和質量保證 。也就是說採用開源軟件造成的法律問題和開源項目無關,另外需要使用者承擔因質量問題造成的所有後果。另外,除了引入的程序包之外,字體、圖片、特效音、手冊等媒體資源也算廣義上的“軟件”需要考慮開源協議和使用場景。

第三方依賴管理

對項目中出現的任何第三方依賴有效的管理有非常重要的意義,通過掃描工具,識別出項目中是否有源碼、jar包、二進製文件是否來源於某個開源項目。

  • 任何的第三方軟件需要申請入庫管理(內部其他團隊申請通過可以直接使用),質量團隊對申請的軟件進行評估:
  • 是否有開源義務需要履行
  • 引入的第三方依賴是否有CVEs等漏洞

第三方開源軟件是否仍然在維護

質量團隊根據上面的一些條件,決定出申請的軟件能否在項目中使用,允許被採用的軟件會定義出優選級別,優先推薦團隊使用較為優選的軟件,並對項目整體的優選率有一定要求。如果項目中出現了無法識別的二進製文件、非約定目錄下的代碼片段,需要報備。通過良好的依賴管理和規範化,能減少不良第三方依賴的引入,讓軟件項目透明、可信。

一些商業公司提供這些完整的服務,例如fossid、blackduck、code-climate 等。

運維安全

大的軟件公司,往往有一堆流程和要求。雖然一線開發對堡壘機、防火牆、各種安全規範顯得不耐煩,但這些安全措施也在保護開發者。

防火牆用於環境隔離

往往開發者理解的防火牆用於防止網絡入侵、審計、入侵檢測等功能,除此之外,防火牆還可以用於各個環境的隔離。一般來說,企業對於生產環境的數據控制比較嚴格,不會將生產環境的權限交給團隊所有開發者,但網絡連接有可能疏漏。

曾經出現過一次線上事故,由於配置文件錯誤,將原本應該連接到測試的數據庫連接到了生產環境,造成大量臟數據寫入。如果通過防火牆規則對各個環境進行隔離,這類問題將不會出現。

另外也可以設計DMZ 區,將面向用戶側的網關部署到DMZ 區,僅僅開放必要的端口給網關,實現內外網的物理隔離。同時,對整個系統的防火牆策略應該清晰地記錄,否則在做大的基礎設施更新時,梳理出所有的防火牆策略,是一件比較困難的事情。

如何產出規範、安全、高質量的代碼? 5

憑據管理

項目中會用到大量的憑據,例如數據庫、第三方系統對接的key,使用明文不是一件好事。理想的情況下,對項目中所有的密碼信息進行掩蓋(mask),避免CI、日誌中敏感信息的洩露。

有很多種方法可以掩蓋項目中的密碼信息:

  1. 使用環境變量對密碼信息進行覆蓋。
  2. 使用Spring boot 的項目可以配置jasypt,使用jasypt 將密碼加密,將生成的加密串配置ENC(加密串) 到工程的配置文件中。加密過程可以加鹽作為解密的憑據,“鹽” 可以不存放到工程中,在工程部署的時候注入即可。
  3. 如果使用Jenkins 等CI/CD 工具,可以使用構建平台提供的憑證管理工具。
  4. 如果使用Spring cloud,可以使用spring cloud vault 組件部署一個憑證管理服務

另外,建議不要用任何個人憑據用作系統對接,應該使用一個公共的應用憑據。

堡壘機

一般來說我們管理服務器,所有的運維操作需要通過堡壘機進行操作。開放22 等高危端口,允許開發者直接登錄到服務器是一種不安全的做法。

堡壘機,通俗的來說是跳板機+ 監控。最初使用的跳板機配置了兩張網卡,用於連接開發環境和生產環境,並沒有監控功能。在此基礎上,堡壘機增加了統一運維管理的功能,往往需要兩步驗證(SMS 或Email),並對所有的操作進行記錄和監控。

在需要團隊參與運維工作的場景中,非常有必要部署一套堡壘機服務,並使用LDAP 對接到團隊成員的ID 上,便於集中運維管理。

定期對系統軟件掃描

Linux 系統往往有云廠商推送安全補丁和風險提示,但是安裝到服務器上的軟件,例如JDK、nodejs,需要自己檢查安全問題。因此需要在系統中安裝並定期運行CVEs 檢查並及時更新。有一款cvechecker 可以幫助運維人員,編寫一個腳本定期運行cvechecker 檢查系統中已知的軟件是否存在CVEs 漏洞,並提醒開發者及時更新。

寫在後面

剛開始工作時候,喜歡動態的、靈活的編程語言,討厭的死板的、套路化的編程語言,然而需要很長一段時間,才能意識到“約束是程序員的朋友”。對一些安全知識了解的來源大多來自修復SonarQube 的經歷,使用findbugs 也讓我對Java 基礎認識的更加深刻。

類似的,在使用一些框架、平台的時候往往存在大量的限制,有時候開發者難以意識到“限制” 正是框架、平台的作者“保護” 應用開發者的一種方式。有一些開發者以Hack 框架、平台為樂,但是這樣會帶來潛在的隱患,在用戶量上來之後負面效應表現的尤為明顯。

項目的規範化對於Tech Lead來說可以減少程序的運行事故和codereview 時間,對於團隊來說也許可以少加班吧。

作者介紹

少個分號(林寧),ThoughtWorks諮詢師,長期從事國內外企業軟件開發,在React、RESTful API等新興技術有大量經驗,喜歡開發基本組件和工具來提升效率。目前專注於富前端和API開發,熱衷於研究前後端的集成和解耦。

本文轉載自ThoughtWorks洞見。

原文鏈接

如何產出規範、安全、高質量的代碼?