我们是如何在研发过程中控制质量的?产品质量正变得越来越重要
![我们是如何在研发过程中控制质量的?产品质量正变得越来越重要](https://static001.geekbang.org/infoq/31/314ef037468135de9498991f1ebf1f29.png)
作者 | Eason 爱数 AnyBackup 平台保护研发部-研发总监
一直以来,灾备都是爱数的核心业务, AnyBackup 正是支撑起此核心业务的关键产品。近些年来,AnyBackup 在政府、金融、运营商等行业项目中遍地开花,运行良好,提供了高效稳定的灾备能力,也受到越来越多客户的认可。
当然,在产品走向高端客户的过程中,我们也经历了产品质量带来的阵痛。我们曾经听到过来自客户的声音:
你们的产品常常报错,但是不能给我明确的错误信息和处理意见。
你们的备份任务并发量很大的时候,会出现卡顿的现象。
产品容错性不强,不能适应我们各种各样的硬件环境和配置场景。
... ....
除了客户的声音外,在我们研发的过程中,也经常听到这样的声音:
需求时间太赶了,这种场景我们以后再考虑。
客户只是说要备份,我们不用考虑那么复杂。
这个问题不能算 bug,需求本来就没说要支持这个场景。
... ...
当研发内部这样的声音越多,客户不良的反馈就越多,提出的改进建议也就越多。特别是近两年,我们客户的数据量规模越来越大,场景越来越复杂,对于我们产品的稳定性、容错性、可靠性等有越来越高的要求。那么我们如何控制好我们的质量呢?是依靠测试人员的测试能力,还是依靠开发人员的开发能力,或者是依靠产品经理能够收集足够多的用户场景?我们给出的解决方案是依靠工程能力。从两年前开始,爱数就在研发内部落地软件工程规范,在各个研发活动环节提供指导手册和要求规范,明确交付物,以此提升每一环节的质量,最终得以交付出高质量的产品。
下文我们将针对软件工程规范在 AnyBackup 中的落地实践展开详述。
AnyBackup 的研发活动和流程
首先,我们来看看爱数在交付需求时涉及哪些研发活动。
![](https://static001.geekbang.org/infoq/a3/a3a40d0b0d5fa8812de8379741231ef3.png)
在需求分析环节,我们需要充分思考需求给客户带来的价值是什么?是否具备通用性?对已有产品的影响是什么?是不是有更好的解决方案?在经过一系列的思考后,我们往往能够给客户提供更合理的解决方案。当然,很大一部分情况下我们会采纳需求,而当我们最终采纳此需求后,会进一步的对需求进行采集和分析,明确客户的目的与期望。这样一来,我们的需求就从“我们要支持 xxx 虚拟化平台的备份” 得到一个更完整的需求:
我们需要支持 xxx 虚拟化平台虚拟机的备份,需要支持完全备份和增量备份。
我们的平台版本是 xxx。
我们的平台是按 xxx 组网与部署的。
我们的平台后台使用的 xxx 存储,存储用的 xxx 产品 xxx 版本。
我们的虚拟机主要是采用 xxx 方式部署的,主要是 xxx 规格,运行 xxx 业务系统。
......
在逻辑设计环节,我们需要结合当前产品和用户需求充分思考用户在使用过程中可能遇到的各种场景,并针对各种场景给出一个合理的预期,最终形成完整的场景用例。
在交互设计环节,我们结合产品自身的设计风格和设计规范,设计出体验良好的功能模块。在这个环节,客户的建议和反馈常常会给我们非常棒的指导。比如在之前的产品中,我们习惯以“备份任务”的概念来进行管理,用户要备份某个对象,需要配置一系列的参数来创建一个任务。而随着用户数据量和业务复杂度的增长,备份任务越来越多,备份变得越来越难以管理。当我们得到这样的反馈信息,我们立刻意识到用户越来越需要针对“备份对象”来进行管理,这样就方便用户快速了解一个“备份对象”的受保护情况。因此,我们的 AnyBackup 产品在体验上迅速做了改进计划:允许用户同时基于“备份对象”和“备份任务”来管理其数据的备份和恢复。在当前产品中,用户可以直观的看到整个虚拟化平台的受保护情况,并且用户可以直接选择一个想恢复的虚拟机进行恢复,而不用苦恼地从数以百计的任务中去找那个要恢复的虚拟机。
实现方案设计和测试方案设计环节,我们提供了各种规范和指南来帮助研发人员开展工作,并确定整个需求的质量目标。
在任务分解与执行环节,我们充分利用看板和站会,每日跟进各项工作进展,确保各项工作稳步推进。
当完成所有任务后,进入需求交付环节,由需求负责人最终验收需求,检查各交付文档,最终对外发布。
在需求交付后,我们会继续收集用户反馈,并部署上线环境用于公司内部业务系统的灾备。通过持续的收集需求、优化产品,AnyBackup 产品进入良性循环,质量不断得以提升。
逻辑设计:更好的澄清需求
什么是逻辑设计?逻辑设计是指在需求的基础上,从业务逻辑和用户应用环境中抽象出领域对象的组成结构、流程和各个部分的相互关系。通过逻辑设计,我们可以得到统一的项目语言和完整的场景用例。
逻辑设计的过程也就是“实例化需求”的前半部分过程,在《实例化需求》一书中给出了推荐的“实例化需求”的实践方式:
![](https://static001.geekbang.org/infoq/9e/9e0288ec544456d254b8d3c8df8439aa.png)
在此实践方式中,强调需求相关的所有人员(客户、产品、开发、测试)一起协作和讨论,并最终输出需求“实例”。鉴于 AnyBackup 产品的特殊性,我们无法与客户进行非常细节问题的沟通,但在“需求分析”阶段,我们还是可以从客户处获取到足够的信息,基本可以完成"范围"的收集。而更细节的逻辑设计就由我们的产品人员、开发人员、测试人员一起参与。因此,我们内部的逻辑设计环节主要涵盖如下 2 个主要流程:
(产品人员、开发人员、测试人员)协作制定需求说明,输出领域模型、业务流程和业务规则。
根据步骤 1 中的内容梳理实例化的场景用例,并找出其中的关键用例,输出成文档。
梳理领域模型
为了讨论的聚焦,我们在协作过程中把讨论限定在可以与用户交互的领域对象上(即用户可以直观看到的“黑盒”),并不涉及产品内部的服务、模块、类或者是具体的 UI 界面。我们假设用户希望 AnyBackup 可以支持 VMware 平台上虚拟机的备份,那么初始情况下我们可以想到的相关的领域对象包括:用户、VMware 平台、虚拟机、AnyBackup 备份代理、AnyBackup 控制台、AnyBackup 备份介质。在我们的讨论过程中,如果涉及到新的领域对象,且必须依赖它才能完成整个业务流程的梳理,那么我们就需要将纳入讨论范围。如下图所示,我们补充了备份任务和恢复任务对象。
![](https://static001.geekbang.org/infoq/e2/e23276463e2fdb11a88a8826bbb03930.png)
当领域对象,及其之前的关系梳理完毕,我们就得到了这个需求所涉及的“领域模型”。
![](https://static001.geekbang.org/infoq/d2/d2a629489433d56220dd510e8934265e.png)
设计业务流程
有了领域模型还不够,我们需要进一步的梳理业务流程。业务流程用于表达各领域对象之间的交互方式和顺序。基于 AnyBackup 的已有流程,我们可以梳理出以下业务流程:
用户创建备份任务流程
用户启动备份任务流程
用户删除备份任务流程
备份任务执行流程
用户创建恢复任务流程
用户启动恢复任务流程
用户删除恢复任务流程
恢复任务执行流程
以备份任务执行流程为例,我们可以梳理出如下的主要业务流程:
![](https://static001.geekbang.org/infoq/44/44b1b109a70e60933c1e9637fbdb329a.png)
实际的业务流程要复杂的多,涉及环境的检查、兼容性的判断、虚拟机快照的管理等,此处不做展开描述。如果是完成一项全新的业务,那么就需要和客户做更多的讨论,进行更多的竞品分析,不断设计和优化我们的业务流程。设计重于编码,领域对象和业务流程的定义对具体的实现方案有非常大的影响,这一阶段需要进行充分的思考和设计,避免后面研发过程的返工。
定义业务规则
业务规则的定义也是逻辑设计中的一项重要工作。我们无法支持所有的场景,因此我们需要规则来限定范围;我们不清晰不同状态下做什么样的处理,因此我们需要规则来限定行为。 规则的来源会比较多,我们主要会结合各领域对象的状态属性、技术可行性的限制、已有的业务规范、业务流程等来进行讨论和设计。我们仍然以 VMware 虚拟机备份为例,涉及的业务规则包括:
虚拟机是否支持备份的规则说明:
处于异常状态的虚拟机不支持备份。
无法执行快照的虚拟机不支持备份。
其余虚拟机均需支持备份。
备份任务状态的变化规则:
创建完成后处于“未启动”状态。
任务启动后,由“未启动”状态切换至“运行中”状态。
“运行中”的任务被停止后,状态切换成“停止中”。
任务执行完成,或停止完成后,状态切换成“未启动”。
.....
输出“实例化”的需求文档
结合领域模型、业务流程和工作流我们就可以开始梳理需求“实例”了。那么什么是“实例”呢?相比一般的规格说明,实例更加场景化,能够激发相关人员的参与和深度讨论;同时,实例是具体的,它的典型形式是:在什么情况下,做什么操作,会得到什么结果?也就是我们常听到的 GWT。
![](https://static001.geekbang.org/infoq/15/158c43cea9084d5a7a791f46ff3bebaf.png)
实例是澄清需求的最好说明,因此我们在逻辑设计环节中会进行充分的讨论。在讨论过程中,我们往往会考虑到足够丰富的场景,这对我们后续方案的完整性有很大的帮助。
在讨论的过程中,我们常常会梳理出各种不必要的细节。此时,为了避免业务的扩散,我们会对内容进行收敛和提炼。基本上会以如下的建议为指导:
关注边界条件的实例,在边界范围内的实例避免穷举。如上表中的 1~3 实例,我们仅需关注 1 个系统盘场景和虚拟机支持的磁盘最大上限个数场景即可,实例就可以得以精简。
摒弃对业务走向没有影响的实例,如:虚拟机的名称属性。整个业务流程不涉及对虚拟机名称做特殊处理,故其不会对业务走向带来影响,可以不用纳入实例范围。
精简对业务走向相同的实例,如上表中的 4 和 5,其最终的结果都是虚拟机执行快照失败,两种场景下的业务效果和流程完全一致,可以提炼成一个实例。
通过梳理,我们就得到了最终的实例化后的需求,这些实例也进一步成为了开发自测和自动化测试依据。
实现方案设计:提供规范和指导
实现方案包含整体架构、内部服务和组件的设计、各服务或组件之间的交互逻辑、数据流图、接口设计、数据库设计等。
实现方案的设计非常考验开发人员的能力,经验不足的开发人员在做方案设计的时候往往考虑不充分,设计随意。为此,我们在研发内部提供了丰富的指导文档和规范文档。包括:
《微服务容器化开发指南》
《微服务开发规范》
《数据库设计规范》
《程序日志规范》
《错误码规范》
《单元测试指南》
《开源软件使用规范》
《安全设计与开发规范》
《接口设计规范》
... ...
开发人员在输出方案文档前,需要自检其方案是否都符合规范。自检通过后,我们会组织团队内部的方案评审,审核过程中,我们除了审查业务功能是否能够实现外,还会重点关注方案可能存在的性能问题;对已有架构的影响;可测试性、可维护性和可扩展性的设计。
测试方案设计:分层测试,关注系统各个层面的质量
测试方案的设计在整个研发过程中也很重要。我们通常采用分层测试的思想,将整个技术方案的层级结构和模块划分清晰,找出各层级中的重点测试部分,进行测试方法的研究和测试用例的设计。测试方案用于指导我们如何正确的开展测试工作,同时帮助我们了解整个产品在各个层面的质量情况。
![](https://static001.geekbang.org/infoq/e0/e05dad1c34d799414bc939e5a71335f0.png)
测试方案的关注对象主要集中在组件层、后端服务层和管理服务层。测试方案确定后,我们会针对特定服务或组件开发测试工具,以确保测试工作的顺利开展。在整个测试方案的推进过程中,我们常常能够发现技术方案中存在的耦合重、边界和职责定义不清晰等问题,以此反向推动我们实现方案的优化。
在测试方案的设计过程中,我们也会确定需求的“质量目标”:从需求的体验、性能、安全性、稳定性等方面评估出合理的指标。在后续测试过程中,我们就可以进行明确的边界测试。
当然,除了分层测试和边界测试外,我们还需要从整体来考虑我们的需求是否已经达成了业务目标。这就利用到了我们在逻辑设计环节输出的场景实例,我们针对所有场景实例进行完整测试,并为其编写自动化用例,做到每日执行。这样,我们就不必担心代码修改,或新增需求对已有业务造成影响。
规范的落地与执行
可能大家会有疑问:我们引入了大量的规范和准则,如何确保这些东西真正落地呢?答案就是在过程中建立审查和约束机制:成立代码委员会和质量委员会,对研发过程中各项活动的交付物进行检查,并针对检查结果进行全研发人员播报。好的形成标杆,差的引以为戒。此外,在构建平台上设置 UT 覆盖率 和 AT 覆盖率门槛,当检查到指标不符合要求时,直接构建失败,不允许需求交付。这些措施都有效提升了研发人员对规范的重视。
通过一系列规范的落地和执行,AnyBackup 研发团队的良好习惯逐步养成,AnyBackup 产品的质量也正是在这个过程中的不断得以提升。
版权声明: 本文为 InfoQ 作者【爱数技术范儿】的原创文章。
原文链接:【http://xie.infoq.cn/article/7a70960ac61616ce10ede1f99】。文章转载请联系作者。
评论