You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
18 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 05 | 以假设驱动为指引:如何评价遗留系统的现代化成果?
你好,我是姚琪琳。
前两节课,我们学习了遗留系统现代化的第一个原则:以降低认知负载为前提,以及能够显著降低认知负载的利器——活文档。今天我们就来看看遗留系统现代化的第二个原则:**以假设驱动为指引**。
我们很多人在做遗留系统现代化的时候呢总觉得它是一个十分复杂的技术问题。本来嘛无论是代码的重构、架构的拆分还是DevOps平台的搭建或技术栈的升级无一不是技术活动。
下面我来分享一个我早年间的经历,看看能不能颠覆你的想法。
## 脱离业务的技术改进都是耍流氓
十年前我曾经试图去主导一次技术改进希望将一个遗留系统上的JDK从1.4升级到5。你可以想象一下使用Java 1.4开发是一个什么样的情形没有stream、没有泛型甚至没有枚举实现一个简单的功能都需要好几行代码当然现在的Java也没好到哪去……在这样的项目上工作简直痛不欲生。
我当时做了充分的调研,制定了详细的计划,以确保升级过程的平滑。然而这样一个看起来很“正常”的改进却被部门领导叫停了。
他的理由是系统刚刚上线不久一两年内不会有很多的新需求旧JDK导致的开发痛点并不明显。而业务方也没有明确提出未来要提升开发效率以支撑更多的需求。所以这样的改进虽然看上去在技术上十分必要但在业务上优先级却没那么高。
这番话一语点醒梦中人。在“怎么改”这件事儿上,我当时的确做了不少功课,但偏偏“要不要改”这个关键问题脱离了业务,所以成了单方面的“技术自嗨”。这种改进虽说从技术上看十分必要,但业务上优先级却没那么高。
这个翻车案例告诉我们,**技术要为业务服务。**业务不需要的话,技术升级没有任何意义。做好了,业务方面也感知不到;做不好,很有可能导致项目失败,可谓费力不讨好。
那么,到底如何做,才能让技术更好地为业务服务呢?
如果一个遗留系统平时用的人不多,需求也不多,一两个开发人员完全能够应付得来,这种情况还需要技术的更新换代吗?其实对于这样的系统,最好的方案就是保持原样,根本不需要做什么升级和优化,因为它所带来的业务价值太小,投入产出比过低。
但面对使用人数众多、需求纷至沓来的遗留系统,想要让业务方充分感知到技术迭代带来的好处,又该怎么办呢?这时候假设驱动的方法就派上用场了。我们先说说假设驱动是什么,又该怎样应用。
## 什么是假设驱动?
假设驱动实际上是一种科学的研究方法,在面对一个问题时,我们先要分析问题,然后试着提出一种阐述或者假设,去解释我们的发现。接着就到了实验环节,如果实验结果满足假设,就证明我们的理论是正确的。
假设驱动的思想在数学、物理、生物等科学领域都是十分常见的方法,比如哥德巴赫猜想、量子力学等,最初都是通过假设的方式提出来,并在后期通过实验加以证明或验证的。
实验是科学研究的基础,但并不是只有在实验室里才能做。在软件开发领域,我们同样可以做实验,这就是**假设驱动开发Hypothesis-Driven Development简称HDD**。
《持续交付》的作者Jez Humble曾经说过
> “验证业务模式或产品理念的最低效的方法,是构建完整的产品以查看设想中的需求是否真实存在”。
我们在构建一个产品或功能之前,应该先扪心自问:“我们应该构建它吗?理由是什么?”然后开展最廉价、最快速的实验,通过用户研究,验证设想的功能是否会产生预想的业务成果。
在一个产品处于探索、复杂和不确定的阶段时,我们更需要的是假设,而不是传统的需求。也就是说,我们假设一个功能上线之后会得到一个什么样的结果,然后等功能上线后,再去验证是否得到了这样的结果,从而得出结论和提取知识。
![图片](https://static001.geekbang.org/resource/image/bf/60/bf108d0f6034379c385e884c3ee06160.png?wh=1400x761)
我们可以看到,以假设驱动的方式去构建产品,可以将用户的反馈纳入到开发过程中来,让每一个需求的效果都可以度量。
比如对于一个电子商城的App我们假设如果在商品的展示页面中加入视频功能商品的销量就会增加10%。之后我们就开始开发视频功能上线之后通过A/B测试做实验对比看看是否加入了视频功能的商品销量真的会增加10%。
![图片](https://static001.geekbang.org/resource/image/12/46/121e2d8f47d3ac3d118968b5f1a22a46.jpg?wh=1920x1457)
你可能会说,我公司的商品展示页面也有视频功能,但就是按照普通需求去开发的,这跟假设驱动开发的方式有什么区别吗?其实区别是很明显的。
一个需求总是要解决一个业务问题的,电商平台不会平白无故开发一个视频功能,背后要解决的问题,就是提升商品销量。但以普通需求的方式提出来,我们就不会特意做度量,等到上线后发现没达成这个目标,也就不了了之了。
你可以回忆一下,你所在的系统中,有多少费时费力开发完但却没人用的功能?它们中大多数都是因为没达成预想的假设。这是巨大的浪费。
而**如果以假设驱动的方式进行开发,我可以在某个方向上快速验证,如果假设不成立,就立即止损,不再追加投资。这样整个过程就显得十分精益了**。
还拿商品页的视频功能为例,我可以先开发一个极其简单的版本快速上线,在对比发现真的对销量有提升效果后,再来逐步优化整个方案,比如延长视频时间、提高视频清晰度,甚至把直播带货时该商品的介绍剪辑下来,放到商品页等等。这样不断迭代,每一步都通过假设驱动,并不断验证假设,得到能带来最多客户价值的方案。
## 在遗留系统中应用假设驱动开发
既然新功能开发上,我们可以借助假设驱动实现“多快好省”,那遗留系统现代化是不是同样适用呢?答案是肯定的。
在遗留系统现代化的过程中,我们接收到的任务,呈现形式往往也是需求或者故事卡,得到的也都是一个技术结果而不是业务结果。比如重构某段代码来提升可读性,或者添加测试来提高某个模块的单元测试覆盖率。这样的技术任务是很难验收的,而且上线之后,业务方无法很容易地感知它所带来的价值。
这时我们可以将假设驱动开发引入到遗留系统现代化中来将那些以“As…I want…So that…”或“Given…When…Then…”编写的故事卡和验收条件改为下面这种形式
> 我们相信<某个功能>
> 将<产生某种结果>
> 当<我们看到某种可度量的信号>时,我们就有信心沿着这个方向继续下去
举个例子来说就是:
> 我们相信,为<库存模块添加单元测试>
> 将<提升库存模块的内建质量>
> 当<我们看到库存模块新需求的bug数量连续三个月降低>时,我们就有信心沿着这个方向继续下去
你看如果只添加单元测试而不拿出添加完测试后的数据业务方就无法直观看到这样做的好处这样的改进也很难获得他们认可。但如果我拿着一份图表向业务方展示连续三个月降低的bug数他们一定会非常开心并支持我们的下一步计划。
在开发软件时,我们主张[关注成效而非产出](https://time.geekbang.org/column/article/268129)在遗留系统现代化过程中同样也应该关注成效而不仅仅是做了哪些改进。在上面的例子中添加完测试后的测试数量和覆盖率就是产出而bug数降低就是成效。
如果只注重产出,我们就会更关注团队都完成了哪些技术改进,考核维度是工作量。这样能快速完成的工作优先级会更高,改进带来的业务价值反而被忽视;但如果注重成效,我们更关注改进如何服务于业务,考核维度变成某项改进,在多大程度上能**提高用户效率**,并在上线后关注相关指标的变化。
关注成效,不但可以激励我们去找出可以衡量业务价值的指标,也能帮助我们避免一些价值不大的技术改进。
还拿添加单元测试举例,如果只是为了产出,那我们关注的就是提高测试数量和测试覆盖率,当这样的度量指标,落到团队头上去真正执行时,他们就会想出一些匪夷所思的方式。
比如给Java类的getter/setter添加测试那产出的测试数量是惊人的但却对降低bug数量完全没有任何帮助是毫无价值的。这就像如果用代码行数去评价开发人员的工作就会多出很多无用代码一样。
说到这,我们明确了关注成效的必要性,下一步就是把“成效”转化成更明确的指标,这样才能更好地建立假设。
## 明确目标和度量指标
在以假设驱动的方式推动遗留系统现代化时,首要工作就是确定目标。没有目标的工作会让我们变成无头苍蝇,到处乱撞。以下图为例,我们通常可以制定这样四个维度的目标。
![图片](https://static001.geekbang.org/resource/image/b4/63/b48084a4f769af2973c271f24b751063.jpg?wh=1920x979)
1.业务敏捷:系统快速响应市场变化和新兴机会的能力,比如一个需求从提出到上线的时间;
2.运营效率:系统提升价值流效率的能力,比如一个业务从开始办理到办理完成的时间;
3.客户洞见:系统理解和解释客户数据、行为和反馈的能力,比如前面提到的,客户对于商品视频和直播等特性的敏感程度,我们应该如何去解释;
4.系统韧性与弹性:云时代对于系统的基本要求。
确定好目标,接下来就是制定各个目标的度量指标了。软件度量是很多项目都欠缺的一环,缺少了度量,就没有办法对我们的系统做出有效的评价。因此有必要在这里重点讲一讲。
以业务敏捷为例我们可以进一步细化成3个维度、6个指标。然后再讨论出某项遗留系统现代化的举措实施之后能带来怎样的数据变化。等交付之后再收集度量数据并把数据可视化。
![图片](https://static001.geekbang.org/resource/image/f8/fa/f87eb8467a6e180503047fc417b5e6fa.jpg?wh=1920x807)
对于其他维度的指标,我在这里举一些例子。你可以根据自己项目的实际情况,定制更适合的指标。
运营效率主要看某个业务条线的技术改进,会对该业务带来哪些效率提升。比如银行贷款业务,从借贷申请到发放贷款的间隔时间,就是一个不错的指标;而对于保险公司的投保业务,可以选择从投保申请到核保完成的时间。
客户洞见,主要看技术改进能否帮助系统更好地理解客户行为。这一点乍听上去有点虚,我来举个例子你就明白了。
很多电商系统都有秒杀功能,在客户秒杀时会造成大量的并发请求,有时甚至会拖垮服务器。这种把客户秒杀行为与系统吞吐量做关联的能力,就是客户洞见。那么给系统“上云”,或者引入缓存,都是可以提升吞吐量的方案,最终服务于优化秒杀体验这个目的。
你可能觉得,这个例子这么简单,哪算得上什么“洞见”嘛!
其实,所谓客户洞见,就是要求我们站在客户视角去理解客户。有些可能很好理解,有些却不是那么直观。比如以地图的形式显示订单的运送路线和状态,可以显著地降低客户的投诉率。这里面隐含的一个客户洞见就是,客户非常关注包裹的物流信息,而地图的形式比文字列表的形式更能让客户安心。
![图片](https://static001.geekbang.org/resource/image/4a/d5/4aea0be02082d5bce447197d57b29fd5.jpg?wh=1148x780)
系统韧性和弹性的指标就很多了,比如平均恢复时间、平均故障时间等、每秒请求数、每秒事务数等。
在确定这些指标时,我们可以通过正推的方式(即某项技术改进可以改善哪些指标)推导指标,也可以通过逆推的方式来寻找解决方案(即想要改善某个指标,都可以通过哪些技术改进来实现)。
举个例子我们希望优化DevOps平台就可以用部署频率这个指标来评价优化的结果我们希望减少线上bug数量也可以逆推出提高测试覆盖率、加强代码评审、拆分模块以降低认知负载等质量内建的手段。
不同目标维度的指标可以是重合的,比如服务恢复时长,既可作为业务敏捷维度的指标,也可作为系统韧性与弹性的指标;一项技术改进也可能带来多个指标的变化。
在制定度量指标时,还要注意的一点是,**要尽量使用相对的数据,避免使用绝对的数据**。比如一次交付周期内的bug数就是一个绝对的数字它在有些情况下有意义但在某些情况下可能就没有意义。
比如某个需求特别大需要横跨几个交付周期在前几个交付周期时不会上线这时bug数自然就少。等最后一个交付周期上线时可能一下子就会多出不少bug。通过这样绝对的数据就不能推出“前几个交付周期质量高最后一个交付周期质量差”的结论。我们把指标换成bug数和上线需求数的比值就可以避免这种偏差。
实际上bug数与需求数的比值也并不十全十美的因为bug的严重程度、修复难度都不相同需求的大小、紧急程度和难易程度也不相同。这时可以用“行bug数”来代替“需求bug数”但不同代码行的难度显然也是不同的但扩大代码行的数量就可以拉平这种差别比如“每千行代码的bug数”。
你还可以将bug的特点和需求的特点作为权重引入到整个评价体系中来。不过如果你的项目上还没有任何度量我建议你先把简单的度量体系搭建起来等想要更精准度量的时候再引入具体系数也不迟。
想要了解更多关于软件指标和度量的内容,推荐你看看《精益软件度量》这本书。
指标制定好之后,等各项改进任务以增量(下节课再讲什么是增量)上线之后,我们就可以开始收集数据,持续度量了。需要牢记的是,**一定要把度量结果用各种图表可视化出来**。
一方面,这可以向开发团队展示改造的成果以及给公司带来的价值,以前开发人员可能只知道我在哪里添加了什么代码,但并不知道这几行代码给公司带来了什么样的价值。另一方面也把改造的过程向业务部门、运营部门、市场部门透明,让他们了解并支持我们的工作。
有了可度量的指标,遗留系统的假设驱动开发就成为了可能。我们在开始一项改进任务时,首先要对相关指标的变化做一个假设,等改进任务的部分交付之后,再收集相应的指标数据,以验证假设。
如果数据是朝着假设的方向变动的,我们就有理由继续投资后续的改进;如果数据变化不明显或是向相反的方向变化,就要停下来仔细研究一下原因了。
**以假设驱动为指引的遗留系统现代化,就是说我们所做的所有现代化任务,都应该能够提升这些指标**。这些指标就像灯塔一样,引领着我们朝着正确的方向前行。
## 小结
今天我们介绍了遗留系统现代化的第二个原则以假设驱动为指引。假设驱动开发是精益里的一个概念不过迁移到遗留系统现代化中也完全适用。Thoughtworks的技术雷达中有一个条目就是[假设驱动的遗留系统改造](https://www.thoughtworks.com/radar/techniques/hypothesis-driven-legacy-renovation),讲的就是类似的技术。
假设驱动开发与传统的需求式开发不同,它先对要达成的目标做一个假设,这个目标其实才是我们真正要解决的问题。然后根据假设制定解决方案,也就是我们平时开发时所面对的需求。
不同于传统需求式开发,并不是功能验收上线之后就算完成了,而是还要验证假设,看看所收集到的数据是否支持我们的假设,从而帮助我们更好地演进产品。
不以假设驱动,遗留系统现代化的很多技术改进就会盲目开展,最后忘了初心,走错了方向。
在应用假设驱动开发时,你首先要根据自己的项目制定一些目标,然后再根据目标建立度量体系。这样,所有的技术改进都可以围绕这些指标展开了。
在建立度量指标时要尽量避免绝对的数值,而要尽量用数据的比值。比值更能体现数据的相对性,比绝对的数值更能减少误差。
最后,要记得把数据可视化出来,可以打印出来贴在墙上,也可以用一个大显示器立在团队旁边。它们一方面可以激励团队成员,另一方面也是向业务方展示工作的成果,让他们相信,一个看上去很技术向的改进任务,也能给业务带来巨大的价值。
[下节课](https://time.geekbang.org/column/article/510594),我们会讲**以增量演进为手段**这个原则,它能有效指导我们在确定完指标之后,在行动上如何一步一步实现这些目标,敬请期待。
## 思考题
感谢你学完了这节课的内容,今天的思考题是:你的项目是否存在盲目做技术改进的情况?你们的改进在上线之后是否在用指标来度量呢?都有哪些指标?
欢迎把你项目上遗留系统现代化的心得和经验分享出来,也希望你把这节课分享给你的朋友,我们一起进步。