即使是技术娴熟、积极主动的敏捷团队有时也无法达成他们的软件质量目标。在本文中,我们介绍了一种简单而有效的实践,可用于帮助敏捷团队达成质量目标,并分享了我们的经验。
这种实践是关于持续关注特定的度量指标。这意味着需鼓励人们在定性和定量两方面同时提高自己。我们通过特定的角色、仪式、团队合作和自动化来实现这一点。
环境与背景
我们于 2018 年开始在瑞士邮政(Swiss Post)担任解决方案架构师。我们的任务是创建一个用于包裹分拣的新软件系统。目标是在保证高可靠性和高可用性的情况下,使用持续部署快速发布产品。根据 DevOps 报告和我们的经验,我们知道软件质量至关重要,我们必须从一开始就实现这一质量目标。
我们的团队有一名产品负责人、一名 scrum 大师、三名解决方案架构师、十三名开发人员和三名业务分析师。敏捷团队负责开发和运维十三个包裹分拣应用程序。我们从一个小团队开始,并获准招募新的团队成员。我们从一开始就注重质量,并在招聘时注重质量文化。
负责包裹分拣的软件系统能决定谁将收到包裹,并确保包裹通过包裹分拣中心能按时到达负责递送包裹的递送员手中。瑞士邮政的这一关键系统需要全天候、可用、可靠且功能齐全有效。通过 Amazon Elastic Kubernetes Service(Amazon EKS)运维用 C# 编写并在 Linux 容器中运行的应用程序。我们混合使用 Postgres 和 MongoDB 来实现持久化,并使用 Kafka、MQTT 和 REST 来实现集成。
实现质量目标的难点
作为解决方案架构师,我们负责塑造软件开发过程。我们希望我们的敏捷团队能够达成它的质量目标。我们从过去的软件项目中发现了以下阻碍团队达成目标的因素:
缺乏激励(Missing Incentive):通常,没有足够的内部软件质量激励,却有尽快发布功能的压力。此外,组织通常认为他们可以用质量换取速度。然而,高质量是导致更少缺陷和更快交付新功能的因素。
缺乏定期关注(Missing Regular Attention):通常会有很多小问题,比如缺少测试、日志错误或告警。它们并不是什么的重大缺陷,但随着它们的慢慢累积,也可能会造成危害。很快,噪音就太多了,就没有人再关注细节了。有必要定期关注并解决这些问题。这意味着需要查看度量指标并采取行动。例如,查看日志的告警数量,并对每个告警进行排查,修复其根本原因。或者,如果它是不值得修复的,那就用一个有意义的解释来驳回它。
破窗效应(Broken Window Effect):另一个问题是破窗效应,它表明恶化会引发更严重的恶化。这意味着当存在问题时,问题的数量会增长得更快。当注意力和行动的频率下降时,问题数量的下降并看到转向积极所需的时间会呈指数级增长。
高努力值(High Effort):开发人员更倾向于避免重复性的工作任务。通过一堆工具检查度量指标是很麻烦的。开发人员最终会跳过需要更多努力的工具,从而忽略了基本的度量指标。从查看度量指标和趋势(定量分析)跳到推理它们为什么显示出特定的值(定性分析),这必须得是一项低努力值的任务。
解决难点
在开发第一个新应用程序之前,我们希望通过轻量级的实践来解决这些困难。我们提出了以下的想法。首先,我们的目标是解决一个简单的问题:高努力值。我们没有使用多种工具来检查特定的度量指标,而是创建了一个 wiki 页面模板,将所有度量指标收集在一个地方。在这个阶段,我们没有投资于工具。然而,我们通过将手动的收集和模板的输入委托给一个人,从而减少了开发人员的工作量。我们在页面中添加了超链接,将用户重定向到度量指标的来源处,以便对详细信息进行推断。
第二个要解决的问题是需要持续关注。我们决定每周开一次例会。在会上,开发人员需要查看 wiki 页面上的度量指标,并解释任何超出阈值或出现负面趋势的度量指标。例如,我们不接受生产中存在日志错误。因此,我们需要检查过去七天日志中的每个错误并定义修复动作。
我们很高兴能为一半的问题找到了这种简单的解决方案。但是,如何激励个人参加例会并严格执行任务呢?每天的忙碌也会影响这个团队,会导致他们推迟非紧急活动。
激励理论认为,对奖励的渴望可以激励人们。虽然许多人认为金钱可能是一种很好的激励或奖励,但研究表明并非如此。一种更有效且可持续的方法是提供意义,并让受人尊敬的人看到他们的工作。在查看 Scrum 时,团队会处理从产品负责人那里得到的任务,并在 Sprint 评审中展示他们的结果。我们希望有一个类似的角色,但重点是关注产品的质量。我们要求产品负责人声明产品质量是一个至关重要的目标,并将实现这一目标的责任委托给“质量守卫者”(Quality Champion)。(我们将在后面描述该角色和相关细节。)
最后,为了解决破窗效应,在问题出现时就采取行动是至关重要的,这样就不会有人习惯了问题的存在。这种快速响应能给人一种紧迫感,并表明特定的问题是不可接受的。与产品负责人达成协议的一部分是使质量守卫者合法化,能立即以最高优先级为每个人分配一天的技术任务。另一个重点方向是,要设定一个非常低、甚至具有挑战性的门槛,不能接受草率的借口来跨过它。当症状经常出现时,这一点尤其正确,报告者会不进行调查,而直接给出特定的结论。
引入质量报告实践
我们为实践定义了构件、角色和仪式。相应的构件被简单地称为“质量报告”(Quality Report)——一个生成的 Confluence wiki 页面,其中包含一组度量指标。某个工具每周通过不同的来源收集一次度量指标,并创建该页面。我们确保产品负责人和所有开发人员都收到了该页面的编辑通知。这意味着他们将看到质量守卫者和产品负责人在报告上的签名,这强调了实践的重要性。
相应的角色即为首席开发人员和质量守卫者。为每个应用程序选择一名负责排查和定义操作的首席开发人员。开发人员可以担任多个应用程序的首席开发人员角色,并且担任该角色的人员也可以更改。
质量守卫者主持每周的例会,询问、质疑推理,并要求改进。除了会议期间的任务外,担任此角色的人还需撰写管理概要,并向产品负责人提出建议。为了保证能达到预期的效果,这一角色必须要由一位技术精湛且受人尊敬的高级开发人员来担任。
仪式是由质量守卫者每周一上午主持的在线会议。会议从准备阶段开始,每位首席开发人员都需要查看度量指标,进行必要的排查,并填写 wiki 页面。然后,首席开发人员在报告阶段向质量守卫者报告他们的排查结果。在这一部分中,质量守卫者对原因和行动提出质疑,特别是当有人做出假设时,例如“存在网络问题”,他需要求其详细说明。主要开发人员之间的讨论发生在会议的最后一部分。
报告详情
报告的“状态”显示在顶部。质量守卫者在撰写完管理概要的自由文本部分并签署报告后,需将状态设置为“批准”(approved)。产品负责人收到通知,至少要阅读概要部分,并签署报告。应用程序概要是自动创建的,报告中总结了每个应用程序的总体状态。
在管理概要部分之后是应用程序部分。每个应用程序都有一个章节;标题包含首席开发人员和指向度量指标来源的深层链接。从不同来源收集的度量指标表格始终显示数字、指标名称、上周的值和当前的值。状态是根据可配置的阈值设置的,并通过不同的颜色来吸引分析报告的人员的注意。
分析报告意味着负责人以“动作”状态查看每个度量指标。要么忽略它(例如,代码覆盖率很低,但已经有一个拉取请求等待合并来解决这个问题了),要么采取必要的行动(例如,我们需要排查并解决生产中无法解释的日志错误)。然后,用表格中的数字作为参考,并在表格下方的章节中作出解释。
不同团队和应用程序在使用和解释度量指标和阈值方面可能会有所不同。我们的团队对每个应用程序使用如下的一组指标。
静态代码分析(Static code analysis):我们使用 Sonar 和所有来自.NET SDK(又名 Roslyn 分析器)的代码分析规则。团队决定不接受任何的问题;因此,阈值设为零。
自动化测试(Automated testing):我们实施持续部署,并自动化所有测试。我们质量保证的基础是我们的组件测试,包括单元测试和组件集成测试。由于没有其他质量门,也无法区分重要和不重要的测试,因此所需的覆盖率为 100%,我们不接受任何被忽略的测试。
代码重复(Code Duplication):代码重复没有设置门槛,是否允许取决于报告者的判断。在这里,密切关注趋势至关重要。这些代码行很有趣,因为它们与构建时间相关,并给出了关于应用程序大小的提示。
日志记录(Logging):我们的目标是在接到客户电话之前即可发现生产中的每一个问题。因此,我们制定了一个严格的异常处理和日志策略。启发式的方法是:每个日志错误都需要立即关注,每个警告都需要在一天内进行排查。我们不接受生产中的任何告警和错误,我们必须排查这类错误的每一条日志信息。
缺陷(Bugs):跟踪未解决的缺陷至关重要。它们始终处于高优先级。团队应该修复它们,或者接受并关闭它们。该指标统计了应用程序从类型“缺陷”开始的 Jira 问题,以防止缺陷遗漏。
拉取请求(Pull requests):我们采用基于主干特性分支的开发。使用该策略,快速合并拉取请求是至关重要的。当 CI 构建为绿色时,工具会自动更新依赖项并合并拉取请求。几天未解决的拉取请求则表示需要采取行动了。
经验教训
在使用质量报告四年之后,团队规模和生产应用程序增加了两倍,代码库也显著扩大了。在一项内部调查中,开发人员一致认为,质量报告实践是一种有价值的维护和运维质量的工具。持续的关注和激励措施促进了对宏伟目标的坚持。所取得的成就包括 100% 的代码覆盖率,没有静态代码分析问题,日志没有噪音,并且生产中没有已知的错误。这种对高质量的承诺支持了任务关键型应用程序的频繁部署。
我们还有一些额外的经验教训和基本要素需要强调。一个值得注意的观察结果是,个体倾向于快速辨别模式并匆忙得出结论。例如,当遇到数据库健康检查错误时,一些人会立即将其归因于正在进行的维护,尽管没有此类活动。它强调了质量守卫者不可或缺的作用,他们的责任在于从技术角度挑战解释。我们还了解到,能即刻解决具体问题的可能性至关重要。它会带来一种紧迫感,这种紧迫感会奖励这些任务的完成,并防止在待办事项中堆积次优先级的问题。
我们看到,初始阶段,即仪式的准备和分析,是至关重要的。我们尝试过使用时间阻断器来进行个人准备,但没有奏效。我们发现,协作和“大声思考”分析在准备阶段或仪式后期具有积极作用。在某些时候,我们试图让非开发人员的角色参与质量报告,但没有成功。尽管他们起初参加了会议,但他们并没有为报告做出任何贡献,过了一段时间后,他们就完全不再参加了。积极参与质量报告需要了解应用程序的内部工作原理并对度量指标有一定的了解。
如何开始
团队必须相信,高质量的标准可以使软件开发的更快、更便宜、更有趣。通常,人们只是口头上说说而已,从而忽略了基本实践。团队需要考虑反映软件开发过程和软件本身质量的度量指标。客观地衡量软件质量具有挑战性,但是当团队为自己定义了度量指标时,这就是不必要的了。
尽管如此,日常业务中的挑战还是会出现。紧急的日常运维会分散注意力,使团队忙个不停,无法专注于关键的长期目标。为了解决这个问题,团队应该使用我们在质量报告中所呈现的内容。考虑和解决上述难点是至关重要的,即激励、定期关注、破窗效应和高努力值。部署你选择的实践来解决这些难点,从小处着手,先不要关注于工具,而是反复迭代地检查和调整它。
除了解决问题之外,团队还必须深入了解技术和软件实践。这一点很重要,因为开发过程决定了质量,团队需要能够根据度量指标来采取行动。在团队中建立资历和教育水平的健康平衡。
最后,确保团队负责软件的开发过程。这意味着团队的责任是决定使用哪些实践以及如何工作。敏捷已经很普遍了。然而,我们仍然注意到,利益相关者决定了软件过程的某些部分。他们缺乏了解哪些实践能奏效的知识。
作者介绍
Marc Sallin 从事软件开发工作已有十多年了。他坚信高速、高质量和低成本不是竞争关系,而是共生关系。为了应对这些挑战,他将软件工程的定义牢记在心,并将系统、规范和可量化的方法应用于软件开发、运营和维护过程中。在对计算机科学和技术着迷的同时,他了解到团队动态和心理学在软件开发中的重要性。凭借自己的想法,五年来,他一直在瑞士邮政担任解决方案架构师,塑造包裹分拣的未来。
Meinrad Jean Richard 在瑞士邮政担任解决方案架构师,他的主要工作是展望未来十五年包裹和邮件分拣的软件生态系统。作为一名开发人员和软件架构师,他拥有二十年的经验,擅长为复杂的挑战制定技术上稳健且具有前瞻性的解决方案。他的方法以质量保证和实用主义之间的平衡为特征,对软件工程原理和开发过程中人类动态的复杂性有着深刻的理解。
评论