1.4 如何可视化管理代码质量
项目启动后,接下来的紧张气氛来到了我们效能团队这里。我从其他团队临时拉了两个研发人员来配合工作,这也是效能团队的初创成员。
技术债务又称技术债,是编程及软件工程中的一个比喻,指开发人员为了加速软件开发,在应该采用最佳方案时进行了妥协,改用短期内能加速软件开发的方案。不过,许多我们归咎于技术债务的事情实际上根本不是债务。例如,随着系统使用年限增加,团队无法一直保持最佳的编码实践,在这种情况下团队增加额外工作量是正常的,不是债务。
首先,我们的第一件大事就是量化每个团队的技术债务。
1.4.1 静态代码质量量化指标
SQALE(Software Quality Assessment based on Lifecycle Expectation,基于生命周期期望的软件质量评估)是一种支持软件应用程序源代码(静态代码)评估的方法。它是一种通用方法,独立于语言和源代码分析工具。
研发人员肯定都知道SonarQube(简称Sonar)平台。该平台就是基于SQALE方法,通过设置代码规则和指标统计来分析和评估代码质量的,并能够量化项目技术债务。
我们使用的是Sonar社区版本,其中很多功能受限,比如不支持多分支扫描、无法配置新增指标计算的开始时间等。这些功能实现网上都有一定的替代方案,这里不再详细讲述。
关于指标计算和代码质量等级划分,这些是一线研发人员最爱钻牛角尖的地方,所以我们必须要先搞明白。
1.代码质量规则配置
通过Sonar平台,我们可针对不同编程语言设置代码质量规则,并对代码质量问题进行分类。代码质量问题可分为漏洞、缺陷、坏味道。通过分类及相关的指标反馈,我们可评估代码的安全性、可靠性和可维护性。
2.代码质量指标以及计算方法
技术债务、技术债务比率、单测相关指标、重复数等可反馈出代码质量问题。
(1)技术债务
Sonar平台通过扫描项目静态代码,按照一定公式进行项目技术债务的计算。技术债务反映出修复所有代码问题(不仅包含扫描出来的静态代码问题,还包括代码运行时的逻辑问题)耗费的时间。该指标值以分钟为单位,存储在数据库中。(一天按照8h计算,默认修复一行代码耗时30min,可以在平台的技术债务配置选项中修改这个基准值。)
(2)技术债务比率
该指标值是开发软件的成本与修复技术债务的成本的比(默认开发一行代码的成本为0.06天,可以在平台参数配置中修改这个基准值),反映解决技术债务所付出的成本。
(3)单测相关指标
单测覆盖率=[可覆盖行+可覆盖分支-(未覆盖代码行+未覆盖分支)]/(可覆盖行+可覆盖分支)
该指标值反映单测覆盖了多少源代码的行和分支。
可覆盖分支具体解释如下。
❑ If语句中一个条件被认定为两个分支,即一个if语句的分支数=条件数×2;
❑ 一个for循环被认定为两个分支;
❑ ||、&&运算符被认定为(操作数×2)个分支。
单测用例数:单元测试的用例数量。
单测耗时:执行所有单元测试用例所持续的时间。
(4)重复数
重复数包括重复文件的数量,重复代码的行数。
在检测重复时,忽略缩进和字符串文字的差异。
3.代码质量等级划分
详细内容可参考官方文档:https://sonar.yummy.tech/documentation。
(1)代码可靠性级别
通过缺陷数可划分代码可靠性等级,具体如下。
A:可靠性为A,没有缺陷;
B:可靠性为B,至少有一个次要缺陷;
C:可靠性为C,至少有一个重要缺陷;
D:可靠性为D,至少有一个严重缺陷;
E:可靠性为E,至少有一个阻断缺陷。
(2)代码安全性级别
通过漏洞数可划分代码安全性级别,具体如下。
A:安全度为A,没有漏洞;
B:安全度为B,至少有一个次要漏洞;
C:安全度为C,至少有一个重要漏洞;
D:安全度为D,至少有一个严重漏洞;
E:安全度为E,至少有一个阻断漏洞。
(3)代码可维护性级别
根据修复代码异味计算出来的技术债务比率,我们可进行代码可维护性评级,具体如下。
A:比率≤5%,评级为A;
B:比率在6%到10%之间,评级为B;
C:比率在11%到20%之间,评级为C;
D:比率在21%到50%之间,评级为D;
E:比率超过50%,评级为E。
所以,我们可以将对项目多维度下的代码质量等级要求,作为项目代码质量提升的目标。
1.4.2 搭建可视化数据分析平台
Sonar平台是针对项目维度进行分析的,而我们还会在技术中心、部门、人员等维度进行统计分析,若想进行多维度分析,需要搭建可视化数据分析平台。此时,效能团队开启了平台规划之旅。
最初,我们为了快,仅仅把Sonar平台中的数据进行打标签和字段扩展,经过数据二次加工后转存到新的数据库,通过Grafana(一个跨平台的开源度量分析和可视化工具)进行前端展示。因为平台的使用比较简单,此过程相对比较顺利。
大概经过2个月时间,我们发现一个很大的问题:随着度量指标计算越来越复杂,若要整合计算一个指标可能需要跨库进行多表关联查询,执行效率和查询体验非常不好。比如一个需求下P0级别的测试用例通过率,需要联动项目管理平台、测试平台以及流水线平台中对应的数据库,经过复杂的表关联逻辑计算得到。
经过一段时间的尝试,以及考虑到DevOps全链路平台的可扩展性,我们规划了一个所谓的“数据中台”(其实就是一个数据仓库),这样以后规划的各平台都可以通过这个中台获取并存储数据。此部分内容将在第4章进行详细讲解。
接下来,我们就要选取指标了。
如图1-1所示,这些指标是经过与技术团队多次讨论,并通过实践证实可有效度量的指标。可将这些指标作为代码质量度量的指标体系。
图1-1 代码质量度量指标体系
1)代码缺陷:代码质量可以通过观察服务运行时的效果来判断,最直接的就是分析测试出来的缺陷。
服务运行时的效果可反映出业务逻辑的正确与否。若代码质量较差,线上环境下问题就会很多。于是,我们可以在不同的时间段,按照团队、项目、产品线等多维度分析缺陷情况,并分析各维度指标的趋势,如图1-2所示。代码缺陷是一个全局性指标,用来初步识别和筛查问题。
图1-2 代码缺陷概况分析
我们还可以根据优先级和运行环境进行缺陷分布分析。图1-3所示为根据优先级进行缺陷分布分析。
图1-3 缺陷分布分析
2)发布故障系数:聚合分析指标。
服务发布故障系数=服务累计发布次数/服务累计上线工单数,其中,服务的一次部署和发布需要通过至少一次上线工单申请。
团队发布故障系数=团队服务累计发布总数/团队服务累计上线工单总数
发布故障系数反映了一段时间内某团队或服务在生产环境下一次性部署和发布成功的能力。所以,这个指标主要用来指导团队进行问题的回顾和复盘。
发布故障系数不为1,则说明服务上线过程中出现多次部署情况,也说明出现了代码质量问题。不过,我们也需要多维度去分析问题产生的原因,比如,人为操作失误、沟通不充分、测试不到位、中间件故障、环境不稳定、部署平台故障等。
团队维度下的发布故障系数示例如图1-4所示。
图1-4 团队维度下的发布故障系数示例
3)代码单测:代码单元测试相关指标。
我们将单元测试覆盖率指标独立出来,是因为这个指标不仅是一个局部性指标,也是一个持续性和多维度指标。该指标进一步可细化为5个指标:全量代码单测覆盖率、新增代码单测覆盖率、单测用例数、单测用例执行耗时、代码行数。
该指标用来引导研发人员了解哪些业务逻辑代码被单测用例覆盖到。对于未被覆盖到的代码,测试与开发人员需着重进行测试和关注。按照金字塔分层测试模型,单测指标也可反映技术团队最基础的自动化测试水平。其中,单测用例执行耗时指标的考量在于:某个项目的单测用例执行耗时较长,将导致项目构建流水线的整体耗时较长,进而使研发人员因等待时间过长而失去频繁构建的信心。
一般来说,单元测试工程实践在技术团队不好落地。第2章将会详细讲解实践方法。
我们可按照团队、项目分支、不同时间段以及不同指标维度进行单测指标分析。图1-5所示为团队维度下代码单测覆盖率分析示例。
图1-5 团队维度下代码单测覆盖率分析示例
4)代码质量:引入能通过Sonar平台扫描统计出来的静态代码质量指标,核心选取的指标包括代码漏洞数、缺陷数和坏味道数,进而评估各团队的静态代码质量等级。
通过在可视化平台上设置代码等级要求,将一些不符合要求的团队、项目、分支过滤出来。比如ABB等级(安全性A级、可靠性B级、可维护性B级)代表代码质量良好的服务。这是一个局部过程性指标,可协助一线研发人员多维度分析代码质量问题。
与此同时,研发人员可点击可视化平台上的每个服务链接,了解此项目对应分支的最新一次代码质量详情;而点击分析结果中的任何一个问题,Sonar平台可给出问题产生的原因和建议的解决方法。
团队维度下代码质量分析示例如图1-6所示。
图1-6 团队维度下代码质量分析示例
1.4.3 代码质量可提升的故事
一个故事至少得有两条线:一条主线和一条辅线,主辅线交织,方能有效控制节奏,一张一弛才能讲出生动的故事。我们要能站在旁观者视角,根据故事的发展情节,协助主角厘清情节发展脉络、寻找问题根因、提供解决方法。为了加深观影者的视觉感知和记忆(抑或警示),我们还需要周期性地植入回忆片段。
下面我们先梳理一下代码质量提升故事的主线和辅线,根据故事的情节发展,让各角色都能合理地参与进去,即为各角色选取合适的观察指标,让他们能够根据可视化平台自助分析团队问题,通过趋势图分析和团队历史改进情况,协助各团队持续改进。
1.故事主线之代码生命周期
代码生命周期一般包括5个过程:编写、编译、打包、运行和下线,这也是从需求交付到价值交付的全过程,如图1-7所示。
图1-7 代码生命周期
前4个过程对应的核心活动为:编写单测用例、发现和修复代码问题、测试发布上线、观察线上运行效果。
此时,代码单测、代码质量、发布故障系数以及代码缺陷(包含线上问题)等指标,就是我们要关注的核心指标,如图1-8所示。通过这些指标值和发展趋势,我们便可驱动技术团队发现问题,并能够根据不同角色和不同场景提供相应的解决方法。所以,周期性地改进技术团队工程实践方法,并合理地改善这些指标值是我们的使命。
图1-8 围绕代码生命周期的代码质量指标
于是,我们和技术团队之间也有了共同的目标:让每次代码的发布上线都能满足业务需求并稳定运行,并形成以代码生命周期的过程管理为主线,以各阶段的全局性和局部性指标改善为辅线的故事发展态势。
2.故事情节之面向“对象”实施不同的策略
各个角色如何参与到故事中非常重要,如何协调各角色参与到不同的故事场景也很重要。我们负责合理地安排不同角色参与到不同场景,并帮助解决场景中的问题。这样才能基于故事的主线,顺利推动故事情节发展。
下面让我们介绍如何面向“对象”实施不同的策略,对象具体包括CTO、部门负责人、团队负责人或小组负责人以及一线研发人员。
1)首先是CTO,他一般不关注细碎的指标、问题解决的具体过程以及采用了哪些详细的解决方案。
所以,我们与其沟通并选择了3个“上层建筑型指标”,告知每个指标背后的含义。从此,这3个指标就作为代码质量委员会每次周会的议题。有了CTO的加持,成功的概率变大。
选择的3个指标为发布故障系数、优秀单测项目的比例、高质量代码项目的比例(支持下钻分析各部门、团队、产品线维度的指标),这些指标都是全局指标。当然,选择的这些指标会不断地变化,以突出不同时期关注的重点。且不同时期对优秀(比如全量代码单测覆盖率超60%、单测用例平均执行耗时小于200ms)、高质量(比如新增代码质量级别达到ABB、线上P0级Bug小于10个)的定义和标准也可能会发生变化。
从此,CTO对技术团队的要求为:每个OKR周期,3个指标要提升到什么程度,对应各技术部门和团队的OKR应达到什么程度。
2)其次是部门负责人,他们关注的是整个部门以及部门下各团队的代码质量问题。
所以,我们整理了一些部门内和部门间相关的指标,与其沟通并选择了3个指标作为部门负责人在后续代码质量委员会周会上汇报的内容。有了各部门负责人的协助,成功的脚步变快。
选择的3个核心指标为技术债、代码缺陷、发布故障系数(支持下钻分析团队、产品线、服务等维度的指标)。
同时,我们选择了3个辅助性指标:代码单测、代码质量等级、代码质量,协助多维度分析部门整体情况。
从此,部门负责人向上可承诺每个OKR周期部门要提升的指标以及改善的环节等;向下可基于各团队现状要求提升哪些指标,提升到什么水平,进而根据指标决定引入哪些工程实践,提升团队哪方面的能力。
从部门管理者视角看,有时候向下管理可能比向上管理更需要“证据”。当发现团队有问题时,部门负责人不能只说团队差,要说出具体差在哪里。而效能团队在一定程度上帮助部门负责人解决了找“证据”的难题。
3)接着是团队负责人或小组负责人,他们需要关注各小组所负责的服务的代码质量。(对于微服务,一个系统包含多个服务,一个服务的维护可能需要多个开发成员,一个系统基本是一个底层“作战单元”。)
选择的3个核心指标为代码质量、代码单测、发布故障系数(支持下钻分析服务、人员等维度的指标)。
上文有详细的示例图,这里不再详述。
有了这些指标,团队负责人或小组负责人可实时感知具体哪个系统中的哪些服务出现了哪些方面的问题,找到改进的方向。他们作为战斗单元的“游击队长”,可根据指标现状,灵活调整改进方案,及时向上汇报改进效果。
4)最后是一线研发人员,他们需要关注自己负责的服务(一般一个研发人员负责一个服务,能力强者可能会负责多个服务),并且需要关注每个服务的代码细节问题。
所以,我们整理了3个核心指标:代码质量、代码单测、流水线构建耗时。
流水线构建耗时长非常影响研发人员频繁构建的信心,同时长时间等待也会让研发人员误认为是CI/CD平台的性能问题等。流水线构建耗时长的原因需要结合CI/CD平台、Kubernetes(简称K8S)集群、代码问题、单测质量、基础镜像等多维度进行分析。
同时,我们选择了3个辅助性指标:新增代码质量、新增代码单测覆盖率、新增代码技术债务。
针对存量代码规模比较大的服务,我们很难在一段时间范围内一次性解决所有问题,因此给出的策略就是:重点把控新增的问题,小步解决存量问题。
从此,一线研发人员可根据与各团队负责人达成的目标,设置好每周新增和存量代码的质量改进指标;同时,可以结合效能团队培训的工程实践方法,提高自身的工程实践能力,拓宽知识边界。
畅想一下:在各位“游击队长”的带领下,有底层“民众”的支持,再加上高智慧的“计策参谋”,肯定可以在“将帅”的领导下走向成功。
3.故事推波助澜之趋势图
就像看电影一样,在了解了故事的前后脉络,掌握了故事主线,知道哪些角色是坏人,哪些角色是好人,但我们还是会想不通:“好人为什么会死?为什么阿珍会爱上阿强?”
我们脑海里不断回放电影片段,想去弄清楚原因。而随着时间的推移,我们很难再将这些片段拼凑成一个完整的故事。
如果我们在看完电影后,将主角参与的场景按照故事主线画一张发展趋势图,并将故事角色连成一个人物关系图,通过趋势图,即可找到事件发生的原委。
可见,趋势图可帮助推导出事件发生的前因后果,分析出团队代码质量的整体走向。比如,在某个时间段指标值整体偏低,可能是团队放松了代码质量要求;在某个时间点指标值出现波峰,可能是集中交付导致上线失败;指标曲线一直呈上升趋势,说明团队改进措施产生明显效果。
(1)发布故障系数指标趋势图(支持按照部门、团队、服务、人员维度进行分析)
从图1-9中可以看出,该团队整体的代码质量非常不稳定,时好时坏,说明团队成员的能力参差不齐。这种团队应提升工程实践能力。
图1-9 团队发布故障系数趋势图
(2)代码质量指标趋势图(支持按照部门、团队、产品线、服务维度进行分析)
从图1-10中可以看出,该团队的代码质量在一段时间几乎没有改善,一直比较差,需要重点辅助。
(3)单测指标趋势图(支持按照部门、团队、服务维度进行分析)
从图1-11中可以看出,该服务在某时间节点前全量代码单测覆盖率和新增代码单测覆盖率都有提升,而在该时间节点后,团队单测覆盖率整体没有进展。这种团队需要周期性地鞭策和提醒。
图1-10 团队代码质量趋势图
图1-11 团队单测覆盖率趋势图
所以,趋势图可以帮助管理者多维度分析问题,及时调整改进策略。更重要的是,这些历史数据可以让部门负责人在代码质量复盘会上,大胆地展示团队的努力和战绩。不过,水能载舟亦能覆舟,不是吗?
谨记:度量指标的选取一定要和各层级负责人沟通并达成一致;指标不是一成不变的,它仅代表你在一段时间内为了解决痛点问题而做出的选择;在不同时间段,核心指标的重要性、优先级都要根据势态而调整;一定不要只盯着数据去推动研发人员解决问题,而是要协助他们寻找到解决方法,站在同一条战线。不要让研发人员认为你是一个“监督者”和“指挥者”,更不要让部门负责人认为你是驱赶羊群的“藏獒”,而要致力于成为一头协助“领头羊”找到方向的“牧羊犬”。
本节让各角色认识到:只要团队稍微不努力,指标数据和趋势图就会暴露出团队的问题。而这些数据都是可视化平台自动生成的,客观而真实,和三方团队本身没有直接关系。所以,技术团队各部门只能选择持续提高自身解决问题的能力。
只有度量指标选得合理,才能帮助团队发现并解决问题。一切让数据说话吧!