草庐IT

谈谈如何提升应用发布的质量?

轻风博客 2023-03-28 原文

 

一、为什么发布成功率很低?

软件交付的终态是提供一个稳定可预期的系统,可预期的系统要确保环境和软件制品的一致性。而在软件研发协作的过程中,无论是制品、环境,还是发布过程,往往都是定义在代码里的。
软件交付体现为发布,而提升交付能力的目标,是要发的容易,发的频繁,增量要多,每次发的时间要少。

以上图为例,横轴是时间,纵轴是每一次发布的耗时。一个红点表示一次失败的发布,一个绿点表示一次成功的发布。图中该应用的发布成功率只有30%左右。
为什么发布成功率这么低?
在我们的实际工作中,很多问题都会导致发布成功率特别地低。例如:

1.无法按期交付、挤占需求开发时间

约定的时间没有办法交付,可能是因为在软件的沟通和设计阶段耗时过多,导致开发很晚开始,或者软件质量特别差,缺陷特别多,导致花费大量的时间修复问题。随着时间的推移,团队花到维护上的时间越来越多,用于新功能开发的时间越来越少。同时为了发地更快,就会减少原来应该在进入维护阶段前做掉的事情,让维护的工作在后期堆得更多。 

2.问题发现越晚,修复成本越高

随着问题暴露得越来越晚,修复成本呈指数级上升,一旦问题留到客户那里,成本将会非常高的。举个例子,有一年在法国出现过一个电信故障,因为电信软件生产环境和开发环境是隔离的,一旦出现问题,意味着需要派工程师从中国飞过去,在现场进行问题调研,再给出相应的修复补丁。在这个过程中,电信设备是没有办法正常工作的,电信设备的停机将会带来极高的成本。在商业角度,电信运营商会向设备商提出赔偿要求,如果一个小时不能恢复,可能就需要赔付好几百万欧元。
在互联网行业里也是一样,问题发现的越晚修复成本越高。修复成本分为两类:我们把上线之后出现质量问题带来的修复成本,称为交付质量的成本,即交付出去之后由于缺陷导致的成本。另一方面,在交付的过程中产生的成本,也是越到后面越高,因为涉及到的研发团队的角色、职能越多。
所以,要提升发布的成功率,我们需要保证待发布内容的质量。只有发布内容的质量得到保证,才能做到发的容易,发的频繁。
要做到这一点,我们需要全方位的测试质量守护体系。

二、全方位的测试质量守护体系

上图把测试做了简单的分类,下半部分偏技术实现导向,上半部分偏业务结果导向,右侧面向整个产品来看质量,左侧面向团队功能实现层面看质量。
1象限(左下角),包括单元测试和组件测试,都是偏技术实现的,而且是团队内部要搞定的。功能测试、工作流、场景性的测试,包括用户体验测试和结对测试偏业务,但是也属于团队实现过程当中做的测试,所以放到2象限。3象限,在整个产品的角度是偏业务的,包括可用性探索测试、客户结对测试和验收测试。最后的4象限一方面偏技术实现,另一方面又是站在整个产品的完整性角度做的验证,如压测、性能测试、安全测试、数据迁移测试、扩展性测试、负荷测试等,要把这些测试都做好成本是特别高的。
有了完整的测试分类,在整个软件交付的生命周期里如何来合理安排它们呢?需要质量保障体系来定义。

上图是企业里比较常见的质量保障体系。从需求评审和架构设计开始,这时需求的质量和架构的质量是非常重要的,如果这时候质量出现问题,后面所有的工作都需要返工。需求和架构明确下来后就进入编码和开发环节,代码质量和安全质量就会成为核心关注点。接下来是编码的测试验证阶段,包括测试质量,数据质量,稳定性质量等。当功能测试告一段落之后,性能和用户体验成为主要关注对象,直到发布完成。发布之后需要综合线上用户的反馈,产生一个非常全面的质量评估。
往下一层是大量的实践,比如自动化测试的实践,稳定性测试的实践,性能测试的实践,安全测试的实践等。
再往下是整个基础平台和流程支撑,包括测试承载的环境和工具等。
最后一层是度量。
有了完整的质量保障体系,还需要有相应的一些措施。
(1)确保质量是团队所有人的事
整个软件交付周期中上下游所有的角色都应该参与在质量保障的工作中。
质量是团队所有人的事。有的团队说要加强业务需求评审,之所以质量问题多,是前序业务输入的需求问题多;有的团队说要加强开发自测,之所以质量不好是因为开发自测的质量不好,事实上,这些观点都是片面的,这些举措往往不但于事无补,还可能造成团队成员间的对立。解决质量问题的第一步是要有一个明确的标准,比如需求的质量标准,作为代码的质量标准,每个阶段有相应的标准定义。
另外,上游永远要考虑能否很好地帮助下游把工作做好。比如如果在做架构设计的时候没有考虑可测性,等到开始测试的时候就没有办法开展了。上游的人应该承担更重大的角色,上游做好下游可以省很多事情。
第三,要避免测试代码的“公地危机”。做测试自动化会产生相应的测试代码,可能会涉及到通用的一些部分,几个团队大家都要共用,这时如果没有很好的维护,没有一种代码集体所有制,没有建立好标准化,测试代码就会变成“公地危机”。
(2)基于持续测试的质量守护

要做到持续的交付,一定要有持续的测试保证达到待发布状态。要做到这一点,以微服务为例。

上图是比较典型的微服务应用的例子,如果要给它做相应的质量保障,提供相应的测试来守护它,整体的测试策略如下:

红色虚线部分是单元测试的时候就可以保证质量的,将测试限定在被测试系统的某一部分,比如只测Service那一层某一个接口或是函数,或者是domain里面的某一个方法。在这个基础上,更大的范围是组件层面的,组件之间能否交互,接口是否正确等。整个组件里中,排除掉外部的依赖(如数据依赖和网络的消息依赖),更多的是跟网关之间的接口测试。再往外,才是与外部应用的集成测试,应用与应用之间会有协议交互,彼此交付所遵循的协议和规范即为契约。基于契约的测试,我们称之为契约测试。而再往外就是端到端的测试了,关注的就是不是一两个应用,而是多个应用组合在一起的完整的系统。
站在用户的角度来说,交付的系统是否提供了一个正常的业务服务,只要关注端到端测试就可以了。而站在组件的开发的角度来说,我只负责Service layer这一层的修改,我只要把这一层用单元测试保护一下。因此,对于不同的角色,选择的测试是不一样的,而且不同的时间阶段里面选择的测试也会不一样。在这个过程当中,选择什么样的测试主要考虑的是成本和收益的平衡。

三、质量与成本的平衡

如图,纵轴表示成本,横轴表示质量,红色的曲线表示质量成本的曲线,成本的曲线会随着质量的要求做相应的变化。
两条斜的虚线,向上的这条是指不做测试,认为质量投入会有成本,不投入相应的手段保证质量。这条虚线一开始的成本为0,因为完全不做跟质量相关的事情。但是随着后面缺陷越来越多,故障越来越多,管理质量的成本就不会不得不越来越高,呈指数级上升。
另一条往下的虚线,认为质量就应该做到极致,一开始就投入了非常多的成本,到质量变好的时候,投入将会快速地下降。我们给质量的成本做了个简单的分类:

质量的成本分为两类:
一个是低质量的成本,包括内部缺陷成本,研发过程当中由于缺陷带来的种种制约。另外也包括外部缺陷成本,缺陷留给客户,可能会产生的业务上的损失。如果觉得这个缺陷成本很低,在质量这一方面就可以不用投入。
另一个是高质量的成本,包括预防的成本和验证的成本,验证成本也被叫做“评估成本”,用来评估质量好与不好。比如,如何知道这个制品的质量好还是不好?需要定义相应的一些验收的用例验证,然后执行它,看是符合预期还是有一些意想不到的问题。
选择哪些测试在哪个阶段执行,是取决于这些成本考虑的。有些测试,要投入很多测试机器,要雇很多人写测试用例,执行和分析很多测试自动化的脚本,有一些测试成本特别高,买一台测试设备需要几十上百万,如果在产品的早期就进行这样的投入,往往是不经济的。从这个角度来说选择什么样的测试策略,其实是从经济学的角度来考虑,测试是非常高成本的事情。

总结

一方面,要提升应用的发布成功率,需要建立在全面的质量守护体系的基础上。研发团队的上下游各个角色都需要参与到质量守护的活动中,并为之负责,特别是上游要更多地为下游考虑质量问题,共同提升质量水平,避免测试的公地危机。
另一方面,质量是有成本的,守护质量也是有成本的,我们对质量的投入要平衡成本与收益,既要避免质量问题带来的过高管理成本,也要避免对质量保障的过度投入。简而言之,质量问题首先是一个经济问题,然后是管理问题,最后才是工程技术问题。
作者:张裕(子丑)

有关谈谈如何提升应用发布的质量?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐