如果你曾在一家高速增长的软件工程公司待过,你可能会听过类似这样的一段对话,是关于技术债务的:
或者是类似这样的评论:
对于这种情况,我一直持观望态度。当我站在李四的立场来看,解决问题几乎总是合乎逻辑的,但并不总是能得到所需的支持!
那么,问题究竟在哪儿呢?为什么偿还技术债务明明是一个很明显且合理的举措,但是却很难说服大家来做呢?
“技术债务” (Technical Debt 或 Tech Debt),这一古老的流行语通常被当成一个包罗万象的术语,用来描述我们希望我们所构建或正在开发软件更好的东西。由于在开发早期阶段所做的一些捷径或权衡,软件通常并不会达到预期的状态。
凡是被贴上技术债务标签的东西,几乎都是软件的非功能性更改。比如,重构代码来消除全局代码,从而支持依赖项输入、修复(或添加)测试、改进文档、甚至让测试套件在 CI 中运行,或者最终实现部署的自动化之类的事情。
因为这些东西都不是功能性的改变,也不会改变软件的行为,所以对客户来说,这些改进他们很可能看不到。因此,如果客户并不会看到你在这个技术债务上所耗费的时间以及软件的任何变化,那你提交的价值又在哪儿呢?又为什么要在技术债务上投入时间呢?
向客户阐述解决技术债务的商业价值,是获得解决债务所需时间的关键。
通常来说,人都会抱有一种固定思维模式( “我们也想解决这个问题啊,但我们没有得到允许去解决这个问题呀” )。记住,在任何组织中,人们都会作出决策,决策是关于权衡的,是可以受到影响、被推翻或改变。更重要的是,组织也在不断变化,昨天做出的决策方式,到明天可能就行不通了!
如果说沟通价值是关键,那么技术债务的偿还价值应该如何沟通呢?对于这一问题,没有一个放之四海而皆准的答案:但我写下本文的初衷是勾勒出这么一个框架,帮助产品团队或工程团队对问题进行分类、时间分配,并明确为什么每段时间都应根据它将提供的价值进行适当的调整。
我希望本文勾勒出的框架能够帮助你与利益相关者保持一致,让你能够更有效地沟通价值,并让你最终解决你一直想解决的技术债务问题。
当我们遇到技术债务问题时,要先问问自己:这里真的有问题吗?很可能不值得付出,除非:
它提高了产出的速度或质量。例如,重做这个构建配置将使我们的交付时间缩短一半,使部署频率增加一倍。
延误导致高昂的代价,造成工期紧迫。例如,如果我们现在不解决这个问题的话,将直接影响客户、利润或者是收入。
如果它不是我上面说的这两种情况中的一种,最好是直接忽略它。
在组织的每个计划阶段,无论你的计划节奏或持续时间是怎样的,都应该能够将所有工作进行分类或分配到以下四个方面之一:
功能改进:提供客户看得到或用得到的功能改变。通常情况下,产品经理 / 所有者会根据研究和客户的意见,对如何优先考虑这方面的优先级进行深入研究。示例:新颖的新功能。
计划工作:更大范围工作的一部分,是在团队之外 / 组织内部进行的,通常由项目经理协调,将你的团队列为该工作计划的依赖者。示例:全平台本地化 / 国际化工作。
交付能力:以可量化的方式提高交付速度或产出质量。示例:将部署过程自动化,以便更快 / 更频繁地将代码从主干 / 主分支投入到生产环境中。
计划外工作:当延误会导致高昂的代价,且造成工期紧迫时,你应优先考虑这项工作,并立即解决问题,或者是与其他所有正在进行的事情一起解决,而不是将它们推迟到你的积压工作中或未来的周期,包括 bug、客户支持请求、生产事故等。示例:影响 20% 的客户的生产停工。
通过将工作归类到这四个方面中,沟通工作的价值应该会变得更容易,并且腾出时间来处理技术债务问题也会变得更容易。
请注意,上述分类应该适用于整个积压工作,而不仅仅是技术债务问题,因为将时间分配给技术债务问题就意味着不能将时间分配给其他工作了。还要注意的是,技术债务可能会被归类为交付能力或计划外工作。
前面我提到的 “在每个阶段” 是有原因的:能够在组织层次之间用一致的词汇进行沟通,有助于影响解决技术债务问题的投资决策。
许多科技公司采用的模式是,每个小组、团队或 “双披萨” 团队往往负责管理受组织层次影响的积压工作。如果你的组织是这样运作的,那么让决策层了解这一框架将有助于你在术语、逻辑和论证上保持一致。
与任何模型或框架一样,也存在不适合其结构的极端情况。乍一看,你可能无法忽略某个问题,但又不能将其分类为交付能力或计划外工作。请不要在第一眼看到问题时就放弃:督促自己找到一种方法,通过稍微深入地研究问题的细节(或与同行、中小企业进行合作),将这些问题合理地归结为其中之一。如果在第二次尝试后仍然不适合,那也没有关系。
“这个代码很难测试。” —— 这是那些令人感到棘手的场景中的一个例子,在这种场景下,可能很难直接忽略它,或者说你不清楚如何对它进行分类。遇到这种情况,我建议采用以下方法:
对你的代码做一些小规模的、渐进式的改进,不要试图花时间来解决 “大爆炸式” 项目。
每个拉取请求(Pull Request)都应该改进你的代码、改进你的测试,并提高测试覆盖率(或者至少不能退步!)。CI 系统中的质量检验甚至可以强制执行一些规则,例如不允许测试覆盖率下降超过 X%,并且要求最低覆盖率为 Y%。
如果这看起来不是一个易于处理的方法,那么你可能需要找到一种方法来重新定义问题,以便将问题分解成更小的 “块” ,也许其中一些部分可以被忽略,一些部分可以被分类,还有一些部分可以用上面提到的即用即付(PAYG)方法来处理。
一旦你将工作分类到四个方面之一之后,下一步就是 “切馅饼” 或者为每个方面分配估计的时间。
这四个方面的时间分配如下:
40% 时间用于功能改进;
10% 时间用于计划工作;
20% 时间用于交付能力;
30% 时间用于计划外工作。
下面是我成功使用过的一个方法:从确定数字开始,与利益相关者一起审查,然后在适当的地方进行调整。
如果没有利益相关的参与,就无法成功确定时间分配比例,这主要是因为这些比例通常是可以协商的,但也涉及许多决策,因此需要进行权衡。
你应该按什么顺序来确定数字?
第一个要确定数字的是 计划外工作;这不应该是你自己决定的事情,而应该以你所掌握的数据为基础,这些数据可以让你能够推断出对未来一段时间的预期。例如,如果你在做季度计划,你可能会看前一个季度所花费的时间,以及(特别是受季节性影响的企业)上一年同一季度所花费的时间。如果利益相关者试图就这一数字进行协商,请向他们介绍你是如何确定这一数字的,并帮助他们了解你的实际情况。
要确定的第二个数字是 交付能力。为什么呢?基于上述框架,我们希望从一个有目标、但有基础的数字开始。每个人都应该有减少计划外工作的动力,而交付能力就是实现这一目标的重点。如果你的计划外工作 “太大” (每个企业的容忍度不同),请考虑增加你的交付能力时间分配,直到该数字变得可接受为止。如果你下调了这一数字,请确保已与利益相关者就此沟通并进行权衡。
建议:Google SRE 一书中关于 “拥抱风险” 主题在识别企业内部的风险承受能力方面有一些很好的想法,这可能有助于确定分配多少时间给交付能力这方面。虽然对某些团队或组织来说,使用错误预算可能是一个非常有效的选择,但同意一个管理错误预算和实施或测量 SLO 的系统本身往往是一个挑战。我在本文中介绍的框架时间分配或许有些重叠,但在某些方面可能提供了一种分配时间的替代路径,以解决一些质量或可靠性问题,否则这些问题将使用错误预算来解决。
要确定的第三个数字是 计划工作。这些要求通常来自其他团队或业务部门,有的要求比较好商量。试着根据你认为接下来的时间必须要做的事情来确定这方面的时间分配。
要确定的最后一个数字是 功能改进。此时,你已经确定了分配时间,因为你这时候处理的是剩余的百分比。你可能听说过 “价值流” 这个术语,团队的活动应该以满足客户的要求为目标。这方面通常是最主要的,它直接解决的是你的客户想 “看到” 的东西。如果这方面的时间分配对于你认为需要交付的价值而言过小,以至于不能满足你的客户在新功能交付方面的期望,我们将很快为这一方面提供更多的时间分配。
一旦完成每个方面的初始时间分配,你将需要考虑如何根据与利益相关者的讨论对此进行调整,在这个框架内有两个 “杠杆” 可供考虑:
交付能力工作应该减少计划外工作。
计划工作影响功能改进工作。
理想情况下,任何分类为交付能力的工作都会减少下一个计划周期或工作节奏的计划外工作量。
建议:请阅读 Nicole Forsgren 博士、Jez Humble 和 Gene Kim 所著的《加速》(Accelerate)一书——这是一本关于衡量和思考如何提高软件交付能力的优秀指南,里面有关于软件交付能力的优秀定义,以及 4 个关键指标,即交付周期、部署频率、平均恢复时间(Mean Time To Restore,MTTR)和变更失败率。
交付能力可能会在两方面影响计划外工作:
交付能力工作应降低平均恢复时间。即使计划外工作方面中的 bug 或问题的数量没有减少,解决它们所需的时间也应该减少,从而减少分配给计划外工作方面所需的总时间。
交付能力工作应提高产出质量,从而降低变更失败率。理想情况下,我们希望看到导致计划外工作方面的问题能随着时间的推移而减少,而提高产出质量(在正确性、可靠性、性能、安全性等方面)恰恰可以做到这一点。根据具体情况,有时优先考虑平均恢复时间,而不是降低变更失败率的做法是有意义的,例如,如果恢复能力需要几天的时间,而故障的数量很少,但却保持不变,那么在选择减少平均恢复时间并更快地解决这些未来问题之前,你可能会为客户提供更多的价值,因为停机或 bug 的持续时间可能比数量的影响更大。我们的目的是利用这两个变量,但要根据客户的价值来构建决策过程。
我认为,计划工作和功能改进之间的平衡是一种分配杠杆。通过与计划的利益相关者合作,有时你可以推迟或重新分配你的团队被要求做的工作 —— 只要这个决定的理由清楚地表达出来,并且这些利益相关者也同意进行出这样的权衡。
计划通常是由多个项目、团队和依赖项组成的庞大而复杂的工作。重要是要保持平衡,所以你要预料到 “阻击者”,避免成为其他团队的 “阻击者”。项目经理或高层很容易要求你的团队提前完成相关工作,但从你的角度来看,将工作推迟一周 / 月 / 季度可能意味着你分配时间改变的方式,以及最终你的团队能够为客户带来的价值存在着巨大的差异。
考虑到计划外工作在某种程度上是固定的,而降低计划外工作的杠杆很大程度上就是交付性能 —— 当你试图找到更多时间分配给功能改进时,我建议与计划利益相关者合作,允许你减少对计划工作的分配时间。你应该能够做到这一点,通过解释你时间分配的原因,以及为什么你认为对你的客户来说,推迟他们的请求是最好的事情。
由于这个杠杆的结构方式,他们所做的权衡实际上是将价值传递给特定于你产品的客户,而不是致力于进行更大的创新。
一旦你的团队完成了将待办事项分类到这四个方面中的一个,并为每个方面分配了一定比例的工作时间,你如何才能真正执行呢?
每个团队都是不同的:团队规模、经验水平、任期 / 专业知识等因素都会影响到谁负责什么工作的决定。
然而,在分配给团队的时间时,有一些事情是你应该考虑的,那就是个人之间的时间分配:
团队中每个人都应该有为客户提供价值的能力。
团队中的每个人都应该拥有产出的质量。这包括正确性、可靠性、性能、安全性等。
让中小企业继续专注于一个领域只会掩盖或延缓风险和成本:例如,如果团队中有人不在 / 生病 / 不能上岗,则会造成业务连续性的缺乏现象;当他们辞职时,可能会造成的知识转移成本等等。
有时候,你可能会为了让事情快速完成,而权衡将风险推迟,这不是不可以,但你应该确保团队或利益相关者能够意识到这一点,并就这个决定保持一致。
如果你确实考虑采用本文提到的这个框架,我强烈建议你在开始之前考虑一下如何衡量自己的具体情况。比如,如果你使用的是像 Jira 这样的问题跟踪器,那么可以考虑添加一个字段或标签,以便使你能够将问题分配到四个方面中的一个。
在你的下一个周期或冲刺阶段之前,你应该能够快速报告你的预期分解是什么,在这个周期结束之后,你应该能够分析你的表现如何。这里经常被问到的一个问题是,是否要跟踪问题的总数,或者估算时间、工作量等等的总和?这实际上要取决于你团队的工作方式,以及你认为什么样的努力会带来更好的回报。不要追求一个完美的系统,只需选择一个你认为能够提供足够实用的东西即可,让你知道自己是如何跟踪的。
标记并跟踪你在每个方面的进展情况,这一做法对计划外工作特别有用,你可以以每周期一次更有规律的节奏进行观察,是否对周期产生了比预期更大或更小的影响。
组织、团队或积压工作都会发生变化,因此你的计划和时间分配的方法也应该有所变化。使用持续改进的思维方式,让衡量或数据驱动的决策成为你工作方式的一部分,类似计划或冲刺回顾之类的惯例,将会帮助你更好地改进时间分配。
Ryan D,专注 DevOps、安全、Go 语言、Linux、Docker、Kubernetes 等。曾任 Cloudflare 的 DevOps 工程经理。
参考阅读:
https://medium.com/humans-of-xero/technical-debt-f5158cc9ca07
InfoQ 读者交流群上线啦!各位小伙伴可以扫描下方二维码,添加 InfoQ 小助手,回复关键字“进群”申请入群。大家可以和 InfoQ 读者一起畅所欲言,和编辑们零距离接触,超值的技术礼包等你领取,还有超值活动等你参加,快来加入我们吧!
点个在看少个 bug 👇