如何确保你永远无法发布

Lobsters Hottest 新闻

摘要

这篇博客文章讽刺地概述了如何结合常见的软件开发实践,如微服务和严格审查,造成多重的协调障碍,阻碍产品发布效率。

<p><a href="https://lobste.rs/s/izlxli/how_guarantee_you_never_ship">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/03 14:04

# 如何确保你永远无法交付 来源:https://kore-nordmann.de/blog/how_to_guarantee_you_never_ship.html ## 如何确保你永远无法交付 这是我一系列博客文章中的一篇,我将在其中反思我在融资、发展(以及出售)SaaS产品公司(https://kore-nordmann.de/companies/)过程中所学到的东西。虽然我写下这些是为了自己,因为我相信自我反思有助于学习,但我也希望与任何可能感兴趣的人分享我的想法。所有相关主题的概述(https://kore-nordmann.de/blog/how_to_guarantee_you_never_ship.html#related-topics)可以在下方找到。我有一堆这样的博文准备发布,所以如果你喜欢这篇,也许也想关注我(https://kore-nordmann.de/blog/how_to_guarantee_you_never_ship.html#follow-blog)。 在一系列文章中,我逐一反对了一些做法:将代码拆分到多个仓库、长期存在的特性分支、通过微服务来建模分离、专门的质量保证团队、为每次更改设定一个固定的测试覆盖率目标,以及将拉取请求评审作为你的质量关卡。每一项本身都是个问题。这篇博文是关于同时采纳所有这些做法的——这比应有的情况更常见,因为每一项都披着最佳实践的徽章。 因此,让我写下与本系列其他内容相反的观点。如果你的目标是建立一个从不交付任何东西的组织,而每一个单独的决定在它被做出的会议中看起来仍然很负责,那么你会这样做: ### 配方 首先,将你的系统拆分到多个仓库中,一个服务或团队一个,以实现清晰的权责归属。然后让工作在长期存在的特性分支上进行,因为这是构建严肃特性的方法。确保这些分支同时跨越多个仓库,这样一个特性永远不会被包含在一个地方。将系统分割成尽可能多的微服务,最好是小的,这样每次交互都要跨网络。要求每个仓库在合并前都必须有一个评审过的拉取请求,并以90%的测试覆盖率作为每个拉取请求的门槛,这样任何东西在被覆盖之前都无法合并。最后,放置一个专门的质量保证阶段,来捕捉所有这些漏过的东西。 每一步单独来看都是合理的。合在一起,它们就成了一台交付的完美解药。 ### 协调边界是相乘,而非相加 这部分使得它致命,而不仅仅是烦人。每一个选择都增加了一个协调边界,而协调边界不是相加,它们是相乘。 看一个小改动:你向一个被系统多个部分共享的概念添加一个字段。看看它的传播路径。它触及四个仓库,因此是四个拉取请求,而不是一个,每个都由只关注自己那部分的人评审,每个合并的顺序都很重要。它们每一个都必须单独通过覆盖率门槛,因此一个单字段的更改需要在合并之前在四个地方编写围绕它的测试。这些仓库都在长期存在的分支上,因此每个拉取请求还必须经受与数周的代码漂移进行合并的考验。这些部分通过网络通信,因此该字段必须在每个它跨越的边界两侧都添加,而这两边是分开部署的,因此在一段时间内,生产环境运行的是一个混合版本,在某些地方有该字段,在另一些地方没有。现在质量保证团队拿到了结果并被要求测试它,第一个问题就是诚实的:到底部署了哪些版本的组合,我们到底在测试什么? 没人能回答这个问题,因为你添加的所有轴的组合数是它们的乘积,而不是和。四个仓库乘以几个进行中的分支乘以一打服务乘以每个仓库的评审队列乘以每个拉取请求的覆盖率门槛,将交付一个连贯变更的成本乘了起来。 而这还只是机械部分。该字段还需要一个名字,而它所跨越的每一个部分的人,都基于对该部分的隐性知识和自己的命名偏好而持有意见。放任不管,他们不会趋同:同一个概念在一个仓库里最终叫 customerType,在下一个仓库叫 accountCategory,在第三个仓库则是一个具有不同成员的枚举。因此这四个人必须沟通,而且如果该字段出现在客户可见的地方,首席技术官(CTO)和首席产品官(CPO)对它叫什么也有利害关系。 这些都不是浪费。在一次结账中,这些相同的人仍然会在拉取请求中或站在白板前进行同样的讨论,并且它会收敛,因为答案只落在一个地方。跨四个仓库的讨论变成了四个队列中的合并前提条件:在它解决之前,什么都动不了,它解决的速度取决于涉及的最慢的日历,而任何人第一次提交的名字现在成了其他三个仓库必须修改去匹配的名字。那个会议室里的人不是问题。问题是一行代码在等待会议室。 你不是减慢了交付速度。你是设计它使之变得不可能,并将每一步称为改进。 ### 为什么它会发生 令人不安的是,没有人坐下来刻意构建这个。1944年有人这么做了:美国战略情报局(中央情报局的战时前身)为被占领的欧洲编写了一份破坏手册(https://www.gutenberg.org/files/26184/26184-h/26184-h.htm),其中关于组织的章节读起来就像一份流程文件。坚持一切通过正式渠道。将每件事都提交给一个委员会,并使委员会尽可能大。为精确的措辞争论不休。主张谨慎。确保任何需要一个人批准的事情必须由三个人批准。那与一条没人能通过的交付流水线之间的区别,只在于意图,仅仅是意图。 我们的方法是逐渐积累的。每一层都是由一个合理的人,用一种他们读到是正确的实践,解决一个真实的、局部的问题而添加的。独立的仓库以实现清晰权责。分支以实现隔离。服务以实现可扩展性。评审以保证质量。一个覆盖率数字以使质量可衡量。一个质量保证阶段以保证安全。失败之处在于,没人关注它们的乘积:每一项在它自身的条件下都是合理的,而交互效应没有所有者。 而且一旦它到位,没人能完整地看到一个变更。那个为了清晰而拆分的系统,现在成了没人能在脑海中或一次检出中完全把握的系统。 ### 出路在于做减法 本系列的每一篇文章,从不同角度,实际上都是在论述同一个观点,现在是时候直说了。原则是:最小化协调面,并确保你添加的每一条边界都能物有所值。 这就是每一条建议的精髓。单个仓库(https://kore-nordmann.de/blog/always_go_with_a_monorepo.html)移除了跨仓库轴。基于主干的开发与特性开关(https://kore-nordmann.de/blog/feature_flags_over_branches.html)移除了长期存在分支轴。进程内的边界代替条件反射的微服务(https://kore-nordmann.de/blog/hexagonal_architecture_vs_microservices.html)移除了网络轴。由团队负责的质量(https://kore-nordmann.de/blog/dont_do_quality_assurance.html)移除了向QA移交的轴。同步评审(https://kore-nordmann.de/blog/dont_do_code_reviews.html)移除了拉取请求队列轴。覆盖率由阶段决定(https://kore-nordmann.de/blog/tests_and_product_market_fit.html)移除了那个无论代码下个月是否还存在,都对每次变更征税的门槛。这些都不是生产力技巧。每一项都删除了一个乘数。 边界不是免费的,有时一条边界能物有所值:一个必须独立扩展的服务,一个你真正向陌生人发布的代码的仓库,一个你不得不支持的客户的特性开关。保留这些。纪律在于拒绝每一条不能物有所值的边界,因为那些不能的并非中立。它们会与所有其他边界相乘。 ### 总结 可怕之处不在于这些实践中的任何单项。而在于其复利效应。同时采用多仓库、长期存在的跨仓库分支、条件反射的微服务、专门的质量保证团队、一个覆盖全部的覆盖率目标以及按仓库的拉取请求评审,你就建立了一个仅凭体面的决策,却无法交付一个连贯变更的组织。修复方法不是在上面叠加一个更好的流程。而是做减法:计算一个单一变更必须跨越多少协调面,然后开始移除那些没有挣得自己位置的。

相似文章

@danshipper: Cosign

X AI KOLs Timeline

作者认为,工作角色应简化为单一的“发货人”角色,该角色负责业务决策与提示词工程,而技术细节则由其他团队单独处理。

代码审查需要认真阅读代码

Lobsters Hottest

一篇开发者博客文章反对在不阅读 AI 生成代码的情况下直接将其部署到生产环境,强调代码审查具有至关重要的作用:分散责任、降低巴士因子风险,以及让团队成员保持对代码库的了解。