ESLint 在中大型团队的应用实践

引言

代码规范是软件开发领域经久不衰的话题,几乎全部工程师在开发过程当中都会遇到,并或多或少会思考过这一问题。随着前端应用的大型化和复杂化,愈来愈多的前端工程师和团队开始重视 JavaScript代码规范。得益于前端开源社区的繁盛,当下已经有几种较为成熟的 JavaScript 代码规范检查工具,包括 JSLint、JSHint、ESLint、FECS 等等。本文主要介绍目前较为通用的方案——ESLint,它是一款插件化的 JavaScript 代码静态检查工具,其核心是经过对代码解析获得的 AST(Abstract Syntax Tree,抽象语法树)进行模式匹配,定位不符合约定规范的代码。javascript

ESLint 的使用并不复杂。依照 ESLint 的文档安装相关依赖,能够根据我的/团队的代码风格进行配置,便可经过命令行工具或借助编辑器集成的 ESLint 功能对工程代码进行静态检查,发现和修复不符合规范的代码。若是想下降配置成本,也能够直接使用开源配置方案,例如eslint-config-airbnbeslint-config-standard前端

对于独立开发者,或者执行力较强、技术场景较为单一的小型团队而言,直接使用 ESLint 及其生态提供的一些标准方案,能够用较低成原本实现 JavaScript 代码规范的落地。若是再搭配一些辅助工具(例如 husky 和 lint-staged),整个流程会更加顺畅。但对于数十人的大型前端团队来讲,面向数百个前端工程,规模化地应用统一的 JavaScript 代码规范,问题就会变得较为复杂。若是直接利用现有的开源配置方案,可能会使工做事倍功半。java

问题分析

规模化应用统一的 ESLint 代码规范,会涌现各种问题,根源在于大型团队和小团队(或独立开发者)的差别性:react

技术层面上:git

  • 技术场景更加普遍:对于大型团队,其开发场景通常不会局限在传统 Web 领域内,每每还会涉及 Node.js、React Native、小程序、桌面应用(例如 Electron)等更普遍的技术场景。
  • 技术选型更加分散:团队内工程技术选型每每并不统一,如 React/Vue、JavaScript/TypeScript 等。
  • 工程数量的增长和工程方案离散化致使 ESLint 方案的复杂度提高:这样会进一步增长工程接入成本、升级成本和方案维护成本。

在团队层面,随着人员的增长和组织结构的复杂化:github

  • 人员风格差别性更大、沟通协调成本更高。
  • 方案宣导更难触达,难以保证规范执行的落实。
  • 执行情况和效果难以统计和分析。

由于存在诸多差别,咱们在设计具体方案时,须要考虑和解决更多问题,以保证规范的落实。针对上述分析,咱们梳理了如下须要解决的问题:typescript

  • 如何制定统一的代码规范和对应的 ESLint 配置?npm

    • 场景支撑:如何实现对场景差别的支持?如何保证不一样场景间一致部分(例如 JavaScript 基础语法)的规范一致性?
    • 技术选型支撑:如何在支撑不一样技术选型的前提下,保证基础规则(例如缩进)的一致性?
    • 可维护性:具体到规则配置上,可否提高可复用性?在方案升级迭代时成本是否可控?
  • 如何保证代码规范的执行?json

    • 人员的增长和组织结构的复杂化,会致使基于管理的执行把控失效,这种状况应该如何保证代码规范的执行质量?
  • 如何下降应用成本?小程序

    • 在工程数量增长、工程方案离散化的状况,下降方案的接入、升级和执行成本能节约大量的人力,同时也有利于方案落地推动。
  • 如何及时了解规范应用情况和效果?

解决方案

为了能在团队内实现 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%的研发效率。

团队有多个岗位正在招聘,欢迎加入咱们,联系邮箱 tech@meituan.com ,注明 “外卖终端团队”。

相关文章
相关标签/搜索