ESLint 在中大型團隊的應用實踐

引言

代碼規範是軟件開發領域經久不衰的話題,幾乎所有工程師在開發過程中都會遇到,並或多或少會思考過這一問題。隨着前端應用的大型化和複雜化,越來越多的前端工程師和團隊開始重視 JavaScript代碼規範。得益於前端開源社區的繁盛,當下已經有幾種較爲成熟的 JavaScript 代碼規範檢查工具,包括 JSLint、JSHint、ESLint、FECS 等等。本文主要介紹目前較爲通用的方案——ESLint,它是一款插件化的 JavaScript 代碼靜態檢查工具,其核心是通過對代碼解析得到的 AST(Abstract Syntax Tree,抽象語法樹)進行模式匹配,定位不符合約定規範的代碼。

ESLint 的使用並不複雜。依照 ESLint 的文檔安裝相關依賴,可以根據個人/團隊的代碼風格進行配置,即可通過命令行工具或藉助編輯器集成的 ESLint 功能對工程代碼進行靜態檢查,發現和修復不符合規範的代碼。如果想降低配置成本,也可以直接使用開源配置方案,例如eslint-config-airbnbeslint-config-standard

對於獨立開發者,或者執行力較強、技術場景較爲單一的小型團隊而言,直接使用 ESLint 及其生態提供的一些標準方案,可以用較低成本來實現 JavaScript 代碼規範的落地。如果再搭配一些輔助工具(例如 husky 和 lint-staged),整個流程會更加順暢。但對於數十人的大型前端團隊來說,面向數百個前端工程,規模化地應用統一的 JavaScript 代碼規範,問題就會變得較爲複雜。如果直接利用現有的開源配置方案,可能會使工作事倍功半。

問題分析

規模化應用統一的 ESLint 代碼規範,會涌現各類問題,根源在於大型團隊和小團隊(或獨立開發者)的差異性:

技術層面上:

  • 技術場景更加廣泛:對於大型團隊,其開發場景一般不會侷限在傳統 Web 領域內,往往還會涉及 Node.js、React Native、小程序、桌面應用(例如 Electron)等更廣泛的技術場景。
  • 技術選型更加分散:團隊內工程技術選型往往並不統一,如 React/Vue、JavaScript/TypeScript 等。
  • 工程數量的增加和工程方案離散化導致 ESLint 方案的複雜度提升:這樣會進一步增加工程接入成本、升級成本和方案維護成本。

在團隊層面,隨着人員的增加和組織結構的複雜化:

  • 人員風格差異性更大、溝通協調成本更高。
  • 方案宣導更難觸達,難以保證規範執行的落實。
  • 執行狀況和效果難以統計和分析。

因爲存在諸多差異,我們在設計具體方案時,需要考慮和解決更多問題,以保證規範的落實。針對上述分析,我們梳理了以下需要解決的問題:

  • 如何制定統一的代碼規範和對應的 ESLint 配置?

    • 場景支撐:如何實現對場景差異的支持?如何保證不同場景間一致部分(例如 JavaScript 基礎語法)的規範一致性?
    • 技術選型支撐:如何在支撐不同技術選型的前提下,保證基礎規則(例如縮進)的一致性?
    • 可維護性:具體到規則配置上,能否提升可複用性?在方案升級迭代時成本是否可控?
  • 如何保證代碼規範的執行?

    • 人員的增加和組織結構的複雜化,會導致基於管理的執行把控失效,這種情況應該如何保證代碼規範的執行質量?
  • 如何降低應用成本?

    • 在工程數量增加、工程方案離散化的情況,降低方案的接入、升級和執行成本能節約大量的人力,同時也有利於方案落地推進。
  • 如何及時瞭解規範應用狀況和效果?

解決方案

爲了能在團隊內實現 JavaScript 代碼規範的統一,在分析和思考團隊規模化應用存在的問題後,我們設計了一套完整的技術解決方案。該方案包括多場景統一的 ESLint 規則配置、代碼集成交付檢查、自動化接入工具、執行狀況監測分析等四個模塊。通過各個模塊協調配合,共同解決上文提出的問題,在降低維護成本、提升執行效率的同時,也保障了代碼規範的統一。

  • 整體方案的設計如下圖所示:

  1. 多場景統一的 JavaScript 規範:該模塊是整個方案的核心,藉助 ESLint 的特性,通過分層分類的結構設計,在保證基礎規則一致性的同時,實現了對不同場景、技術選型的支撐。
  2. 代碼集成交付檢查:該模塊是方案落地執行的保障,將代碼靜態檢查集成到持續交付工作流中。具體設計實現上,在保證交付質量的同時,也通過定製集成檢查工具降低了開發者的應用執行成本。
  3. 自動化接入和升級方案:通過命令行工具提供“一鍵”接入/升級能力,同時集成到團隊腳手架中,大大降低了工程接入和維護的成本。
  4. 執行狀況監測分析:通過對工具運行和代碼集成交付檢查過程進行埋點、檢查結果收集和分析,瞭解方案的應用狀態和效果。

方案實現

上文中提出的問題,通過各模塊的協調配合能夠得到有效地解決,但具體到各個模塊的實現,仍然需要進一步深入思考,以設計出更加合理的實現方案。本章將對方案的四個核心模塊進行詳細介紹。

通用 ESLint 配置方案

這一模塊主要藉助 ESLint 的基礎特性,採用分層分類的結構設計,提供多場景、多技術方案的通用配置方案,並使方案具備易維護、易擴展的特性。

ESLint 特性簡介

在進行 ESLint 配置方案設計前,我們先看一下 ESLint 的一些特點。

1.插件化

下圖簡單地描述了 ESLint 的工作過程:

ESLint 的能力更像一個引擎,通過提供的基礎檢測能力和模式約束,推動代碼檢測流程的運轉。原始代碼經過解析器的解析,在管道中逐一經過所有規則的檢查,最終檢測出所有不符合規範的代碼,並輸出爲報告。藉助插件化的設計,不但可以對所有的規則進行獨立的控制,還可以定製和引入新的規則。ESLint 本身並未和解析器強綁定,我們可以使用不同的解析器進行原始代碼解析,例如可以使用 babel-eslint 支持更新版本、不同階段的 ES 語法,支持 JSX 等特殊語法,甚至可以藉助 @typescript-eslint/parser 支持 TypeScript 語言的檢查。

2.配置能力全面、可層疊、可共享

ESLint 提供了全面、靈活的配置能力,可以對解析器、規則、環境、全局變量等進行配置;可以快速引入另一份配置,和當前配置層疊組合爲新的配置;還可以將配置好的規則集發佈爲 npm 包,在工程內快速應用。

3.社區生態較爲成熟

開源社區中基於 ESLint 的項目非常多,既有針對各種場景、框架的插件,也有各種 ESLint 規則配置方案,基本可以涵蓋前端開發的所有場景。

規範配置方案設計

基於 ESLint 的插件化、可層疊配置特性,以及面向各種場景、框架的開源方案,我們設計瞭如下圖所示的 ESLint 配置架構:

該配置架構採用了分層、分類的結構,其中:

  • 基礎層:制定統一的基礎語法和格式規範,提供通用的代碼風格和語法規則配置,例如縮進、尾逗號等等。
  • 框架支撐層(可選):提供對通用的一些技術場景、框架的支持,包括 Node.js、React、Vue、React Native等;這一層藉助開源社區的各種插件進行配置,並對各種框架的規則都進行了一定的調整。
  • TypeScript 層(可選):這一層藉助 typescript-eslint,提供對 TypeScript 的支持。
  • 適配層(可選):提供對特殊場景的定製化支持,例如 MRN(美團內部的 React Native 定製化方案)、配合 prettier 使用、或者某些團隊的特殊規則訴求。

具體的實際項目中,可以靈活的選擇各層級、各類型的搭配,獲得和項目匹配的 ESLint 規則集。例如,對於使用 TypeScript 語言的 React 項目,可以將基礎層、框架層的 React 分支、以及 TypeScript 支撐層的 React 分支層疊到一起,最終形成適用於該項目的 ESLint 配置。如果項目不再使用 TypeScript 語言,只需要將 ts-react 這一層去掉即可。

最終,形成了如下所示的 ESLint 配置集:

考慮到維護、升級和應用成本,我們最終選擇將所有配置放到一個 npm 包中,而不是每種類型分別設置。仍以使用 TypeScript 語言的 React 項目爲例,只需在工程中進行如下配置:

// 需要安裝 typescript、eslint-plugin-react、@typescript-eslint 等插件
module.exports = {
  root: true,
  extends: [
    // 因爲基礎層是必備的,所以框架層默認引入了對應的基礎層,不需再單獨引入 eslintrc.base.js
    'eslint-config-xxx/eslintrc.react.js',
    'eslint-config-xxx/eslintrc.typescript-react.js'
  ]
}

這種通過分層、分類的結構設計,還有利於後期的維護:

  • 對基礎層的修改,只需修改一處即會全局生效。
  • 對非基礎層某一部分的調整不會產生關聯性的影響。
  • 如需擴展對某一類型的支持,只需關注這一類型的特殊規則配置。

衆所周知,TypeScript 類型的項目使用 TSLint 進行代碼檢查,也是一種簡單、便捷的方案。但在本方案中我們依舊選擇了:eslint + @typescript-eslint/parser + @typescript-eslint/eslint-plugin 的組合方案。主要有以下幾點原因:

  • ESLint 的規則配置更加詳細全面,覆蓋更加廣泛。
  • 採用了分層分類的架構,能夠保證即使框架或語言不同,也能在基本語法、風格層面保持規則的一致性,這樣有利於團隊內不同技術選型項目的風格統一。
  • @typescript-eslint 方案持續迭代,問題響應非常迅速,對 TSLint 相關的規則基本提供了對等的實現。

根據最新消息,TypeScript在 2019 路線圖 中明確表明後續對 Lint 工具的支持和建設會以對 ESLint 進行適配的方式爲主。

代碼集成檢查

基於團隊對工程化基礎設施的建設,將代碼規範靜態檢查與開發工作流集成,保證代碼規範的落實。

通常而言,工程接入 ESLint 後,可以在開發的同時藉助編輯器集成的 ESLint 檢查提示能力(例如 VSCode 的 ESLint 插件),實時發現和修改不符合規範的語法錯誤和風格問題。但這仍不能避免因一些主觀因素或疏漏造成的規範執行不到位,所以我們考慮在開發工作流的特定節點自動執行代碼靜態檢查,阻斷不合規範代碼的提交或交付。

集成靜態檢查的開發工作流節點有很多,我們主要參考以下兩種方案:

  • 代碼提交檢查:在代碼 Commit 時,通過 githook 觸發 ESLint 檢查。其優點在於能實時響應開發者的動作,給出反饋,快速定位和修復問題;缺陷在於開發者可以主動跳過檢查。
  • 代碼交付檢查:在代碼交付(藉助 CI 系統的交付流程功能)時,在代碼檢測平臺中對代碼進行 ESLint 檢查,檢測不通過則阻斷交付。其優點在於能夠強制執行,可在線追蹤檢測報告;缺陷在於離開發者的開發環境太“遠”,開發者響應處理成本較高。

如果將兩者進行結合,可能會事半功倍,效果如下圖所示:

常用的代碼提交檢查方法一般是 husky 與 lint-staged 結合,在代碼 Commit 時,通過 githook 觸發對 git 暫存區文件的檢查。但考慮到團隊現有工程數量龐大、存在大量行數較多的文件,雖然 lint-staged 策略能夠降低部分成本,但仍稍顯不足。爲此,我們對該方法進行優化,定製了本地代碼提交檢查工具 precommit-eslint,其核心特點是:

  • 將增量檢查執行到代碼行這一粒度,支持 Warn 和 Error 兩個檢查級別。
  • 只需將工具安裝爲工程的依賴,無需任何配置。
  • 減少了 pre-commit hook 中植入腳本的侵入性。
  • 進行了執行狀況埋點和採集。

使用效果如下圖所示:

在美團,我們使用自主開發的 CI 系統,並在獨立部署的 Sonar 系統上定製化實現了相應規則,基本可以滿足訴求,這裏就不再贅述。對於獨立的團隊,基於 ESLint 提供的工具,可以很容易的實現使用 Node 快速搭建一個代碼檢測服務或平臺,大家有興趣不妨一試。

自動化接入工具

這個模塊主要通過 CLI 工具提供方案自動化接入的能力,降低工程接入和升級的成本。如果不借助自動化工具,在工程中接入上述方案還是有一定的工作量和複雜度的,大致步驟如下:

  1. 安裝 Eslint。
  2. 根據項目類型安裝對應的 ESLint 規則配置 npm 包。
  3. 根據項目類型安裝相關的插件、解析器等。
  4. 根據項目類型配置 .eslintrc 文件。
  5. 安裝代碼提交檢查工具。
  6. 配置 package.json。
  7. 測試及修復問題。

在這個過程中,特別需要注意依賴的版本問題:依賴之間的版本兼容性,例如 typescript 和 @typescript-eslint/parser 之間的兼容性;依賴對規則的支持性,比如某個版本的插件中去除了對某個規則的支持,但規則配置中仍然配置了該規則,此時配置就會失效。對於 ESLint 不熟悉的開發者而言,在配置的過程中都會或多或少遇到兼容性、解析異常、規則無效等問題,反覆排查和定位問題會浪費大量的精力。

因此,在設計開發自動化接入工具時,我們綜合考慮了操作步驟、依賴版本、規則集和工程方案的兼容性,設計瞭如下的工作流程:

該工具流程簡單,不管什麼開發場景和框架選型,繁瑣的接入流程都可以簡化爲一條命令,需要配合工程方案升級時同樣如此。如下圖所示,執行該命令後項目就完成了 ESLint 的接入,使用統一的規則規範編碼,同是在代碼提交時自動進行增量檢查:

埋點與統計分析

統計分析的主要目的是掌握方案應用執行狀況和效果,理論上應當支持工程和大盤兩個視角,如下圖所示:

執行情況分析其實並不複雜,核心是信息採集和分析。在本方案中,信息採集通過 precommit-eslint 工具實現:在 git commit 觸發本地代碼檢查後,腳本會把檢查結果(包括檢查是否通過、錯誤或警告信息的數量級別等)上報;信息的統計分析藉助日誌上報分析平臺實現,美團使用的是 CAT 平臺(如果團隊或公司沒有專門的平臺,可以在上文提到的代碼檢測服務平臺中實現這部分功能)。爲了便於數據的聚合分析,我們將一次代碼提交檢查中出現的問題數量進行了分級:

  • 檢查通過:檢查無代碼規範錯誤。
  • 錯誤 1 級:檢查出代碼規範錯誤數量小於 10 個。
  • 錯誤 2 級:檢查出代碼規範錯誤數量在 10 - 100 個之間。
  • 錯誤 3 級:檢查出代碼規範錯誤數量在 100 - 1000 個之間。
  • 錯誤 4 級:檢查出代碼規範錯誤數量大於 1000 個。

比如下圖中,201903 第一週的代碼提交檢查結果統計(綜合採樣率 0.2),很明顯,所有檢查失敗的提交中,錯誤數量在 10 個以內的佔比最大,修復成本不高。

1.提交檢查異常分佈(僅篩選檢查未通過信息)


2.提交檢查警告信息分析


除此之外,還可以對單一工程,在更細的時間粒度上去觀察提交檢查的執行情況。

效果質量主要分析工程質量的變化:一方面可以通過代碼檢查執行通過率變化趨勢、檢查結果分佈去看持續的生產流程中,代碼質量是否有所提升;另一方面,由於代碼檢查採用增量模式,需要對工程代碼進行整體分析,得到工程整體的不規範代碼佔比及變化趨勢,從而從工程維度分析判斷質量效果(涉及到權限相關問題,目前團隊中未採用工程分析的方法)。具體的分析會在方案應用效果中一併進行介紹。

方案應用

除了上述整體方案外,爲保證開發者使用更方便,我們還進行了一些配套工作:

  • 持續維護升級:以每月一版的方式持續迭代升級,解決應用中的問題、規則爭議,以及支持新的規則或方案。
  • 工程化集成:整套方案可以無縫接入到各個團隊的腳手架工具中,自動成爲團隊的默認方案,在工程初始化階段即可完成接入。
  • 官網建設:提供詳細的使用文檔,包括規則信息、接入方法,並且對每個版本提供規則、環境依賴、changeLog 等詳細說明。
  • 常見使用問題:更新維護FAQ,幫助後續接入者快速查找並解決問題。

目前,該套方案已經接入美團外賣、餐飲平臺、閃購、榛果、金融等多個團隊,基於埋點統計分析,我們(基於2019年2月份最後一週統計數據分析,綜合採樣率0.2)得到了如下數據:

  • 截止到2019年2月底,該方案已接入超過 200 個前端工程。
  • 集成檢查(增量)每天執行接近 1000 次。
  • 集成檢查(增量)平均每天檢查出錯誤約 20000-25000 處。
  • 集成檢查代碼質量:平均通過率爲 75.562%,錯誤 1 級的比率爲 15.644%,在所有未通過檢查中佔比 64.015%。

同時,我們持續統計上述數據的變化趨勢,跟蹤代碼質量提升效果,以2018年12月到2019年3月的數據爲例(截止2019年3月第一週,以周爲時間統計尺度):

從圖中可以看出,最近三個月檢查通過率整體呈上升趨勢,但 2019 年 1 月的第 2 周和第 3 周集成檢查通過率有明顯下降。分析項目信息發現,在 2019 年 1 月的第 2 周有一批新項目接入,代碼檢查規範檢查出幾十個錯誤。但整體來看,目前集成檢查通過率基本穩定在 75% - 80%,從變化趨勢看仍有上升空間。

方案實施之後,我們做了一個用戶調研,發現整體方案的運營正在發揮着正向的作用。一方面,在一定程度上提升了多人協作的效率,無論是共同維護一個工程還是在多個工程間切換,避免了代碼風格不一致帶來的可讀性成本和格式化風險;另一方面,會幫助大家發現和避免一些簡單的語法錯誤。

規劃和思考

該方案已經穩定應用,除了現有功能,我們還在思考是否可以更進一步的優化,提供更豐富的能力。由此規劃了一些仍未落地的方向:

  • 擴展支持 HTML 和 CSS 的代碼風格檢查:雖然近幾年前端框架、組件庫的建設一定程度上減少了業務開發中(尤其是中後臺業務)對 HTML 和 CSS 的需求,但是規範 HTML 和 CSS 的代碼風格仍是必要的。基於此,可以用同樣的思路將 HTML 和 CSS 的代碼靜態檢查方案集成到當前的方案中,不再侷限於 JavaScript(或 TypeScript)。
  • 進一步的封裝:目前整體方案會將所有依賴和配置暴露在工程內,如果將其完全封裝在一個工具內會更便於應用,但難點在於兼顧靈活性、對編輯器的支持等問題。
  • 增加工程維度的代碼質量趨勢分析:目前代碼檢查策略是增量檢查,可以對接入的工程定期全量檢查,基於時間線分析工程的代碼質量變化趨勢。
  • 進一步深入分析檢查結果和統計數據,發現一些潛在問題,爲推動開發質量提升提供輔助,如:

    • 統計開發者在工程中關閉或調整的規則,分析佔比較高的規則被關閉的原因,進而調整規則或推動規則的執行。
    • 統計分佈檢查出錯誤的規則分佈,梳理出最常出問題的代碼規則,發佈對應的最佳實踐或手冊。

以上是美團外賣團隊在 ESLint 方案規模化應用過程中的一些實踐,歡迎大家提出建議,一起溝通交流。

作者簡介

宋鵬,美團外賣事業部終端研發工程師。

團隊介紹

美團外賣事業部終端團隊,負責的多個終端和平臺直接連接億萬用戶、數百萬商家和幾萬名運營與銷售,目標是在保障業務高穩定、高可用的同時,持續提升用戶體驗和研發效率。

在用戶方向上,構建了全鏈路的高可用體系,客戶端、Web前端和小程序等多終端的可用性在99%左右;跨多端高複用的局部動態化框架在首頁、廣告、營銷等核心路徑的落地,提升了30%的研發效率;

在商家方向上,從提高進程優先級、VoIP Push拉活、doze等方面進行保活定製,並提供了Shark、短鏈和Push等多條觸達通道,訂單到達率提升至98%以上;

在運營方向上,通過標準化研發流程、建設組件庫和Node服務以及前端應用的管理與頁面配置等提升10%的研發效率。

團隊有多個崗位正在招聘,歡迎加入我們,聯繫郵箱 [email protected] ,註明 “外賣終端團隊”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章