2.11 软件质量
概述与动机
软件质量是指一个系统满足既定需求和合理期望的程度。这不仅仅意味着它能否工作:它还意味着这个系统是否可靠、安全、可维护、可用、性能良好,并且能长期适用于其目的。质量比测试更广泛。测试(第 2.4 章)是揭示缺陷的一项活动。而质量是把正确的东西做好、并用证据知道自己确实做到了这一点的整套学科。一个系统可能通过了每一项测试,却仍然是低质量的,如果它不可维护、无法访问,或者不适合用户真正的需求。
在一个大型团队中,质量不能只存在于某一个人的脑子里,或某一个团队的习惯之中。数百名工程师、多个产品和长期存续的系统,需要一个共享的质量定义、明确的保障流程,以及能告诉你情况正在变好还是变坏的度量手段。没有这些,“质量”就会沦为一种模糊的愿望,在与截止日期的每一次较量中都会落败,缺陷不断堆积,直到变更变得缓慢而危险。
在企业和政府场景中,利害关系更高。受监管的、安全攸关的、面向公民的系统,必须证明质量,而不仅仅是宣称质量:有文档记录的流程、可追溯的证据,以及独立验证,往往是强制性要求。质量低劣会带来直接的财务、法律和声誉代价,在某些领域甚至会危及人身安全。一套有意识的质量学科()建立在模型、流程、度量和文化之上()正是把质量从一场意外,变成一个被管理的结果的关键。
关键原则
- 质量等于适用性加上对需求的符合性;要明确地界定这两者。
- 质量是被构建进去的,而不是被测试出来的;验证能发现缺陷,但预防能避免缺陷。
- 要区分质量保证(我们的流程是否健全?)与质量控制(这个产品是否合格?)。
- 验证问的是”我们是否把它做对了?“;确认问的是”我们做的是不是正确的东西?”
- 用一小组有意义的指标来度量质量;把指标当作信号,而不是目标。
- 一个缺陷被发现得越晚,其成本就越高,因此要把质量活动尽量前移。
- 质量是整个组织及其文化的属性,而不是末端的一道关卡。
建议
采用一个共享的质量模型,例如 ISO/IEC 25010
通过采用一个公认的产品质量模型,为你的组织提供一套关于质量的共同词汇。ISO/IEC 25010 定义了包括功能适用性、性能效率、兼容性、可用性、可靠性、安全性、可维护性和可移植性在内的一系列特性。用它来让质量变得具体:对于每一个系统,决定哪些特性最重要,以及每一项特性达到什么程度才算”足够好”。这些产品质量特性,与驱动架构决策(第 3.1 章)的质量属性是同一回事。质量和架构是同一个关切点的两种视角,因此要让它们共享同一份优先级列表,而不是各自维护两份相互竞争的列表。
把质量保证与质量控制区分开来
把质量保证(QA)和质量控制(QC)当作两项各自独立、又相互补充的活动来对待。QA 面向流程,具有预防性:它通过标准、评审、完成的定义和培训,改进工作完成的方式,从而让缺陷从一开始就更不容易出现。QC 面向产品,具有检测性:它通过测试、代码评审和审计,检查实际的工作产出,捕获那些确实混进来的缺陷。一个成熟的组织会同时投资于这两者,但会更偏向 QA,因为预防缺陷比发现并修复缺陷更便宜。
运行明确的软件质量管理流程
要把质量变成一个被管理的流程,而不是一种默默的期望。对于重要的工作,要撰写一份质量计划,说明目标质量特性、保证和控制活动、验收标准,以及谁对此负责。要把它编织进你已经拥有的实践之中:代码评审(第 2.5 章)既是一种控制手段,也是一种共享知识的方式;测试策略(第 2.4 章)作为自动化的安全网;静态分析作为持续的检查。要定期审查质量数据并针对趋势采取行动,而不是只在事件发生后才做出反应。
把验证和确认当作两门独立的学科来实践
验证确认工作产出是否符合其规范,从而让每个阶段正确的输入产生正确的输出,方式包括评审、静态分析,以及针对需求的测试。确认则通过用户测试、验收测试、试点和现场反馈,确认已完成的系统是否真正满足了用户需求及其预期用途。这两者你都需要。一个系统可能相对于一份有缺陷的规范是正确的(经过验证但未经确认),也可能满足了一个真实的需求、却仍然包含缺陷(经过确认但未经验证)。在受监管的环境中,可能要求由独立于开发方的一方来进行独立的验证和确认(IV&V)。
用有意义的指标度量质量
选择一小组能反映质量结果及其驱动因素的指标,并随时间持续观察它们。有用的度量包括缺陷密度、缺陷逃逸率(生产环境中发现的缺陷与发布前发现的缺陷之比)、平均检测时间和修复时间、变更失败率、诸如复杂度和重复度这样的代码健康信号,以及诸如用户报告的问题和无障碍符合性这样的确认信号。要避开虚荣性的、容易被操纵的指标:一旦一个指标变成了目标,它就不再反映真实情况。要把这些数字与来自评审和用户反馈的定性信号结合起来看。
系统化地刻画和管理缺陷
把缺陷当作数据,而不仅仅是需要扑灭的火。按严重程度、类型和根本原因对缺陷进行分类。跟踪它们从被发现到被解决的整个过程。寻找规律,以便预防复发。使用诸如根本原因分析和缺陷分类之类的技术,把一次性的失误与系统性的弱点区分开来。要把你学到的东西反馈回 QA 之中,通过更新标准、增加测试和改进评审,让同一类缺陷不再重现。一个未经理解其原因就被修复的缺陷,是一个你已经邀请它回来的缺陷。
有意识地管理质量成本
通过经典的分类来理解质量经济学:预防成本(培训、标准、良好的设计、工具)、评估成本(评审、测试、审计),以及失败成本(发布前的内部返工,加上被用户发现的外部失败,后者的成本要高得多)。要把你的投入向预防和早期评估倾斜,因为在那里花的每一块钱,都能避免日后失败成本中的许多块钱。要让这些成本变得可见,这样”我们没时间搞质量”这句话,就会被看穿其本质:那其实是选择在失败上花更多的钱。
建设质量文化
要让质量成为每个人的责任,由构建软件的团队自己拥有,而不是交给一个在末端进行检查的下游 QA 部门。领导者应当奖励质量方面的成果,让报告缺陷和未遂事件变得安全,并把质量数据当作学习工具,而不是惩戒的大棒。一种无责的方式能让问题及早被公开;而一种归咎他人的方式,则会让问题一直被隐藏,直到代价变得高昂。
权衡:优点与缺点
| 实践 / 选择 | 优点 | 缺点 |
|---|---|---|
| 正式的质量模型(ISO 25010) | 共享的词汇;明确的优先级 | 如果教条式地套用会带来开销 |
| 高强度的质量保证(预防) | 缺陷更少;总成本更低 | 需要前期投入;回报显现较慢 |
| 高强度的质量控制(检查) | 能捕获漏网的缺陷 | 成本高;发现缺陷的时间较晚 |
| 独立的验证与确认 | 高保证水平;客观 | 成本高;速度慢;可能显得对立 |
| 丰富的质量指标 | 可见性;早期预警 | 存在被操纵的风险;度量本身有开销 |
| 专职的 QA 团队 | 专注且具备专业能力 | 可能让开发人员卸下自己的责任 |
| 由团队自己拥有质量 | 主人翁意识;反馈快 | 要求各处都具备纪律和技能 |
核心的权衡在于投入与保证之间,而时机塑造着这一权衡。预防要求现在花钱,以避免日后更大的失败成本。因此,经济上正确的质量水平并不是最大化,而是”额外保证的边际成本”恰好等于”它所避免的失败成本”的那个点。对安全攸关的系统而言,这个点位置较高;对低风险的内部工具而言,这个点则较低。另一个反复出现的张力在于归属权。中心化的 QA 团队能积累专业能力,但可能让开发人员卸下责任。由团队自己拥有的质量能建立主人翁意识,但要求各处都具备技能和纪律。
与团队讨论的问题
当同一类缺陷第二次出现时,我们是进行根本原因分析,还是只是再修一次? 一个未经理解其原因就被修复的缺陷,是一个你已经邀请它回来的缺陷,而在一个大型团队里,同一个根本原因可能在许多服务中先后浮现,才有人把这些点连起来。把缺陷当作数据(按严重程度、类型和原因分类,然后挖掘规律),正是把一个可靠性稳步提升的团队,与一个始终忙于重复修复同一个错误的团队区分开来的关键。请把你的缺陷跟踪系统带到会议中来,寻找重复出现的特征:近期有多少事件共享一个你从未系统性解决过的原因?答案应当反哺预防工作,因此一个反复出现的原因应当推动一项标准的更新、一个新的共享辅助函数、一项新增的测试,或一份更好的评审检查清单,因为在一处的修复,正是让整整一类问题不再重现的方式。
在我们团队里,报告一个缺陷或一次未遂事件是安全的吗,提出问题的人会遭遇什么? 质量是文化的一种属性,一种无责的方式能让问题及早被公开,而一种归咎他人的方式,则会让问题一直被隐藏,直到代价变得高昂()在一个受监管或面向公民的系统中,这可能意味着一次公开的失败或一项处罚。这一点在规模化的场景下尤为重要,因为最贴近风险的工程师往往是资历最浅的人,保持沉默的动机也最强烈。请带上诚实的信号:未遂事件是被记录和讨论,还是就此消失?事后总结点名的是原因,还是点名某个人?应采取的行动,是把质量数据变成学习工具,而不是惩戒的大棒;要奖励那些揭示问题的人;并进行无责的事后总结,因为你无法预防你的团队不敢报告的事情。
确认真的能够阻止一次发布吗,当截止日期临近时,这个权力握在谁手中? 验证(我们是否把它做对了?)和确认(我们做的是不是正确的东西?)是两门不同的学科,而确认只有在一次未通过的无障碍检查、一次未通过的验收测试,或一份令人无法忽视的用户研究结果,真正能够阻止发布时,才有实际的约束力。在企业和政府场景中,这往往是强制性的,有时还要求由独立于开发方的一方进行独立的验证和确认,而”我们还是把它发布了”并不是监督机构能接受的答案。请带上你最近的几次发布:是否曾有任何质量信号真正阻止过一次发布,还是这道关卡总是向日期让步?如果确认从未阻止过一次发布,那它就只是装饰品,解决办法是提前在质量计划中写明验收标准,指明谁对”放行还是不放行”的决策负责,并让这个决策拥有独立于交付压力的真正权威。
我们是否真正了解我们的低劣质量成本,我们是否正在有意识地把开支从失败向预防转移? 低劣质量成本(COPQ)是因内部返工、生产事件、紧急修复、支持负荷、用户流失和处罚而损失的金钱,它几乎总是远大于评审和测试上那些看得见的开支。在一个大型团队中,失败成本分散在事件频道、支持队列,以及没有人把它记录为返工的返工工作之中,因此它一直不可见,直到有人把它们加总起来。这里的张力在于,预防需要在一个预算周期内现在就花钱,以避免日后才会到来、且落在别人预算头上的失败成本,这使得这项权衡很容易被永远推迟。请带上真实的数据:事件数量和成本、返工工时、缺陷逃逸率,以及当前预防、评估和失败三者之间的开支比例,然后决定这个组合是否应当更早介入。对于生命周期成本大多产生在首次发布之后的企业和政府系统而言,要把 COPQ 呈现在掌握预算的人面前,因为一个监督机构能看到的数字,比一句关于”质量”的模糊呼吁,要难以被搪塞得多。
我们的哪些质量指标已经悄悄变成了目标,它们现在正在驱动什么样的行为? 一个变成目标的指标就不再反映真实情况:追逐一个覆盖率百分比,你得到的是为了推动数字而写的测试,而不是能捕获缺陷的测试。在规模化的场景下这一点尤其危险,因为一个跨数十个团队共享的头条仪表盘,会为所有这些团队设定激励机制,一个容易被操纵的指标,会让操纵行为在各处同时蔓延开来。与之相抗衡的考量是,你仍然需要度量,因此答案很少是”放弃这个指标”,而是”给它配一个反向信号,并结合来自评审和用户的定性证据一起解读”。请带上你当前的指标集合,对每一个指标都问一问:一个承受压力的人可以做什么,在不改善质量的情况下推动这个数字,你是否见过这种情况发生。在受监管和面向公民的场景中,要格外警惕那些看起来是绿色的符合性指标,而其背后的确认(无障碍性、真实用户结果)从未被真正执行过,因为审计人员迟早会测试数字背后的真实情况。
这里的质量由谁拥有:是写代码的团队,还是末端的一个独立部门,我们实际在为哪一方配置资源? 归属权塑造了下游的一切,因为一个下游的 QA 孤岛会让开发人员卸下他们所写代码的责任,而由团队拥有的质量则能建立主人翁意识,代价是要求每个团队都具备技能和纪律。在一个大型团队中,这不是非此即彼的选择:可持续的模式通常是团队通过代码评审和自动化测试来拥有质量,同时由一个维护标准、把质量保证作为流程改进来运行、并提供辅导的小型中心团队提供支持,而不是在末端检查出质量。请带上一份诚实的地图,标明质量工作目前实际发生在哪里、缺陷逃逸时谁对此负责,以及预算和人头实际落在哪里,与说辞中质量”应该”存在的地方相比又如何。对企业和政府组织而言,还要加上独立验证和确认的要求:一些保证制度要求由独立的一方进行,因此要有意识地决定哪些控制属于交付团队,哪些必须保持独立以满足审计要求。
行业视角
初创企业。 速度比仪式更重要,因此要指明真正保护你产品的两三个质量特性()通常是可靠性和可维护性,其他的打磨可以先放一放。让整个团队通过代码评审和一套规模适中的自动化测试套件来共同拥有质量,而不是搭建一个你根本配不齐人手的独立 QA 团队。当同一类 bug 第二次出现时,花二十分钟排查根本原因,再加一个共享的辅助函数和一项测试,这样预防的成本就能保持低廉,你的变更失败率也能在快速前进的同时保持较低水平。
小型企业。 没有专职的质量专家,预算也紧张,应当依靠内建在你所购买的工具和平台中的质量,而不是自己去运行一套流程。在选择软件时,要把供应商的质量证据当作采购决策的一部分:安全态势、无障碍性、支持响应速度,以及他们的发布多久出一次问题。要跟踪少数几个成本低廉、诚实可靠的信号(生产事件、客户报告的问题、修复时间),而不是搞一套你根本没人维护的复杂指标体系。
企业。 这里的工作重点是跨众多团队的一致性:采用一个共享的质量模型,例如 ISO/IEC 25010,把质量保证(流程)与质量控制(产品)区分开来,并开展质量成本审查,把开支向预防倾斜。要让质量始终由交付团队拥有,并由一个维护标准、维护缺陷逃逸率、变更失败率和代码健康趋势仪表盘的小型中心团队提供支持。要把词汇和关卡标准化,让各团队不再各自重新发明质量实践,同时给团队留出空间,让他们以自己的方式达到这些标准。
政府。 采购、透明度和公共问责制设定了整体框架,因此要把质量要求写入合同,要求有文档记录、可追溯的质量证据,而不仅仅是口头断言。要预期由独立于开发方的一方进行独立的验证和确认、强制性的无障碍符合性要求,以及作为审计记录一部分保存的、带有严重程度和根本原因的缺陷记录。要向监督机构报告低劣质量成本数字(返工、申诉、服务失败),并赋予确认环节真正的权威,使其能够阻止一次将让依赖它的公民遭受损失的发布。
示例
初创企业。 一家五人规模的初创企业决定,对其早期产品而言,可靠性和可维护性才是真正重要的质量特性,至于像素级的完美打磨可以先放一放。质量由整个团队共同拥有:代码评审和一套规模适中的自动化测试套件是控制手段,没有一个独立的 QA 团队来接手缺陷。当同一类 bug 第二次出现时,他们花二十分钟快速排查根本原因,再加一个共享的辅助函数和一项测试,这样它就不再复发,而不是每次都靠手工重新修复。这种小小的预防习惯,让他们在快速前进的同时,依然保持较低的变更失败率。
企业。 一家大型金融服务公司采用 ISO/IEC 25010 作为其质量词汇,并为每个产品记录可靠性、安全性和可维护性的目标水平。团队拥有质量:代码评审和自动化测试是流水线中的控制手段,同时一个小型的中心团队通过维护标准和提供辅导来运行 QA。一份质量仪表盘跟踪缺陷逃逸率、变更失败率和代码健康趋势。缺陷被分类并追溯其根本原因,反复出现的原因会推动共享库和检查清单的更新。领导层每季度审查质量成本数据,并已把开支向预防倾斜,同时降低了生产事件的数量和修复它们的成本。
政府。 一家交付面向公民的福利平台的国家机构,在一套要求有文档记录的质量证据的保证制度下运作。它为每次发布运行一套正式的质量管理流程,配有质量计划,外加由独立于开发方的团队进行的独立验证和确认。验证对照可追溯至政策的需求,检查每一项工作产出。确认包括无障碍符合性测试,以及与真实公民进行的用户研究,两者中的任何一个都可以阻止一次发布。缺陷连同严重程度和根本原因一起被跟踪,作为审计记录的一部分,低劣质量成本数字(返工、申诉和服务失败)会被提交给监督机构,以证明持续投资于预防是合理的。
商业论证:动机、投资回报率与总拥有成本
质量带来的回报,是更低的总拥有成本和稳定的交付速度。质量成本有两面。良性的支出()预防和评估()是可见的、可控的:设计、标准、评审、测试和工具。低劣质量成本(COPQ)则更大,却常常是隐藏的:内部返工、生产事件、紧急修复、客户支持、用户流失、监管处罚和声誉损害。从克劳士比(Crosby)的《质量免费》(Quality Is Free)一书开始的一系列研究一致发现,低劣质量的总成本远远超过预防它的成本,而且缺陷发现得越晚,其成本就增长得越快:在设计阶段发现的一个问题,其成本只是同一个问题在生产环境中被发现时的一小部分。
对领导层而言,这个论证不是”在质量上多花钱”,而是”更早花钱,总体花得更少”。要从你自己的数据(事件数量和成本、返工工时、缺陷逃逸率)中量化 COPQ,并展示预防和早期评估如何把它降下来。要把质量与业务结果联系起来:可靠性留住客户,可维护性让未来的变更保持低成本,安全性和无障碍性则让你远离法律麻烦。在寿命长久的企业和政府系统中,由于大部分成本产生在首次发布之后,质量的可维护性和可靠性维度主导着生命周期成本。这使得早期的质量投资,成为你能做出的杠杆效应最高的决策之一。
反模式与陷阱
- 把质量当作最后一道关卡: 在末端才检查出质量,而不是把它构建进去,导致缺陷在成本最高的时候才被发现。
- 把测试与质量混为一谈: 认为通过测试就等于高质量,忽视可维护性、可用性和适用性。
- 让 QA 成为一个独立的孤岛: 由一个下游团队”拥有质量”,让开发人员卸下自己所写代码的责任。
- 指标表演: 把覆盖率百分比或缺陷数量当作目标去追逐,这会招致操纵,并掩盖真实的质量状况。
- 只有验证,没有确认: 正确地构建了规范,却从未检查过这份规范是否满足真实的需求。
- 没有根本原因分析: 逐个修复缺陷,却不解决系统性的原因,导致同一类问题反复出现。
- 忽视低劣质量成本: 因为失败成本是隐藏的、未被度量的,就把质量当作纯粹的开支来对待。
成熟度模型
第 1 级(启动)。 质量没有明确定义,是临时的。它依靠个人的自觉,主要在末端通过手工测试来检查,缺陷被动地在出现时才处理。没有共享的模型,没有指标,也没有保证与控制之间的界限。
第 2 级(发展)。 基本实践开始出现:代码评审、自动化测试和一个缺陷跟踪系统。一些质量数据被收集起来,但并不均匀,每个团队都按自己的方式来做。质量仍然主要被视为测试,预防工作很少,验证已经在进行,而确认则是非正式的。
第 3 级(标准化)。 组织采用了一个共享的质量模型(例如 ISO/IEC 25010),把 QA 与 QC 区分开来,并运行带有质量计划和验收标准的质量管理流程,这些流程被记录下来,并在各团队之间一致地应用。验证和确认是两门独立、且被有意识执行的学科,缺陷按照一套商定的方案被分类并追溯根本原因。
第 4 级(管理)。 质量被对照基线进行度量和控制。一小组有意义的指标(缺陷密度、缺陷逃逸率、平均检测和修复时间、变更失败率,以及诸如复杂度和重复度这样的代码健康信号)被持续跟踪,质量成本在预防、评估和失败三者之间被量化。验收和质量关卡依据证据而不是主观看法来执行,趋势按固定节奏被审查,确认能够真正阻止一次发布。
第 5 级(协同)。 质量成为一门被持续改进、由文化所拥有、并与业务和风险规划相整合的学科。预防是重点,质量成本数据指引投资方向,根本原因的发现能够系统性地预防复发。团队端到端地拥有质量,指标反哺持续改进,组织也随着产品、风险和监管的变化,不断调整其质量实践。这与第 10.8 章成熟度模型中更高的层级相一致。
讨论思路
- 对你们的系统而言,ISO/IEC 25010 中的哪些质量特性最重要,每一项特性达到什么程度算是”足够好”?
- 你们的组织在预防、评估、失败三者的开支组合中,目前处于什么位置,是否应当调整?
- 你们在实践中是否真正区分验证和确认,还是把两者都笼统地归为”测试”?
- 质量是由构建软件的团队拥有,还是委托给一个独立的部门,如果把它转移过去,会发生什么变化?
- 你们真实的低劣质量成本是多少,你们能否把它度量到足以支撑商业论证的程度?
- 你们的哪些质量指标是真实的信号,哪些已经变成了可被操纵的目标?
关键要点
- 质量比测试更广泛:它是适用性加上符合性,涵盖诸如可靠性、安全性和可维护性这样的特性。
- 使用一个共享的质量模型(ISO/IEC 25010),让质量属性变得明确,并与架构(第 3.1 章)保持一致。
- 把质量保证(预防、流程)与质量控制(检测、产品)区分开来,并向预防倾斜。
- 把验证(把它做对了)和确认(做的是正确的东西)当作两门独立的学科来实践。
- 用少数几个有意义的指标来度量质量,并按严重程度和根本原因刻画缺陷,以预防复发。
- 管理质量成本:预防和早期评估远比失败便宜得多,在长期存续的系统中尤其如此。
- 建设一种无责的质量文化,让团队拥有质量,并由代码评审(第 2.5 章)和测试策略(第 2.4 章)提供支持。
参考文献与延伸阅读
- IEEE Computer Society, SWEBOK Guide (Guide to the Software Engineering Body of Knowledge), Software Quality knowledge area.
- ISO/IEC 25010, Systems and software engineering: Systems and software Quality Requirements and Evaluation (SQuaRE): System and software quality models.
- ISO/IEC 25000 series (SQuaRE), Software product quality requirements and evaluation.
- Philip B. Crosby, Quality Is Free: The Art of Making Quality Certain.
- W. Edwards Deming, Out of the Crisis.
- Capers Jones and Olivier Bonsignour, The Economics of Software Quality.
- Gerald Weinberg, Quality Software Management.
- ISO/IEC/IEEE 12207, Systems and software engineering: Software life cycle processes (quality assurance and V&V process context).