【敏捷FAQ 第5回】文档不可靠的现行系统,应以何为“规范”进行验证?

日本語|English|中国语
| 7 min read
Author: tomohiro-fujii tomohiro-fujiiの画像
Information

为了覆盖更广泛的受众,这篇文章已从日语翻译而来。
您可以在这里找到原始版本。

 感谢您阅读本文。我是隶属于敏捷小组的藤井智弘。

 这是将更改项目的“现有功能保障”分为三个子主题探讨的三部曲的第三篇(最终回)。在第三回中,我们观察了“现有”的实体,在第四回中,我们构建了可达成一致的“整体框架”。本次的子主题是“验证、编制预算并分配”。

问题

#

 在更改项目中,打开现有系统的规格书后才发现最后一次更新竟然是10年前。无法保证其与代码保持一致,询问现场人员时,他们不断地说“手册里没有,但实际上是这样操作的”。那么应该以什么作为“规范”进行验证呢?没有足够的时间逐一人工对照。更何况,一旦发现意外事项,就会牵涉到追加预算的讨论,老实说,我甚至有些害怕去发现这些问题。

回答

#

 在第四回中我们发现,虽然从用例粒度上可以达成整体框架的共识,但其具体内容(切片)只有通过运行现物才能得知。本文讨论如何发现这些“不明内容”并将其转化为已验证状态,以及如何编制和分配接纳这些发现的预算。

 先说结论:如果无法信任文档,就应将最值得信赖的——正在运行的现物——作为规范,对于未知的规范,不要等到发布后再将其作为事故才去发现,而是在开发过程中通过小范围运行来提前发现。在更改项目中,敏捷模式之所以有效,正是因为它作为“发现手段”。“因为没有齐全的文档所以无法验证”是继“现有功能保障”“敏捷不会确定所有事项”之后的第三种会停止思考的说法。在陷入这种思路之前,不妨考虑能否将验证对象换成现物。

 在第三回中提到的“规范的三个实体”需要分别采用不同的验证手段。

  • 不依赖纸面文档——但也不要丢弃它,应将其用作探查与现物差异的线索。
  • 通过特性化测试来固定代码的实际行为。
  • 只有通过早期的部分运行和现场访谈才能发现人员的实际操作。

      图1:三种实现各自采用不同的验证手段。
图1: 三个实体与三种验证手段

 以下方法大体按照该顺序排列,并设置产品负责人(PO)统一汇总判断已发现事项的处理流程。

处方——以现物为规范,发现未知

#

通过实际使用数据衡量“真实的现有功能”
 正如第四回所写,“对无使用记录的功能不进行迁移”,首先收集现有系统的操作日志、报表输出记录、界面访问记录等信息,识别实际被使用的功能。将此测量结果逐项记录在各用例条目中。
 但是,如年度决算、税制变更应对、灾害时的替代流程等,虽使用频率低但在法规或年度业务中必不可少的功能,可能在日志中显示“未使用”。这些应依据业务日历和法规要求另行收集,而非仅凭使用记录。

通过特性化测试将“现物”固定为规范
 特性化测试(characterization test)是 Michael Feathers 在著作『レガシーコード改善ガイド』中介绍的技术,用于将现有系统的实际行为——不论其是否正确——记录为测试。通过从现物中采集“对于该输入会有这样的响应”,并以此构建回归测试网,机械地验证迁移后行为是否与现有系统一致。第一条测试案例一般从对业务影响较大、业务部门频繁使用的界面进行正常流测试开始——对于报表或批处理,则从将现有输出与变更版输出进行对比开始——这是发现规范书对照时常漏掉的“代码实际行为”的最务实手段。
 在第四回中介绍的 Use-Case 同样将测试用例定位为用例描述中最重要的部分。第四回所述的“深度”级别,在实际工作中由特性化测试采集到哪些模式来决定——L1 代表包含所有最小集合流程的测试,L2 包括主要替代流程,L3 包括所有可采集到的模式。在后半部分所述的实测冲刺中,计算为“已推进”的也仅限于通过特性化测试得到验证的部分。

不采取一次性切换,而是部分运行以发现差异
 可以并行运行新旧系统并对比输出,也可从业务的部分领域分阶段切换,或者进行影子运维(将生产数据同时导入变更系统,但不用于生产,仅用于对比)。手段因项目而异,但共同的理念是“将发现差异的时机从发布后提前到开发中”。一次部分运行的范围应基于用例目标来明确(例如“财务负责人能在月初通过新系统发出常规发票”),这样就能自然决策。只要目标以业务语言写出,就能确定影子运维需要对比哪些内容,并让发包方负责人向内部报告。
 特性化测试所能覆盖的仅限“代码的实际行为”。只有通过此部分运行和现场访谈才能发现“人员的实际操作”。当下备受关注的“生成式 AI 进行遗留系统分析”也只能读取到纸面文档和代码。若将其结果当作“现行规范”来推进,第三回所描述的事故模式依旧会重演。发现的差异不是失败,而是令人欣喜的收获。第四回我们将用例列表比作地图,每发现一个差异,就在地图上绘出新的路径,已验证列表也随之扩大。

对于发现的“神秘行为”,统一交由 PO 判断
 在推进验证时,总会出现“无法判断该行为是规范还是缺陷”的情况。如果由开发者私下个别判断,事后会被指责“擅自更改”。应将神秘行为制成列表,并在一开始就达成共识,按照“遵循/修复/舍弃”的规则,由发包方的产品负责人(PO)来做出判断。第三回中曾提到,持有素材的基层人员没有发声机会。神秘行为列表可能是将基层的发现逆向传递给发包方的唯一机制。

     图2:将“神秘行为”交由 PO 决定
图2: 将“神秘行为”整理列表并提交给PO

 需要预先确定以下三点:

  • 由谁来做出判断
  • 在何时之前做出判断
  • 若未收到判断则如何处理

 若未预先确定,验证过程中便会不断积累等待判断的事项。发现问题的责任不予追究,决定如何处理的角色属于发包方——这一分工在任何现场都是共通的。判断记录将成为下一次变更的宝贵资产。

剩余风险将移交给维护运营团队
 第三回和第四回中提到的“最后由谁承担剩余风险”。答案因组织而异,但剩余风险不会随着项目结束而消失,而是移交给在第二回中所述的维护运营团队。因此,这个问题应在一开始就与接手团队一并讨论。

已发现事项的存放处——待办事项列表是开放的

#

 在第二回中,我们看到 Backlog 是一个“假设条目会在之后不断流入的列表”。在变更项目中,这一特性会改变范围管理的含义。

 套用第四回的粒度,用例列表被视为已确定——置于需要繁复手续的层面——几乎是一个封闭的清单(增减由发包方作为对已确定项的变更来判断)。而其下的切片则是一个开放的清单。将繁复手续仅保留在总体用例列表层,切片层保持开放。在上一节的方案中发现的行为,将作为替代流程被静默地追加。“第四回中写到‘切片的增删不作为审批流程的对象(但会留存记录)’”,就是这个意思。即使不需要审批,追加和更替都会保留在待办列表的历史中,后续可以追溯。并且不要将追加变成责备的问题(与第三回中“不要因遗漏而追究责任”一脉相承)。一旦追究责任,发现就会被隐藏,发现流程也将陷入停滞。

 不过,“Backlog 是开放的”对开发团队来说理所当然,但对管理稟议和合同的部门而言并非如此。若未经审批发生追加或更替,从公司的流程角度来看是例外。在此仅以“深化对敏捷的理解”为正论硬推,很可能被对方视为“强加教条”,反而产生阻力。
 将繁复手续仅保留在列表层,切片层保持开放——这种划分线是为过渡期组织量身打造的折衷方案。

预算的处理——为发现事项打开钱包了吗

#

 即使 Backlog 是开放的,如果钱包关闭了,发现也无法被接纳。第四回讨论到从整体框架推算出工作量(本数×规模及深度的初始设置)。这里将讨论如何将工作量转换为金额来构建预算框架,并在开发过程中进行预算分配。由于稟议制度因组织而异,以下内容并非绝对答案,而是供各自组织在讨论时参考的素材。

稟议与合同是不同的场景
 稟议是发包方内部为确保预算框架(上限额度)而进行的流程;合同是发包方与承包方之间就承诺内容、可调事项及支付方式达成协议的流程。合同金额需在预算框架内决定,但不必与框架完全一致。将稟议中提交的明细直接抄录到合同的保证条款中——第三回所划的一道界限,就是指这一区别。以下内容主要讨论稟议方面。

将工作量转换为工期与金额——先进行实测
 将第四回推算出的工作量转换为工期与金额的依据是团队的实测值。实际迁移并验证前2~3个用例,测量团队在一个冲刺(Sprint)内推进多少点数——以用例粒度衡量的速度(Velocity)——。与通常以故事点衡量的 Velocity 不同,此处计数对象是用例,且仅将通过特性化测试并达到指定深度的部分计为“已推进”。如何将深度反映到工作量中——例如将 L1 视为该批量的某个比例——也不在纸上决定,而是在此阶段验证。
 将总点数除以 Velocity 即可得出所需周期,再将周期乘以团队的月薪支出即可得金额(例如:总量120点,Velocity 为每个冲刺6点,则为20个冲刺,若每个冲刺两周,则约10个月)。由于实测存在波动,稟议时提交的预算框架应采用偏高端的数值。此换算由承包方(若为内制则由团队)作为提案提交,发包方据此组建预算框架。若预算框架已先行确定,则可反向计算可涵盖的范围与深度(有关实测结果区间的应用以及如何使用换算后的具体数值,将在后续文章中详细讨论)。
 若稟议截止日期无法等待实测完成,也可使用过去项目的实绩作为初始值。但需将其视为临时值,并预先确认在最初几个冲刺周期内用实测结果进行替换。不以标准工时为前提的原因在于,如果将各功能的工时作为确定值,则其明细将直接流入合同。此处设置的初始值仅用于将总量转换为周期所用的 Velocity。
 此换算以可设置 Velocity 初始值为前提(如果组织既无实测也无过往实绩可类推,则无法成立)。算出的数字是稟议预算框架的上限,并非合同金额或最终支付额。此后预算管理将以从上限中扣除已验证部分的方式进行,可能会出现预算结余。但这并不代表失败。

在保持预算框架不变的前提下,仅改变分配决策方式
 在此先厘清前提。由于无法预见所有需求,无法对整个预算框架进行全面的自底向上估算。然而,这并不意味着毫无可预见之处。针对迁移目标的用例完成至最小集合的部分,如第四回所述,可以从现物中逐一计数并进行自底向上估算。而无法自底向上估算的是超出最小集合的可选替代流程(即第四回的深度 L2、L3),以及在验证过程中发现的未知事项。因此,预算框架应分为两层:可自底向上估算的部分和通过从总额度中扣减进行管理的部分。
 发包方方面仍有一道壁。自底向上式稟议,其预算用途即对应于其详细构成。“用件列表×单价”一旦通过,不仅金额被锁定,就连“用于何处”也固定下来,制度上将无法针对验证中发现的问题灵活调整分配。若每次发现问题都要提出“意外”的追加稟议,本方案就无法运转(这里所指的“意外”追加稟议是不可预见的申请,与先行划定大框架并分阶段使用的稟议不同)。
 要从根本上解决,则需改变预算制度本身。放弃年度预算的管控,转而采用滚动预测与动态资源分配的脱预算经营即是一种典型做法。但对于依赖自底向上式稟议运转的组织来说,一下子切换至此难度很高。因此,作为在现行制度下可迈出的第一步,我们提出三种折衷方案:在保持预算框架不变的前提下,仅改变其内部分配决策方式。

 第一,将预算框架的明细分为 承诺额度 和 发现额度 并写入稟议。承诺额度用于完成所有迁移目标用例至最小集合(第四回的 L1)。发现额度则用于完成超出最小集合的可选替代流程以及应对验证中发现的未知事项,约占总额度的 20%~30%。与传统预备金不同之处在于,将发现额度的使用规则写入稟议——“在验证列表中已达成最小集合的用例,按业务影响大小依次拓展至超出最小集合的替代流程。分配由负责人于定例会议中决定,仅当超出总额或超期时才重新提出稟议。”因其依据可由第四回的数量×规模和前文实测提供,稟议格式可保持不变,只需增加一条将部分决策权限委托给负责人的条款。
 不将预算框架保持为单一整体,主要有以下三点原因:

  • 为应对发现事项的预算不会消耗用于保证业务不中断的最小集合预算。
  • 若将授权范围限定于发现额度,则委托条款更易获批。
  • 当预算不足时,能够区分估算偏低(承诺额度超支)与未知量过多(发现额度消耗)两者;后者属于可预见事件,如第三回所述,不作为责任问题。若发现事项可在发现额度内消化,则无需追加稟议;即便超出,也可从额度消化情况早期察觉。

图3: 在保持框架不变的前提下,改变内部分配

 第二,将稟议分为两阶段,先进行实测。第一阶段为“实测冲刺”,以小额、短期的准委任方式,实际迁移并验证数个用例。第二阶段在实测获得 Velocity 后,再申请主体预算框架。此做法与“先外包需求定义阶段”的既有惯例形式相同,因此在自底向上估算导向的组织中也易获通过。不同之处在于,第一阶段的成果物不是需求定义文档,而是验证列表的最初若干行以及实测得到的 Velocity。前文实测及首条特性化测试都在这一阶段完成。

 第三,年度预算框架保持不动,每季度更新项目的“预算与执行+预测”。根据验证列表的增长和目标完成度来更新预测,并提前说明发现额度的消化预期——若有剩余,也一并给出预期。只需在许多组织已在使用的季度审查模板中,增加一行“迁移目标用例中已验证到何种程度”(对应第四回“用例×深度”表的汇总)即可。剩余预算可在最后决定用于进一步完善替代流程或返还。此种在年度预算框架内持续滚动预测的方式,也相当于在项目层面小规模试验脱预算经营的滚动预测。

 固定的是总额、期限和承诺额度,可变的是发现额度的分配和预测。之所以能实现,依赖于在稟议中写入分配权限委托条款,以及先以小规模实测作为稟议,这两点均无需新制度,仅需在现有表单中添加一行即可。这表明,在改变制度之前,仍可在现行制度框架内进行尝试。仅开放 Backlog 不够,钱包也必须同样开放。

为什么这个问题会被重复了30年

#

 每次变更都会因为“没有文档”而被抱怨,变更结束后文档又再次停止更新。这一连锁反应背后有其原因。在变更的尾声阶段,因推动“可运行”被置于首位,记录决策的精力最先被削减。然后在下一次变更中,上次决策以“为何会出现这种行为”之形式被重新发现,再次从调查开始。第四回中提到,现在人们手动承担的许多工作,正是前次变更中有人默默删除的替代流程痕迹。其真正身份,是未经记录而被删除的决策在三十年间的堆积。

 打破这一连锁反应的第一步,不是“抱怨”,而是在本次项目中留下“验证列表”和“决策记录”。特性化测试是支持验证列表(第四回“用例×深度”表)中各个格子的文档,它以机器可读取的方式持续地将现物行为固定下来。神秘行为的判断列表则是针对“为何会这样”这一问题,首份被书写的答案。第三回开篇写道,决策终将成为习惯,无人再提出质疑。决策列表则将尚未成为习惯的决策,连同理由一并传递给下一代。这两者都比重写一整套设计文档省力,接手下一次变更的人,可以在你当前所处的“全貌不明”状态之外至少向前迈出一步。第四回中留下的“作为验收依据,用何物替代纸质文档”的提问,我的答案也是这两者。不是添写纸张,而是保留从现物中获取的内容。这才是最宝贵的文档。

 在第三回开篇写道,处理变更项目的这三篇文章意在掀起一些波澜。在这三篇中展示的三个视角——观察“现有”的实体、构建可达成共识的整体框架、进行验证并编制预算与分配——仅是一种假设,并非唯一正确解。这既不是将 Scrum 模式原封不动地套用,也是在日本现场的稟议、合同及多层分包的语境中重塑流程的一个示例。“我们现场行不通”“稟议流程没这么简单”“特性化测试的工时谁来付”……正是这些异议,才是这三篇文章希望引发的。欢迎在您的工作场所进行讨论。


 到这里,您是否已经成功从雷区上安全着陆了呢?

图4: 雷区,您成功脱险了吗?

下回预告:“Daily Scrum 已变成进度报告会”

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。