Martin Fowler-Bliki:Yagni(“你不需要它”)

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书活动

目前, 星球 内第2个项目《仿小红书(微服务架构)》正在更新中。第1个项目:全栈前后端分离博客项目已经完结,演示地址:http://116.62.199.48/。采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 276 小节,累计 43w+ 字,讲解图:1917 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 1500+ 小伙伴加入,欢迎点击围观

yagni 最初是一个首字母缩写词,代表“你不需要它”。这是来自极限编程的口头禅,通常在敏捷软件团队中使用。这是一个声明,我们认为我们的软件未来需要的某些功能现在不应该构建,因为“你不会需要它”。

yagni 是一种参考 xp 简单设计实践的方法(来自第一版 白皮书
第二版提到了“增量设计”的相关概念)。与 xp 的许多元素一样,它与 90 年代后期广泛接受的软件工程原则的元素形成鲜明对比。当时大力推动对软件开发进行仔细的前期规划。

假设我正在与米那斯提力斯的一家初创公司合作,为航运业务销售保险。他们的软件系统分为两个主要部分:一个用于定价,一个用于销售。这种依赖性使得他们在相关定价软件完成之前无法有用地构建销售软件。

目前,该团队正在努力更新定价组件,以增加对风暴风险的支持。他们知道,在六个月后,他们还需要支持盗版风险定价。由于他们目前正在开发定价引擎,他们现在考虑构建盗版定价的推定功能,因为这样定价服务将在他们开始开发销售软件之前完成。

yagni 反对这一点,它说,因为你在六个月内不需要盗版定价,所以除非有必要,否则你不应该构建它。因此,如果您认为构建此软件需要两个月的时间,那么您不应该再开始四个月(忽略任何用于进度风险和更新销售组件的缓冲时间)。

yagni 的第一个论点是,虽然我们现在可能认为我们需要这个假定的特征,但我们很可能会错。毕竟敏捷方法的背景是接受我们欢迎不断变化的需求。一个计划驱动的需求大师可能会反驳说这是因为我们没有做好我们的需求分析工作,我们应该投入更多的时间和精力。我反驳说,提前弄清楚你的需求是多么困难和昂贵,但即使你能做到,当刚铎海军消灭海盗时,你仍然会措手不及,从而破坏整个商业模式。

在这种情况下,假定功能有一个明显的成本—— 构建成本 :所有花在分析、编程和测试这个现在无用的功能上的努力。

但想想我们对自己需求的理解是完全正确的,刚铎海军并没有消灭海盗。即使在这种令人满意的情况下,构建假定特征也会产生两个严重的成本。第一个成本是延迟价值的成本。通过在盗版定价软件上花费我们的精力,我们没有构建其他功能。如果我们将精力投入到构建天气风险销售软件中,我们本可以将完整的风暴风险功能投入生产,并在两个月前产生收入。由于推定特征造成的 延迟成本 是风暴保险两个月的收入。

人们构建假定特征的常见原因是因为他们认为现在构建它比以后构建它更便宜。但是这种成本比较必须至少与延迟成本进行比较,最好考虑到你正在构建一个不必要的功能的可能性,你的几率至少是 ⅔。

人们通常不会考虑现在建造和以后建造的比较成本。在这种情况下指导开发人员时,我使用的一种方法是让他们想象他们以后必须进行的任何重构,以便在需要时引入该功能。通常,思想实验足以让他们相信,以后添加它的成本不会高得多。这种想象的另一个结果是添加一些现在很容易做的事情,增加最小的复杂性,但显着降低以后的成本。使用查找表查找错误消息而不是内联文字是一个简单的示例,但可以使以后的翻译更容易支持。

提醒一下,任何从未使用过的扩展点都不仅仅是浪费精力,它也可能会妨碍您 ——jeremy miller

延迟成本是成功的推定特征强加的一种成本,另一种是 携带成本 。假定功能的代码增加了软件的一些复杂性,这种复杂性使得修改和调试该软件变得更加困难,从而增加了其他功能的成本。软件中具有盗版定价功能带来的额外复杂性可能会使构建风暴保险销售组件所需的时间增加几周。这两周有两个方面的影响:构建该功能的额外成本,加上延迟的额外成本,因为它看起来需要更长的时间才能投入生产。从现在到盗版保险软件开始可用之间,我们将承担每项功能的费用。如果我们永远不需要盗版定价软件,我们将承担在删除盗版定价功能(假设我们这样做)之前构建的每个功能的成本,以及删除它的成本。

到目前为止,我将推定特征分为两类:成功的和不成功的。自然地,那里确实存在一个范围,并且在该范围内有一点值得强调:正确的功能构建错误。开发团队总是在学习,包括他们的用户和他们的代码库。他们了解他们正在使用的工具,并且这些工具会定期升级。他们还了解他们的代码如何协同工作。所有这一切意味着您经常意识到六个月前编码的功能并没有按照您现在认为应该完成的方式完成。在这种情况下,您已经积累了技术债务,并且必须考虑该功能的 修复成本 或解决其困难的持续成本。

所以我们最终得到三类推定特征,以及当你忽略它们时发生的四种成本。

我的保险示例讨论了相对用户可见的功能,但同样的论点适用于支持未来灵活性的抽象。在构建风暴风险计算器时,您可以考虑现在进行抽象和参数化,以支持以后的盗版和其他风险。 yagni 说不要这样做,因为您可能不需要其他定价功能,或者如果您目前对需要哪些抽象的想法与您实际需要它们时所学的不相符。这并不意味着放弃所有的抽象,但它确实意味着任何使理解当前需求的代码变得更加困难的抽象都被假定为有罪。

yagni 以较大的特征最为明显,但您更常在小事物上看到它。最近我写了一些代码,可以让我突出显示一行代码的一部分。为此,我允许使用正则表达式指定突出显示的代码。我看到的一个问题是,由于突出显示了整个正则表达式,我无法处理需要正则表达式匹配比我想突出显示的部分更大的部分的情况。我希望我可以通过在正则表达式中使用一个组来解决这个问题,并让我的代码只在存在一个组时突出显示该组。但是我不需要使用比我突出显示的内容更匹配的正则表达式,所以我没有扩展我的突出显示代码来处理这种情况——直到我真正需要它时才会这样做。出于类似的原因,在我真正准备好使用它们之前,我不会添加字段或方法。

像这样的小 yagni 决定在项目规划的雷达下飞行。作为开发人员,很容易花一个小时来添加我们确信很快就会需要的抽象。然而,上述所有论点仍然适用,许多小的 yagni 决策加起来显着降低了代码库的复杂性,同时加快了更迫切需要的功能的交付。

现在我们了解了为什么 yagni 很重要,我们可以深入了解关于 yagni 的常见混淆。 yagni 仅适用于内置于软件中以支持假定功能的功能,不适用于使软件更易于修改的努力。 如果代码易于更改,yagni 只是一种可行的策略,因此在重构上花费精力并不违反 yagni,因为重构使代码更具延展性。类似的推理适用于 selftestingcode 和 continuousdelivery 等实践。这些是进化设计的有利实践,没有它们,yagni 就会从有益的实践变成诅咒。但如果您确实拥有可延展的代码库,那么 yagni 会增强这种灵活性。 yagni 有一个奇怪的特性,即它既受进化设计支持,又使进化设计成为可能。

yagni 不是忽视代码库健康的理由。 yagni 需要(并启用)可延展的代码。

我还争辩说,yagni 仅在您现在引入额外的复杂性时才适用,而您要等到以后才能利用它。如果您为将来的需要做的事情实际上并没有增加软件的复杂性,那么就没有理由调用 yagni。

综上所述,有时应用 yagni 确实会导致问题,并且您面临着昂贵的更改,而早期的更改会便宜得多。这里棘手的是,这些情况很难提前发现,而且比 yagni 省力的情况更容易记住。我的感觉是 yagni 失败相对较少,当 yagni 成功时,它们的成本很容易被抵消。