ESLint 在中大型团队的应用实践
总第346篇
2019年 第24篇
引言
代码规范是软件开发领域经久不衰的话题,几乎所有工程师在开发过程中都会遇到,并或多或少会思考过这一问题。随着前端应用的大型化和复杂化,越来越多的前端工程师和团队开始重视 JavaScript 代码规范。得益于前端开源社区的繁盛,当下已经有几种较为成熟的 JavaScript 代码规范检查工具,包括 JSLint、JSHint、ESLint、FECS 等等。本文主要介绍目前较为通用的方案——ESLint,它是一款插件化的 JavaScript 代码静态检查工具,其核心是通过对代码解析得到的 AST(Abstract Syntax Tree,抽象语法树)进行模式匹配,定位不符合约定规范的代码。
问题分析
技术场景更加广泛:对于大型团队,其开发场景一般不会局限在传统 Web 领域内,往往还会涉及 Node.js、React Native、小程序、桌面应用(例如 Electron)等更广泛的技术场景。
技术选型更加分散:团队内工程技术选型往往并不统一,如 React/Vue、JavaScript/TypeScript 等。
工程数量的增加和工程方案离散化导致 ESLint 方案的复杂度提升:这样会进一步增加工程接入成本、升级成本和方案维护成本。
在团队层面,随着人员的增加和组织结构的复杂化:
人员风格差异性更大、沟通协调成本更高。
方案宣导更难触达,难以保证规范执行的落实。
执行状况和效果难以统计和分析。
因为存在诸多差异,我们在设计具体方案时,需要考虑和解决更多问题,以保证规范的落实。针对上述分析,我们梳理了以下需要解决的问题:
如何制定统一的代码规范和对应的 ESLint 配置?
场景支撑:如何实现对场景差异的支持?如何保证不同场景间一致部分(例如 JavaScript 基础语法)的规范一致性?
技术选型支撑:如何在支撑不同技术选型的前提下,保证基础规则(例如缩进)的一致性?
可维护性:具体到规则配置上,能否提升可复用性?在方案升级迭代时成本是否可控?
如何保证代码规范的执行?
人员的增加和组织结构的复杂化,会导致基于管理的执行把控失效,这种情况应该如何保证代码规范的执行质量?
如何降低应用成本?
在工程数量增加、工程方案离散化的情况,降低方案的接入、升级和执行成本能节约大量的人力,同时也有利于方案落地推进。
如何及时了解规范应用状况和效果?
解决方案
整体方案的设计如下图所示:
多场景统一的 JavaScript 规范:该模块是整个方案的核心,借助 ESLint 的特性,通过分层分类的结构设计,在保证基础规则一致性的同时,实现了对不同场景、技术选型的支撑。
代码集成交付检查:该模块是方案落地执行的保障,将代码静态检查集成到持续交付工作流中。具体设计实现上,在保证交付质量的同时,也通过定制集成检查工具降低了开发者的应用执行成本。
自动化接入和升级方案:通过命令行工具提供“一键”接入/升级能力,同时集成到团队脚手架中,大大降低了工程接入和维护的成本。
执行状况监测分析:通过对工具运行和代码集成交付检查过程进行埋点、检查结果收集和分析,了解方案的应用状态和效果。
方案实现
通用 ESLint 配置方案
ESLint 特性简介
ESLint 的能力更像一个引擎,通过提供的基础检测能力和模式约束,推动代码检测流程的运转。原始代码经过解析器的解析,在管道中逐一经过所有规则的检查,最终检测出所有不符合规范的代码,并输出为报告。借助插件化的设计,不但可以对所有的规则进行独立的控制,还可以定制和引入新的规则。ESLint 本身并未和解析器强绑定,我们可以使用不同的解析器进行原始代码解析,例如可以使用 babel-eslint 支持更新版本、不同阶段的 ES 语法,支持 JSX 等特殊语法,甚至可以借助 @typescript-eslint/parser 支持 TypeScript 语言的检查。
规范配置方案设计
基于 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 配置集:
这种通过分层、分类的结构设计,还有利于后期的维护:
对基础层的修改,只需修改一处即会全局生效。
对非基础层某一部分的调整不会产生关联性的影响。
如需扩展对某一类型的支持,只需关注这一类型的特殊规则配置。
众所周知,TypeScript 类型的项目使用 TSLint 进行代码检查,也是一种简单、便捷的方案。但在本方案中我们依旧选择了:eslint + @typescript-eslint/parser + @typescript-eslint/eslint-plugin 的组合方案。主要有以下几点原因:
ESLint 的规则配置更加详细全面,覆盖更加广泛。
采用了分层分类的架构,能够保证即使框架或语言不同,也能在基本语法、风格层面保持规则的一致性,这样有利于团队内不同技术选型项目的风格统一。
@typescript-eslint 方案持续迭代,问题响应非常迅速,对 TSLint 相关的规则基本提供了对等的实现。
根据最新消息,TypeScript在 2019 路线图 中明确表明后续对 Lint 工具的支持和建设会以对 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 快速搭建一个代码检测服务或平台,大家有兴趣不妨一试。
自动化接入工具
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 个。
比如下图中,2019年3月第一周的代码提交检查结果统计(综合采样率 0.2),很明显,所有检查失败的提交中,错误数量在 10 个以内的占比最大,修复成本不高。
1. 提交检查异常分布(仅筛选检查未通过信息)
2. 提交检查警告信息分析
方案应用
持续维护升级:以每月一版的方式持续迭代升级,解决应用中的问题、规则争议,以及支持新的规则或方案。
工程化集成:整套方案可以无缝接入到各个团队的脚手架工具中,自动成为团队的默认方案,在工程初始化阶段即可完成接入。
官网建设:提供详细的使用文档,包括规则信息、接入方法,并且对每个版本提供规则、环境依赖、changeLog 等详细说明。
常见使用问题:更新维护FAQ,帮助后续接入者快速查找并解决问题。
截止到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)。
进一步的封装:目前整体方案会将所有依赖和配置暴露在工程内,如果将其完全封装在一个工具内会更便于应用,但难点在于兼顾灵活性、对编辑器的支持等问题。
增加工程维度的代码质量趋势分析:目前代码检查策略是增量检查,可以对接入的工程定期全量检查,基于时间线分析工程的代码质量变化趋势。
进一步深入分析检查结果和统计数据,发现一些潜在问题,为推动开发质量提升提供辅助,如:
统计开发者在工程中关闭或调整的规则,分析占比较高的规则被关闭的原因,进而调整规则或推动规则的执行。
统计分布检查出错误的规则分布,梳理出最常出问题的代码规则,发布对应的最佳实践或手册。
作者简介
宋鹏,美团外卖事业部终端研发工程师。
About 团队
美团外卖事业部终端团队,负责的多个终端和平台直接连接亿万用户、数百万商家和几万名运营与销售,目标是在保障业务高稳定、高可用的同时,持续提升用户体验和研发效率。
在用户方向上,构建了全链路的高可用体系,客户端、Web前端和小程序等多终端的可用性在99%左右;跨多端高复用的局部动态化框架在首页、广告、营销等核心路径的落地,提升了30%的研发效率;
在商家方向上,从提高进程优先级、VoIP Push拉活、doze等方面进行保活定制,并提供了Shark、短链和Push等多条触达通道,订单到达率提升至98%以上;
在运营方向上,通过标准化研发流程、建设组件库和Node服务以及前端应用的管理与页面配置等提升10%的研发效率。
---------- END ----------
美团外卖事业部终端团队是由一群活力四射,对技术饱含热情,平均年龄不超过26岁的人共同组成。在这里,你可以看到大家对技术的追求,对产品的雕琢,对团队的认同,一切都是那么自然;在这里,你可以做一个纯粹的FE,写写JS,打磨一下CSS;也可以做一个精致的猪猪女孩(男孩),优雅的调试 Android 和 iOS 代码。当然,你绝对可以做一个霸道总裁,肆意的拥抱跨端方案,Hybrid,Flutter,React Native 等,都是你的新战场。我们正在持续努力成为一个面向未来编程的团队,而这里还缺一个你。欢迎志同道合的同学发送简历到:tech@meituan.com。