跳转至

05 以假设驱动为指引:如何评价遗留系统的现代化成果?

你好,我是姚琪琳。

前两节课,我们学习了遗留系统现代化的第一个原则:以降低认知负载为前提,以及能够显著降低认知负载的利器——活文档。今天我们就来看看遗留系统现代化的第二个原则:以假设驱动为指引

我们很多人在做遗留系统现代化的时候呢,总觉得它是一个十分复杂的技术问题。本来嘛,无论是代码的重构、架构的拆分,还是DevOps平台的搭建或技术栈的升级,无一不是技术活动。

下面我来分享一个我早年间的经历,看看能不能颠覆你的想法。

脱离业务的技术改进都是耍流氓

十年前,我曾经试图去主导一次技术改进,希望将一个遗留系统上的JDK从1.4升级到5。你可以想象一下,使用Java 1.4开发是一个什么样的情形,没有stream、没有泛型,甚至没有枚举,实现一个简单的功能都需要好几行代码(当然现在的Java也没好到哪去……),在这样的项目上工作简直痛不欲生。

我当时做了充分的调研,制定了详细的计划,以确保升级过程的平滑。然而这样一个看起来很“正常”的改进却被部门领导叫停了。

他的理由是,系统刚刚上线不久,一两年内不会有很多的新需求,旧JDK导致的开发痛点并不明显。而业务方也没有明确提出,未来要提升开发效率以支撑更多的需求。所以,这样的改进,虽然看上去在技术上十分必要,但在业务上优先级却没那么高。

这番话一语点醒梦中人。在“怎么改”这件事儿上,我当时的确做了不少功课,但偏偏“要不要改”这个关键问题脱离了业务,所以成了单方面的“技术自嗨”。这种改进虽说从技术上看十分必要,但业务上优先级却没那么高。

这个翻车案例告诉我们,技术要为业务服务。业务不需要的话,技术升级没有任何意义。做好了,业务方面也感知不到;做不好,很有可能导致项目失败,可谓费力不讨好。

那么,到底如何做,才能让技术更好地为业务服务呢?

如果一个遗留系统平时用的人不多,需求也不多,一两个开发人员完全能够应付得来,这种情况还需要技术的更新换代吗?其实对于这样的系统,最好的方案就是保持原样,根本不需要做什么升级和优化,因为它所带来的业务价值太小,投入产出比过低。

但面对使用人数众多、需求纷至沓来的遗留系统,想要让业务方充分感知到技术迭代带来的好处,又该怎么办呢?这时候假设驱动的方法就派上用场了。我们先说说假设驱动是什么,又该怎样应用。

什么是假设驱动?

假设驱动实际上是一种科学的研究方法,在面对一个问题时,我们先要分析问题,然后试着提出一种阐述或者假设,去解释我们的发现。接着就到了实验环节,如果实验结果满足假设,就证明我们的理论是正确的。

假设驱动的思想在数学、物理、生物等科学领域都是十分常见的方法,比如哥德巴赫猜想、量子力学等,最初都是通过假设的方式提出来,并在后期通过实验加以证明或验证的。

实验是科学研究的基础,但并不是只有在实验室里才能做。在软件开发领域,我们同样可以做实验,这就是假设驱动开发(Hypothesis-Driven Development,简称HDD)

《持续交付》的作者Jez Humble曾经说过:

“验证业务模式或产品理念的最低效的方法,是构建完整的产品以查看设想中的需求是否真实存在”。

我们在构建一个产品或功能之前,应该先扪心自问:“我们应该构建它吗?理由是什么?”然后开展最廉价、最快速的实验,通过用户研究,验证设想的功能是否会产生预想的业务成果。

在一个产品处于探索、复杂和不确定的阶段时,我们更需要的是假设,而不是传统的需求。也就是说,我们假设一个功能上线之后会得到一个什么样的结果,然后等功能上线后,再去验证是否得到了这样的结果,从而得出结论和提取知识。

图片

我们可以看到,以假设驱动的方式去构建产品,可以将用户的反馈纳入到开发过程中来,让每一个需求的效果都可以度量。

比如对于一个电子商城的App,我们假设如果在商品的展示页面中加入视频功能,商品的销量就会增加10%。之后我们就开始开发视频功能,上线之后通过A/B测试做实验对比,看看是否加入了视频功能的商品,销量真的会增加10%。

图片

你可能会说,我公司的商品展示页面也有视频功能,但就是按照普通需求去开发的,这跟假设驱动开发的方式有什么区别吗?其实区别是很明显的。

一个需求总是要解决一个业务问题的,电商平台不会平白无故开发一个视频功能,背后要解决的问题,就是提升商品销量。但以普通需求的方式提出来,我们就不会特意做度量,等到上线后发现没达成这个目标,也就不了了之了。

你可以回忆一下,你所在的系统中,有多少费时费力开发完但却没人用的功能?它们中大多数都是因为没达成预想的假设。这是巨大的浪费。

如果以假设驱动的方式进行开发,我可以在某个方向上快速验证,如果假设不成立,就立即止损,不再追加投资。这样整个过程就显得十分精益了

还拿商品页的视频功能为例,我可以先开发一个极其简单的版本快速上线,在对比发现真的对销量有提升效果后,再来逐步优化整个方案,比如延长视频时间、提高视频清晰度,甚至把直播带货时该商品的介绍剪辑下来,放到商品页等等。这样不断迭代,每一步都通过假设驱动,并不断验证假设,得到能带来最多客户价值的方案。

在遗留系统中应用假设驱动开发

既然新功能开发上,我们可以借助假设驱动实现“多快好省”,那遗留系统现代化是不是同样适用呢?答案是肯定的。

在遗留系统现代化的过程中,我们接收到的任务,呈现形式往往也是需求或者故事卡,得到的也都是一个技术结果而不是业务结果。比如重构某段代码来提升可读性,或者添加测试来提高某个模块的单元测试覆盖率。这样的技术任务是很难验收的,而且上线之后,业务方无法很容易地感知它所带来的价值。

这时我们可以将假设驱动开发引入到遗留系统现代化中来,将那些以“As…I want…So that…”或“Given…When…Then…”编写的故事卡和验收条件,改为下面这种形式:

我们相信<某个功能>
将<产生某种结果>
当<我们看到某种可度量的信号>时,我们就有信心沿着这个方向继续下去

举个例子来说就是:

我们相信,为<库存模块添加单元测试>
将<提升库存模块的内建质量>
当<我们看到库存模块新需求的bug数量连续三个月降低>时,我们就有信心沿着这个方向继续下去

你看,如果只添加单元测试,而不拿出添加完测试后的数据,业务方就无法直观看到这样做的好处,这样的改进也很难获得他们认可。但如果我拿着一份图表,向业务方展示连续三个月降低的bug数,他们一定会非常开心,并支持我们的下一步计划。

在开发软件时,我们主张关注成效而非产出,在遗留系统现代化过程中,同样也应该关注成效,而不仅仅是做了哪些改进。在上面的例子中,添加完测试后的测试数量和覆盖率就是产出,而bug数降低就是成效。

如果只注重产出,我们就会更关注团队都完成了哪些技术改进,考核维度是工作量。这样能快速完成的工作优先级会更高,改进带来的业务价值反而被忽视;但如果注重成效,我们更关注改进如何服务于业务,考核维度变成某项改进,在多大程度上能提高用户效率,并在上线后关注相关指标的变化。

关注成效,不但可以激励我们去找出可以衡量业务价值的指标,也能帮助我们避免一些价值不大的技术改进。

还拿添加单元测试举例,如果只是为了产出,那我们关注的就是提高测试数量和测试覆盖率,当这样的度量指标,落到团队头上去真正执行时,他们就会想出一些匪夷所思的方式。

比如给Java类的getter/setter添加测试,那产出的测试数量是惊人的,但却对降低bug数量完全没有任何帮助,是毫无价值的。这就像如果用代码行数去评价开发人员的工作,就会多出很多无用代码一样。

说到这,我们明确了关注成效的必要性,下一步就是把“成效”转化成更明确的指标,这样才能更好地建立假设。

明确目标和度量指标

在以假设驱动的方式推动遗留系统现代化时,首要工作就是确定目标。没有目标的工作会让我们变成无头苍蝇,到处乱撞。以下图为例,我们通常可以制定这样四个维度的目标。

图片

1.业务敏捷:系统快速响应市场变化和新兴机会的能力,比如一个需求从提出到上线的时间;

2.运营效率:系统提升价值流效率的能力,比如一个业务从开始办理到办理完成的时间;

3.客户洞见:系统理解和解释客户数据、行为和反馈的能力,比如前面提到的,客户对于商品视频和直播等特性的敏感程度,我们应该如何去解释;

4.系统韧性与弹性:云时代对于系统的基本要求。

确定好目标,接下来就是制定各个目标的度量指标了。软件度量是很多项目都欠缺的一环,缺少了度量,就没有办法对我们的系统做出有效的评价。因此有必要在这里重点讲一讲。

以业务敏捷为例,我们可以进一步细化成3个维度、6个指标。然后再讨论出某项遗留系统现代化的举措实施之后,能带来怎样的数据变化。等交付之后,再收集度量数据,并把数据可视化。

图片

对于其他维度的指标,我在这里举一些例子。你可以根据自己项目的实际情况,定制更适合的指标。

运营效率主要看某个业务条线的技术改进,会对该业务带来哪些效率提升。比如银行贷款业务,从借贷申请到发放贷款的间隔时间,就是一个不错的指标;而对于保险公司的投保业务,可以选择从投保申请到核保完成的时间。

客户洞见,主要看技术改进能否帮助系统更好地理解客户行为。这一点乍听上去有点虚,我来举个例子你就明白了。

很多电商系统都有秒杀功能,在客户秒杀时会造成大量的并发请求,有时甚至会拖垮服务器。这种把客户秒杀行为与系统吞吐量做关联的能力,就是客户洞见。那么给系统“上云”,或者引入缓存,都是可以提升吞吐量的方案,最终服务于优化秒杀体验这个目的。

你可能觉得,这个例子这么简单,哪算得上什么“洞见”嘛!

其实,所谓客户洞见,就是要求我们站在客户视角去理解客户。有些可能很好理解,有些却不是那么直观。比如以地图的形式显示订单的运送路线和状态,可以显著地降低客户的投诉率。这里面隐含的一个客户洞见就是,客户非常关注包裹的物流信息,而地图的形式比文字列表的形式更能让客户安心。

图片

系统韧性和弹性的指标就很多了,比如平均恢复时间、平均故障时间等、每秒请求数、每秒事务数等。

在确定这些指标时,我们可以通过正推的方式(即某项技术改进可以改善哪些指标)推导指标,也可以通过逆推的方式来寻找解决方案(即想要改善某个指标,都可以通过哪些技术改进来实现)。

举个例子,我们希望优化DevOps平台,就可以用部署频率这个指标来评价优化的结果;我们希望减少线上bug数量,也可以逆推出提高测试覆盖率、加强代码评审、拆分模块以降低认知负载等质量内建的手段。

不同目标维度的指标可以是重合的,比如服务恢复时长,既可作为业务敏捷维度的指标,也可作为系统韧性与弹性的指标;一项技术改进也可能带来多个指标的变化。

在制定度量指标时,还要注意的一点是,要尽量使用相对的数据,避免使用绝对的数据。比如一次交付周期内的bug数就是一个绝对的数字,它在有些情况下有意义,但在某些情况下可能就没有意义。

比如某个需求特别大,需要横跨几个交付周期,在前几个交付周期时不会上线,这时bug数自然就少。等最后一个交付周期上线时,可能一下子就会多出不少bug。通过这样绝对的数据,就不能推出“前几个交付周期质量高,最后一个交付周期质量差”的结论。我们把指标换成bug数和上线需求数的比值,就可以避免这种偏差。

实际上,bug数与需求数的比值也并不十全十美的,因为bug的严重程度、修复难度都不相同,需求的大小、紧急程度和难易程度也不相同。这时可以用“行bug数”来代替“需求bug数”,但不同代码行的难度显然也是不同的,但扩大代码行的数量就可以拉平这种差别,比如“每千行代码的bug数”。

你还可以将bug的特点和需求的特点作为权重,引入到整个评价体系中来。不过如果你的项目上还没有任何度量,我建议你先把简单的度量体系搭建起来,等想要更精准度量的时候,再引入具体系数也不迟。

想要了解更多关于软件指标和度量的内容,推荐你看看《精益软件度量》这本书。

指标制定好之后,等各项改进任务以增量(下节课再讲什么是增量)上线之后,我们就可以开始收集数据,持续度量了。需要牢记的是,一定要把度量结果用各种图表可视化出来

一方面,这可以向开发团队展示改造的成果以及给公司带来的价值,以前开发人员可能只知道我在哪里添加了什么代码,但并不知道这几行代码给公司带来了什么样的价值。另一方面也把改造的过程向业务部门、运营部门、市场部门透明,让他们了解并支持我们的工作。

有了可度量的指标,遗留系统的假设驱动开发就成为了可能。我们在开始一项改进任务时,首先要对相关指标的变化做一个假设,等改进任务的部分交付之后,再收集相应的指标数据,以验证假设。

如果数据是朝着假设的方向变动的,我们就有理由继续投资后续的改进;如果数据变化不明显或是向相反的方向变化,就要停下来仔细研究一下原因了。

以假设驱动为指引的遗留系统现代化,就是说我们所做的所有现代化任务,都应该能够提升这些指标。这些指标就像灯塔一样,引领着我们朝着正确的方向前行。

小结

今天我们介绍了遗留系统现代化的第二个原则:以假设驱动为指引。假设驱动开发是精益里的一个概念,不过迁移到遗留系统现代化中也完全适用。Thoughtworks的技术雷达中有一个条目就是假设驱动的遗留系统改造,讲的就是类似的技术。

假设驱动开发与传统的需求式开发不同,它先对要达成的目标做一个假设,这个目标其实才是我们真正要解决的问题。然后根据假设制定解决方案,也就是我们平时开发时所面对的需求。

不同于传统需求式开发,并不是功能验收上线之后就算完成了,而是还要验证假设,看看所收集到的数据是否支持我们的假设,从而帮助我们更好地演进产品。

不以假设驱动,遗留系统现代化的很多技术改进就会盲目开展,最后忘了初心,走错了方向。

在应用假设驱动开发时,你首先要根据自己的项目制定一些目标,然后再根据目标建立度量体系。这样,所有的技术改进都可以围绕这些指标展开了。

在建立度量指标时要尽量避免绝对的数值,而要尽量用数据的比值。比值更能体现数据的相对性,比绝对的数值更能减少误差。

最后,要记得把数据可视化出来,可以打印出来贴在墙上,也可以用一个大显示器立在团队旁边。它们一方面可以激励团队成员,另一方面也是向业务方展示工作的成果,让他们相信,一个看上去很技术向的改进任务,也能给业务带来巨大的价值。

下节课,我们会讲以增量演进为手段这个原则,它能有效指导我们在确定完指标之后,在行动上如何一步一步实现这些目标,敬请期待。

思考题

感谢你学完了这节课的内容,今天的思考题是:你的项目是否存在盲目做技术改进的情况?你们的改进在上线之后是否在用指标来度量呢?都有哪些指标?

欢迎把你项目上遗留系统现代化的心得和经验分享出来,也希望你把这节课分享给你的朋友,我们一起进步。

精选留言(13)
  • aoe 👍(8) 💬(1)

    DDD、TDD、假设驱动都需要想象力,当个程序员要想的多一点

    2022-04-20

  • Kent 👍(2) 💬(1)

    - 假设驱动开发,本质上是以**成效**为目的的开发。 - 成效=需求产出+技术效率。 - 只关注需求产出会产生大量技术债。只关注技术效率会浪费开发资源。 - 决定开发任务时遵循假设驱动的思考模型,可以帮助我们审视开发任务的成效。即:“我们相信 < 某个功能 >将 < 产生某种结果 >当 < 我们看到某种可度量的信号 > 时,我们就有信心沿着这个方向继续下去” - 贯彻假设驱动开发,应当尽可能引入合理的成效评价指标,既可以帮助团队客观衡量工作情况,也可以向团队外相关方展示价值。

    2022-05-08

  • 小菜鸟 👍(1) 💬(1)

    本质上就是项目的roi,投入产出比,要有可量化的收益。关于ab实验有个问题请教作者,如果每个需求都增加对照实验会额外多一些成本,毕竟又要开发分组功能,还要测试分组功能,为啥要做ab呢,本身就是业务需求,即便效果不好,又能怎么样呢,技术说了又不算

    2022-06-12

  • 跫音 👍(0) 💬(1)

    需求响应力如何去衡量呢?如果没有一个需求管理工具,感觉量化有点难度。有推荐量化需求的工具吗?

    2022-05-17

  • 子夜枯灯 👍(0) 💬(1)

    假设驱动法,还能举例一些场景么?老师。这部分理解的差一些,希望能给出一到两个例子

    2022-05-10

  • “K、Biao. 👍(0) 💬(1)

    客户洞见这条价值指标如何做量化定义呢?

    2022-04-22

  • 特修斯之船 👍(0) 💬(1)

    Bug数这东西,不是越修越少,趋向于0的吗,不好衡量吧

    2022-04-20

  • peter 👍(0) 💬(1)

    正常开发中,不管是传统的瀑布式开发,还是敏敏开发,是否可以运用假设驱动方法?

    2022-04-20

  • MaLu 👍(1) 💬(0)

    假设其实面向的还是价值,价值是需要被验证的,而验证就要有度量指标,口说无凭,拿数据说话。数据从哪里来?收集什么数据,数据做什么处理,怎样分析、怎样统计,面向的是最终的业务价值。而不是为了收集而漫无目的地收集。

    2022-11-07

  • Jaising 👍(0) 💬(0)

    软件度量在传统行业数字化转型过程中确实很面临很多问题,比如招投标软件价值衡量、软件开发工作量核算,而且很多私有化部署很难做到精细化运营,最终就会出现很多以客户满意度这种可操作性很强的度量手段,我想这也是数字化转型不断深入后行业专业性提升的方向。

    2024-09-16

  • belief 👍(0) 💬(0)

    小公司用不到

    2022-12-28

  • 花花大脸猫 👍(0) 💬(0)

    年轻的时候有过,完全就是为了技术搞,但是对应的功能其实在线上已经没有多少用户再增加了,完全就可以维持现状!!!没有现在业务的角度去审视技术修改带来的成本与收益

    2022-10-01

  • Jxin 👍(0) 💬(0)

    卫哲 3+1

    2022-09-22