- 前端工程质量保障体系实践
- 曾静益
- 6672字
- 2024-02-27 16:14:02
1.2 建立前端工程质量保障体系
1.2.1 说明
前端工程化是随着前端能力范围逐步扩大后兴起的一个概念,因为前端不再是单纯的静态页面,它甚至能脱离传统的Java、PHP等后端,自行使用Node.js实现服务端的功能。前端能力多元化固然值得庆幸,但是当前端工程变得复杂以后就将面临以下问题。
• 如何搭建一个能力符合预期的前端?
• 如何在多人开发时进行有效协作?
• 如何保障项目的代码质量?
• 如何建设基础的公共组件库?
• 如何在持续集成阶段保障工程质量?
• 如何在测试阶段保障工程质量?
• 如何在系统部署阶段将发布风险降到最低?
• 如何监控生产环境的页面运行状态?
• 如何建立指标大盘实时反馈生产环境的工程质量?
• 如何对工程质量进行持续优化?
前端工程本质上是软件工程的一种。软件工程化关注的是性能、稳定性、可用性、可维护性等方面,在注重基本的开发效率、运行效率的同时,思考维护效率。为了使规模大、结构复杂和管理复杂的软件开发变得容易控制和管理,人们把整个软件生命周期划分为若干阶段,每个阶段都有明确的任务。
前端工程质量保障就是以前端工程化为前提,建立一套有计划、有系统的方法。简单来说,前端工程质量保障是一套体系化的解决方案而不是某种技术,它能够覆盖前端的研发生命周期,包括工程搭建、功能研发、测试保障、发布上线、运行维护。具体到不同生命周期的不同环节,可以有不同的表现形式。例如,在功能研发的编码环节,它可以是编码规范,一套良好的编码规范可以有效增强团队开发协作效率、提高代码质量。比如,使用语义化的HTML标签,符合团队的文件命名规范,可隔离的CSS命名规范(例如BEM)等。
1.2.2 工程搭建
工程搭建是前端项目必经的过程,其中最主要的就是技术方案的调研选型,通常会涉及基础研发框架、打包构建工具、网络库、组件库、CSS预编译语言、状态管理、IDE、代码质量检查工具、包管理工具、版本控制工具等。
工程质量除了依赖开发人员的个人技术能力,很大程度上还受工程搭建初期方案选型是否全面、合适的影响。比如若工程搭建初期没有考虑CSS隔离方案,研发过程中就可能出现CSS样式相互覆盖的问题。
本书第2章将概述主要方案的选型,细化后的质量保障手段将在后续章节中逐一进行讲解。
1.2.3 版本控制
版本控制指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是工程化管理里至关重要的一个环节,功能如下。
• 实现跨区域多人协同开发。
• 追踪和记载一个或者多个文件的历史记录。
• 组织和保护源代码及文档。
• 统计工作量。
• 并行开发,提高开发效率。
• 跟踪记录整个软件的开发过程。
• 减轻开发人员的负担,节省时间,同时减少人为错误。
版本控制最主要的功能就是追踪文件的变更。它将什么时候、什么人更改了文件的什么内容等信息忠实地记录下来。每一次改变文件,文件的版本号都会增加。除了记录版本变更,版本控制的另一个重要功能是并行开发。软件开发往往是多人协同作业的,版本控制可以有效地解决版本的同步以及不同开发人员之间的开发通信问题,提高协同开发的效率。并行开发中最常见的不同版本软件的错误(Bug)修正问题也可以通过版本控制中分支与合并的方法有效地解决。
具体来说,在每一项开发任务中,都需要首先设定开发基线,确定各个配置项的初始版本,在开发过程中,开发人员基于开发基线的版本,开发出所需的目标版本。当发生需求变更时,开发人员通过对变更进行评估,确定变更的影响范围,对被影响的配置项的版本进行修改,根据变更的性质使配置项的版本树继续延伸或产生新的分支,形成新的目标版本,而对于不受变更影响的配置项则不做修改。同时,记录和跟踪变更对版本的影响,必要时还可以回退到以前的版本。例如,当开发或变更需求被取消时,需要将版本回退到开发基线。曾经出现过的季度升级包拆包和重新组包的过程,其实就是将部分配置项的版本回退到开发基线,将对应不同需求的不同分支重新组合归并,形成新的升级包版本。
版本控制工具有Git、SVN(Subversion)、CVS(Concurrent Versions System)、VSS(Microsoft Visual SourceSafe)、Visual Studio Online等,其中Git占据绝对优势地位,本书第3章将围绕Git讲解版本控制相关内容。
1.2.4 代码质量
代码质量一般指代码本身的质量,包括复杂度、重复率、代码风格等。代码是团队的共同财产,代码质量是团队技术水平和管理水平的直接体现。代码质量下降通常会自成因果,导致恶性循环。
• 破窗效应:在烂代码上继续生产烂代码,开发人员的心理负担会小很多。
• 传染性:烂代码传递着一种不在意质量,只看业务成果的负面信息,会伤害团队的技术热情和工作氛围,导致更多烂代码出现。
代码质量过硬则能带来以下好处。
• 在发生问题时,能够帮助开发人员快速理解和定位。
• 可以加快应用的迭代速度,不必花费过多的时间来修复Bug和优化代码逻辑。
• 能够帮助新的项目开发成员更快、更容易地融入项目开发中。
• 便于项目组不同开发成员之间快速做好承接。
• 有效促进团队间交流合作,提升开发效率。
对于软件项目来说,代码质量代表着项目的有序程度,烂代码增加是项目无序性上升的表现。在无外力影响的情况下,烂代码只会越来越多。为了保证项目质量不持续下降,需要主动采用技术或者管理手段来缓解甚至抑制烂代码越来越多的趋势。
烂代码产生的常见原因是业务压力大,开发人员没有时间或意愿追求代码质量。在向业务压力妥协产生烂代码后,开发效率会下降,导致业务压力增加,形成恶性循环。为了缓解业务压力,常见的做法是增加人力,但是单纯地增加人力,会因为工作风格不一致、沟通成本上升等原因导致烂代码更多。
要遏制这种恶性循环,既需要主动对代码质量进行管控,也要持续进行技术升级,才能系统性地解决问题。
不过两种方案都需要长期投入才能产生效果。在通常情况下,人们倾向于通过增加人力快速解决业务压力的问题,而忽略了代码质量下降产生的负面影响,导致代码质量越来越差。
代码质量可以采用传统的业界标准进行衡量,比如编码规范、可读性、可维护性、代码重复率及可测试性。
• 编码规范:主要包括是否遵守了最佳实践和团队编码规范,是否包含可能出问题的代码,以及可能存在的安全漏洞。编码规范有助于提高团队协作的效率以及代码的可维护性。
• 可读性:Code Review是一个很好的检测代码可读性的手段。如果同事之间可以轻松地读懂彼此写的代码,就说明当前工程的代码可读性很好;反之则说明代码可读性有待提高。遵守编码规范也能让开发人员写出可读性更好的代码。
• 可维护性:代码的可维护性是由很多因素协同作用的结果。代码的可读性好、简洁、可扩展性好,就会使得代码易维护;更具体地讲,如果代码分层清晰、模块化好、高内聚低耦合、遵从基于接口而非实现编程的设计原则等,就可能意味着代码易维护。除此之外,代码的易维护性还与项目代码量的多少、业务的复杂程度、用到的技术的复杂程度、文档是否全面等诸多因素有关。
• 代码重复率:遵守DRY(Don't Repeat Yourself)原则,尽量减少重复代码的编写工作,复用已有的代码。定期对项目进行代码重复度检测是一件很有意义的事,可以帮助开发人员发现冗余代码,进行代码抽象和重构。重复的代码一旦出错,就意味着大量的工作和持续的不可控。如果代码中有大量的重复代码,就要考虑将重复的代码提取出来,封装成公共的方法或组件。
• 可测试性:代码的可测试性同样可以反映代码质量的好坏。代码的可测试性差,比较难写单元测试,基本上就能说明代码设计得有问题。
根据以上5个原则,可以在不同阶段进行应对和治理。在开发时,在团队中建立代码规范制度,例如,代码缩进、注释风格等。在代码提交时,基于代码规范使用自动化工具进行代码质量检查,例如,代码规范、圈复杂度、代码重复率等。在代码评审时,从业务逻辑角度入手,评估需求还原度,针对异常边界情况提出疑问,帮助代码编写人员进行自我审查。
本书第4章将进行展开叙述,具体讲解有哪些手段可以保障代码质量。
1.2.5 组件建设
组件是对公共模块的封装实现,可以有效解决前端项目的复用性问题,通常这些公共模块都是项目中使用频率特别高或者具有共性的模块。组件建设一方面可以方便跨项目的组件复用,减少重复的代码;另一方面,可以方便团队分工协作,最终目的都是为了提高研发质量,保障研发效率。
在进行组件建设时,应当遵循以下原则。
• 高内聚:尽可能使得每个组件只完成一件事(最大限度地聚合)。
• 低耦合:组件与组件之间是低耦合的,只要遵循组件规范,就可以用一个组件轻易地替换另一个组件。
• 可复用性:组件的功能明确,实现清晰,API易于理解,可以在其他业务中被重复使用。
大多数人对前端组件的认知仅停留在视图组件上,即肉眼可以捕捉的内容。实际上,按照不同的维度进行划分,可以得到以下不同结果。
当按照组件的复杂度进行划分时,可以划分为:
• 原子组件:颗粒度最细的组件,无法再继续拆分,例如,Button、Switch等。
• 复合组件:基于原子组件进行拼接得到的新组件,例如,ModalTable就是由modal和table拼接的复合组件。
当按照组件的可视化进行划分时,可以划分为:
• 视图组件:在页面上渲染出来,直接呈现在屏幕上的组件,能被肉眼捕捉到。
• 功能组件:无法通过肉眼捕捉的组件,例如,时间格式化方法、状态管理等。
当按照组件的功能领域进行划分时,可以划分为:
• 通用组件:可以跨业务项目实现复用的组件,例如,视图组件中的Button,功能组件中的状态管理等。
• 业务组件:一般具有业务属性,只有在特定的业务模块下才可以使用,例如,不同业务中的业务选择器等。
除了以上3种模式,还可以依据业务特性和个人认知,从不同角度去划分。
本书第5章主要针对UI组件的建设,分别从组件规范、目录结构、样式主题、国际化、组件测试、文档管理、构建打包以及发布规范等方向进行讲解。
1.2.6 测试保障
软件测试是伴随着软件产生的。早期的软件规模小、复杂程度低,软件开发的过程混乱无序,测试包含的范围比较窄。开发人员将测试等同于“调试”,目的是纠正软件中已经知道的故障,常常自己完成这部分工作。测试工作投入少、介入晚,常常要等到产品基本完成时才进行测试。
到了20世纪80年代初期,软件和IT行业进入了大发展阶段,软件趋向大型化、复杂化,软件的质量越来越重要。这个时候,一些软件测试的基础理论和实用技术开始形成,人们开始为软件开发设计了各种流程和管理方法,软件开发的方式也逐渐由混乱无序向结构化过渡,以结构化分析与设计、结构化评审、结构化程序设计以及结构化测试为特征。人们还将“质量”的概念融入其中,使得软件测试不再是一个单纯发现错误的过程。同时,软件测试开始包含软件质量评价的内容,Bill Hetzel在《软件测试完全指南》一书中指出,测试是以评价一个程序或者系统属性为目标的任何一种活动,测试是对软件质量的度量。这个定义至今仍被引用。软件开发人员和测试人员开始坐在一起探讨软件工程和测试问题。
软件测试指使用人工或自动的手段来运行或测定某个软件的过程,以检验软件是否满足需求或找出预期结果与实际结果之间的差别,具体包括发现软件中的错误,对软件是否符合设计要求进行验证并评估软件的质量等,最终将高质量的软件交付给用户。
前端测试是软件测试的一部分,用来保障质量,提高系统可维护性。
本书第6章将结合实际开发经验,详细讲解测试保障中的流程划分并介绍测试的手段、平台和工具。
1.2.7 持续集成
持续集成是一种软件开发实践。通常,开发团队的每个成员每天至少集成一次自己的工作,这意味着每天都会发生多次集成。每次集成都会通过自动化构建(包括编译、发布、自动化测试)来验证,从而尽早地发现集成错误。
持续集成的宗旨是避免集成问题,提升软件质量并缩短交付时间。持续集成应遵循以下原则。
• 所有开发人员都需要做本地构建,然后提交到版本控制库中,从而确保变更不会导致持续集成失败。
• 开发人员每天至少向版本控制库提交一次代码。
• 开发人员每天至少需要从版本控制库中更新一次代码到本地机器。
• 有专门的集成服务器执行集成构建,每天执行多次。
• 每次构建都要100%通过。
• 每次构建都可以生成可发布的产品。
• 修复失败的构建是优先级最高的事情。
• 测试是未来,未来是测试。
前端常见的持续集成手段有单元测试、代码检测、自动化测试、代码质量检测等,本书第7章会详细讲解。
1.2.8 系统部署
系统部署指将研发的功能和线上缺陷修复部署到实际生产环境中。传统的软件工程中不包括系统部署,但不断增长的系统复杂度和部署所面临的风险,迫使开发人员开始关注系统部署。
系统部署是一个复杂过程,整个流程中存在着风险,这是由以下原因造成的:应用软件越来越复杂,包括许多构件、版本和变种;应用发展很快,版本发布的间隔很短(可能只有几个月);环境的不确定性等。
前端的系统部署起初是一个非常简单的流程——直接将静态文件提交到服务器即可。随着互联网的发展,企业为了提升用户体验,开始追求交付质量、性能及系统稳定性。例如,为了提升网站性能,把静态资源和动态网页分集群部署,静态资源会被部署到CDN节点上;为了控制新功能上线的影响面,做代码灰度、A/B分流;当新功能上线出现缺陷时,要能够快速进行代码回滚。
随着系统部署需要考量的方面越来越多,单纯依赖人工进行管理的成本将非常高昂,并且非常容易出错。因此,需要借助平台化的能力来串联发布能力,将发布链路中的各个环节结合起来,由此诞生了部署平台。
借助部署平台,开发人员可以通过可视化的方式进行一键部署,直接发布任务。在整个发布过程中,平台会将各个环节的运行信息和日志返回。借助部署平台,开发人员在跟踪发布流程和排查发布问题等方面的体验和效率将大大提升。
本书第8章将介绍系统部署时的质量保障手段以及如何设计一个简单可用的系统部署平台。
1.2.9 页面监控
页面是前端直接呈现给用户的界面,用户依赖页面与应用进行交互。当页面发生卡顿时,会引起用户的焦虑情绪;而当页面发生白屏时,用户与应用的交互将被直接阻断,导致线上缺陷。除此之外,对于页面中的一些不阻塞交互的JavaScript报错,也需要进行关注,在问题爆发前将其解决。
页面监控包括性能监控、异常监控、白屏监控、卡顿监控、用户行为监控,本书第9章节将介绍实现这些功能的具体方法。
1.2.10 请求监控
前端页面主要通过请求与服务端进行数据交互,因此请求在前端页面中扮演着至关重要的角色。请求监控主要包括以下内容。
• 请求拦截:通过拦截器的形式采集请求数据。
• 高并发请求:一定时间范围内发送的请求数量过多。
• 重复请求:一定时间范围内发送的同一请求的数量过多。
• HTTP状态码:HTTP请求报文中的状态码。
• 高延迟请求:请求从发出到返回超过指定时间范围。
• 被取消的请求:业务发起后由于某些原因被取消。
• 业务异常请求:请求返回业务异常。
• 爬虫请求:监听非正常提交的页面请求。
本书第10章将介绍如何针对上述内容实现监控,从而保障前端工程质量。
1.2.11 资源保障
前端页面依赖的是若干静态资源,包括HTML、JavaScript、CSS、图片、SVG等。当CSS、图片、SVG等文件出现问题时,通常只会导致页面使用体验下降,而当HTML、JavaScript等文件出现问题时则可能直接导致整个前端服务不可用。资源问题主要包括加载失败、加载时间过长、资源劫持等,因此资源保障也是前端质量建设中极其重要的一个环节。
在访问量巨大的前端应用中,任何小的问题都会被放大。例如,某一地区的CDN出现问题,导致资源加载失败,引起页面白屏,那么很可能导致该地区用户投诉。面对这种问题,前端可以通过资源保障手段有效减少因资源问题引起的用户流失与业务损失,提升前端页面的可用性与稳定性。
常见解决方案是全局监听资源加载事件,当资源加载失败时,将资源链接降级到备用资源尝试重新加载,并将对应事件上报到前端监控系统,前端开发人员和网站可靠性工程师(Site Reliability Engineer,SRE)根据报错的资源、数量、地区等,进行综合排查定位,看是个例问题还是CDN出现问题,最后给出对应解决方案。
更多与资源相关的问题和保障手段将在本书第11章进行详细讲解,这里不再赘述。
1.2.12 工程质量优化
工程质量优化一般指通过技术手段来改善现有的工程质量问题,例如,提升构建速度、减小资源压缩包体积等。本书将以webpack构建工具为例介绍工程质量优化手段。
提高构建速度的主要手段如下。
• 合理配置include和exclude,减少loader和plugin需要处理的文件数量。
• 使用DLL,避免重复构建三方库。
• 使用并行构建方案。
• 缓存构建结果。
减小资源压缩包体积的主要手段如下。
• 使用Tree Shaking,删除冗余代码。
• 代码体积压缩,例如,消除代码缩进、变量名压缩等。
• 作用域提升(Scope Hoisting),将引入的JavaScript文件的内容提升到它引入的顶部,缩减代码体积。
• 代码分割(Code Splitting),例如,代码按需加载、DLL等。
使用其他构建工具也可以参考上面的思路寻找对应的解决方案。
除构建上的优化外,还可以针对页面性能进行优化,例如,CDN加速、按需加载、预编译优化、服务端渲染等,都可以提升工程质量的可靠性,本书第12章会进行详细讲解。