Day953.以假设驱动为指引 -遗留系统现代化实战

以假设驱动为指引

Hi,我是阿昌,今天学习记录的是关于以假设驱动为指引的内容。

很多人在做遗留系统现代化的时候呢,总觉得它是一个十分复杂的技术问题。

本来嘛,无论是代码的重构、架构的拆分,还是 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 的技术雷达中有一个条目就是假设驱动的遗留系统改造,讲的就是类似的技术。

假设驱动开发与传统的需求式开发不同,它先对要达成的目标做一个假设,这个目标其实才是我们真正要解决的问题。

然后根据假设制定解决方案,也就是平时开发时所面对的需求。

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

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

在应用假设驱动开发时,首先要根据自己的项目制定一些目标,然后再根据目标建立度量体系。

这样,所有的技术改进都可以围绕这些指标展开了。

在建立度量指标时要尽量避免绝对的数值,而要尽量用数据的比值。

比值更能体现数据的相对性,比绝对的数值更能减少误差。

  • 要记得把数据可视化出来,可以打印出来贴在墙上,也可以用一个大显示器立在团队旁边。

  • 它们一方面可以激励团队成员,另一方面也是向业务方展示工作的成果,让他们相信,一个看上去很技术向的改进任务,也能给业务带来巨大的价值。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/13621.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

除了学历,你更需要有能力

遥想当年&#xff0c;家里培养出一个大学生&#xff0c;是多荣耀的事&#xff01;可现今却处于一个比较尴尬的状态。 为什么大学生贬值得这么厉害&#xff1f;其实大学生之所以会不值钱不外乎三大原因&#xff1a;量大、与企业需求不匹配、质量差。 高校扩招下&#xff0c;大…

Python每日一练(20230423)

目录 1. 删除链表的倒数第 N 个结点 &#x1f31f;&#x1f31f; 2. 最小覆盖子串 &#x1f31f;&#x1f31f;&#x1f31f; 3. 二叉树的层序遍历 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏…

Java核心技术 卷1-总结-11

Java核心技术 卷1-总结-11 Java 集合框架将集合的接口与实现分离Collection接口迭代器泛型实用方法集合框架中的接口 Java 集合框架 将集合的接口与实现分离 Java集合类库将接口&#xff08;interface&#xff09;与实现&#xff08;implementation&#xff09;分离。 例如队…

小航助学答题系统编程等级考试scratch一级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.下列说法不正确的是&#xff1f;&#xff08; &#xff09; A.可以从声音库中随机…

使用buildroot编译完整系统【IMX6ULLPRO】

目录 构建 IMX6ULL Pro 版的根文件系统 编译系统 ​编辑 镜像文件 构建 IMX6ULL Pro 版的根文件系统 配置文件说明 编译系统 下面以 100ask_imx6ull_pro_ddr512m_systemV_qt5_defconfig 配置文 件为例&#xff0c;在 ubuntu 终端上说明 Buildroot 的配置过程&#x…

抖音数字人主播app

抖音数字人主播app是指一款利用计算机生成的虚拟数字人&#xff0c;在抖音平台上进行实时音视频传输和互动的应用程序。该软件可以让用户创建自己的虚拟数字人&#xff0c;并在抖音平台上进行实时互动和交流。 抖音数字人主播app通常需要包含以下功能&#xff1a; 3D建…

前端学习之路 来自前端方向学生的总结

恭喜您&#xff01;您发现了宝藏&#xff01; 我发现很多小伙伴&#xff0c;对于前端感兴趣&#xff0c;也很想去学好&#xff0c;但是却无从下手&#xff0c;不知道如何去学习。作为一名现处于大三即将大四的学生&#xff0c;借此机会来分享分享我的前端学习之路&#xff01;…

Visual Instruction Tuning: 用LLaVA近似多模态GPT-4

©Paperweekly 原创 作者 | Chunyuan Li 使用 GPT-4 进行视觉指令学习&#xff01;Visual Instruction Tuning with GPT-4! ▲ Generated by GLIGEN (https://gligen.github.io/): A cute lava llama and glasses 我们分享了 LLaVA (Language-and-Vision Assistant)&#…

设计模式--单例模式

目录 介绍 单例模式的八种实现方式 饿汉式(静态常量) 优缺点说明: 饿汉式(静态代码块) 优缺点说明 懒汉式(线程不安全) 优缺点说明 懒汉式(线程安全 同步方法) 优缺点说明 懒汉式(线程安全 同步代码块) 优缺点说明 双重检查 优缺点说明 静态内部类 优缺点说明 …

打怪升级之FPGA组成原理(LE部分)

FPGA芯片逻辑单元的原理 不论你使用哪一款FPGA芯片&#xff0c;其核心可编程逻辑单元都是从一段内存种按顺序读取执行并执行的过程。具体来说&#xff0c;FOGA芯片内部包括可编程逻辑块(LAB)、可配置输入输出单元(IOE)、时钟管理模块、嵌入式RAM(BRAN&#xff0c;在Cyclone IV…

PNAS: 这些病毒是原生动物基因组中的偷渡者

在对复杂单细胞微生物进行大规模研究时&#xff0c;奥地利因斯布鲁克大学生态学系的Christopher Bellas博士、Marie-Sophie Plakolb和Ruben Sommaruga教授发现了一个意外情况&#xff1a;微生物的基因组中找到超过30,000种先前未知的病毒DNA。这种“隐藏”的DNA可能允许宿主细胞…

字节跳动正式开源分布式训练调度框架 Primus

动手点关注 干货不迷路 项目地址&#xff1a;https://github.com/bytedance/primus 随着机器学习的发展&#xff0c;模型及训练模型所需的数据量越来越大&#xff0c;也都趋向于通过分布式训练实现。而算法工程师通常需要对这些分布式框架涉及到的底层文件存储和调度系统有较深…

基于 多态 的职工管理系统(Staff Management System)

目录 一、管理系统需求 作用&#xff1a;管理公司内所有员工的信息 分类&#xff1a;要显示每位员工的编号、姓名、岗位与职责 具体实现的功能&#xff1a; 二、创建管理 类 三、各个接口函数 1、菜单展示功能 2、 选择功能 3、创建员工功能 ①普通员工employee ②经理…

怎么批量把heic格式转化jpg,3招快速解决

怎么批量把heic格式转化jpg&#xff1f;heic是一种新型的图像文件格式&#xff0c;是苹果独家搞出来的一个图片格式&#xff0c;它小巧玲珑&#xff0c;而且图像质量超好&#xff0c;专门给iOS11系统用户用的。这种格式比老JPEG更厉害&#xff0c;不仅图片质量好&#xff0c;而…

【网络原理】应用层协议 与 传输层协议

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; 目 录 &#x1f3c9;一. 应用层协议⚾️二. 传输层协议&#x1f452;1. UDP 协议&#x1f302;2. 校验和&#x1f453;3. TCP 协议 &#x1f3c9;一. 应用层协议 我们自己写的应用…

Bitmap 实现当前在线用户数量

Bitmap是什么&#xff1f; Bitmap是Redis中的一种数据结构&#xff0c;它是一个类似于位数组的数据结构&#xff0c;用于处理位数据。在Redis中&#xff0c;Bitmap是使用字符串来存储的&#xff0c;一个Byte可以存储8个二进制位&#xff0c;一个字符串可以存储232个二进制位&a…

【CocosCreator入门】CocosCreator组件 | ProgressBar(进度条)组件

Cocos Creator 是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中的ProgressBar组件是一种用于实现进度条效果的重要组件。它可以让我们在游戏中展示各种进度条效果&#xff0c;例如加载进度条、血条等。 目录 一、组件介绍 二、组件属性 三、脚本…

12. 图的进阶

12. 图的进阶 12.1 有向图 在实际生活中&#xff0c;很多应用相关的图都是有方向性的&#xff0c;最直观的就是网络&#xff0c;可以从A页面通过链接跳转到B页面&#xff0c;那么a和b连接的方向是a->b,但不能说是b->a,此时我们就需要使用有向图来解决这一类问题&#x…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

为什么许多人吐槽C++11,那些语法值得我们学习呢?

致前行的人&#xff1a; 人生像攀登一座山&#xff0c;而找寻出路&#xff0c;却是一种学习的过程&#xff0c;我们应当在这过程中&#xff0c;学习稳定冷静&#xff0c;学习如何从慌乱中找到生机。 目录 1.C11简介 2.统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 …