软件加速与失同步:为什么「写代码变快了」却不一定更快

软件加速反而让认知瓶颈转移,代码评审被误认为拖慢AI开发流程的元凶,问题或许只是被挪了位置。

中文
复制
Software Acceleration and Desynchronization 的题图

软件加速与失同步

一个多月前,我在社交媒体上发过这样一段话:

看到越来越多报告和业内人士把代码评审说成是拖慢 AI 快速开发流程的元凶。不清楚有没有人在问:这是不是只是把「搞清楚到底发生了什么」这个认知瓶颈挪了个位置。「给评审也加上 AI」似乎就是他们想要的终点。

我收到了不少回复,有人说「这太糟糕了」,也有人说「其实这主意不坏」。当时我还没有合适的概念把这些想法讲清楚,后来才找到一种更清晰、更以系统为中心的表述方式。这篇文章显然是由围绕 AI(尤其是 LLM)的讨论引发的,但它更像是一个结构性论证,谈的是这类技术的采用会触发哪一类变化,以及业界此前在其他技术和流程上已经出现过的更广泛的加速模式。因此,下文我不再具体提它们。

我在这里提出的模型,灵感来自(或者说是对它的危险误用式简化)Hartmut Rosa 在《社会加速》中提出的模型,1 被我掰弯了来贴合自己的观察。我先从一个模式讲起:循环,或者说周期。

循环、周期与活动

先看一个围绕写软件这件事的线性表示:

一个有向图,包含:规划工作 → 写代码 → 代码评审 → 部署

这是一种简化,因为还可以拆得更细,比如 DORA 报告里价值流映射可能长这样:2

DORA 报告 2025 图 50:价值流映射,包含一系列元素,如 backlog、分析代码库、编码、生成单元测试、安全分析、提交代码、生产环境等。提交代码与生产环境之间的间隔被展开,显示创建合并、代码评审、构建、部署到 QA、部署缺陷修复等子步骤。

不过,我们也可以画得不那么线性,以展示另一种复杂性,哪怕步骤已经做了简化:

一个有向图,包含:Plan work → scope ticket → write tests → write code → self-review → code review (peers) → merge → ship;plan work 与 merge 之间的每一步也都连回多个更早的步骤,表示推倒重来。另有箭头短路连接 Plan work → write code → self-review → merge → ship(以及 self-review → ship)。随后序列从 ship 绕回 plan work,表示选定新任务。

上面每一步都可能意味着向后跳回更早的任务,而紧急情况则可能意味着向前跳跃。就论证而言,这个模型是否足够细致、还是只是个粗略估计并不重要;我们可以做得更精确或更粗糙(“write tests”这个节点轻松就能扩成一本书),这里主要是为了说明问题。

总的来说,在所有版本中,任务的目标都是以可接受的质量,尽可能快地从起点走到终点。抱着加速开发的心态,我们既可以逐个节点去看(写代码、调试或审查代码)有哪些环节可以提速,也可以从整体工作流入手,去影响那些循环本身。

比如,代码审查可以通过自动格式化和 linting 来提速——用自动化规则检查来强制规范或阻止某些做法——这些原本得靠人工完成。这既省时间,也让人能专注于更高层面的审查内容。而把这些自动化规则搬进开发环境,还能让整个循环更快,从而收紧反馈回路:边写边修,而不是把缺陷一层层堆上去,事后再花时间拆开来修地基。

并发的循环

到目前为止一切顺利。但问题在于,仅靠这一个孤立的循环,不足以准确描述大多数软件工作。不仅有多个人并行推进多个任务,每个任务各自构成独立的循环,每个人自己也同时身处多个循环之中。比如,你在处理某个代码库的工单时,可能还得为即将开始的项目写设计文档、支援一个支持工单、参加会议、通过导师辅导来发展自己的职业,还要靠发布和阅读状态报告来跟上组织的各项动作,等等。

下面是一些相关但经过简化的循环,作为直观的辅助:

这里展示了四个互不相连的循环:编码、协助支持、职业发展、撰写设计文档。这些图相当复杂,长度也各不相同。编码循环有 8 个节点;协助支持有 5 个节点,并带有一条分支路径;撰写设计文档有 10 个节点,构成一个循环;职业发展也有 10 个节点,但充满了环和引用(通过颜色和标签体现),它把编码循环和设计文档循环当作子任务来引用。

再说一遍,你团队里的每个人在工作日中可能并行运行多个这类循环,类型各异。

但不止如此,还可能有多个循环_共享_某些环节。你可以想象,在系统的某一部分写代码,会如何让你为多种任务做好准备,或提升你在这些任务上的贡献:编写与它交互的代码、修改它、审查变更、撰写或审阅文档、对事故可能出现的边界情况保持敏感、更准确地估算未来任务,等等。

你也可以想象,在规划如何为新的产品变更最好地组织代码时,对代码当前结构的经验可能很重要,同时对即将到来的产品野心有所了解也很重要。同样,熟悉当前系统运维挑战的人所提供的意见,在排定变更优先级时也会很有用。这种共享的关注点集合,催生了 DevOps 之类的理念,其背后的信念是:良好的反馈和集成(而不是把东西甩过墙去)有助于软件交付。

基本上,一组循环可以选择性地为同一组共享活动做出贡献,但有些活动也可以反过来为多个循环做出贡献,而这些循环可能处在不同的时间尺度上:

一个编码循环、一个职业成长循环和一个高层架构循环被描绘为并发的。然而,这三个循环共享一个步骤。对编码循环来说,这是代码审查;对成长循环来说,它关乎对团队工作的了解和规范的执行;对架构循环来说,它代表对变更的感知。

在这里,代码审查可能是编码循环得以直接接受审查的环节,但它同时也是个人职业成长计划得以施展的场所——试图影响或推行某些规范,也是关注长期架构与成长模式的人建立对持续技术变化之认知的地方。

这些共享环节既可能成为瓶颈,也可能抵消速度提升。打个比方:假设你每天骑车上下班,单程 30 分钟,若改乘公共交通或私家车把速度提高一倍,每周就能省下 2 小时 30 分钟;但如果你考虑到自己可能仍需锻炼以维持体能,那么这部分时间并不算真正“省下”。你要么得把省下的大量时间花在通勤之外的锻炼上,要么就会在不知不觉中用通勤时间换取了长期的健康因素。

放到软件上,我们可能会看到这样一种模式:“我们现在能更快地写代码了,但代码审查成了新的瓶颈。”显而易见的做法是设法加快代码审查,以匹配代码编写速度的提升。在某种程度上,代码审查的_部分环节_确实可以优化。也许通过改进,我们能更可靠、更迅速地检测出某些类型的错误。同样,就像 linting 或类型检查一样,这些理想情况下应该前移到开发阶段,而不是留到审查时。

但代码审查不只是找错误。它还用于讨论可维护性、运维方面的顾虑,传播知识与认知,获取外部视角,或培养更广泛的主人翁意识。这些目的即便可以被自动化或加速,也都表明可能存在其他人们无论如何都得维持的循环。

同步与去同步

如果我们决定优化部分工作,只要做到以下之一,就有望获得可观的速度提升:

  1. 持续深入地理解一项任务可能具有的多种目的,从而做出有用且整合良好的改动
  2. 通过解耦循环,放弃其中一些共享目的

第一个方案难度不小,往往需要调研、反复迭代,还要对工效有敏锐的判断。否则很快就会陷入“跑得更快、速度照旧”的困境:工具和方法都换了,真正卡住我们的瓶颈却基本没变。要把这件事做对,意味着你在动手提速时就得清楚,这些改动会从结构上影响工作方式,并且准备好接住随之而来的扰动。

第二个方案很容易在不经意间拖慢甚至破坏其他循环——如果那些用途依然存在,新的活动就得顶替旧的活动——而这又可能反过来作用于原来的循环(比如代码评审可能挡住当下的编码,但也在为日后的编码提供输入),结果是两边都被削弱,或者一旦解耦就各走各的节奏。后一种效应我们称之为“失同步”。失同步的一个风险是,某个循环里有用的、甚至关键的反馈,再也传不到另一个循环。

为了应对这一点(而非彻底杜绝),我们在优化层面还有第三个方案:

  1. 提前采用“规范”或做法,保证各方对齐,减少对同步的依赖。

“最佳实践”和平台想提供的差不多就是这个:一套标准,照着做就能少一些沟通和意义协商。它们通常能提供一个稳定的基础,让多项活动得以加速。但它们并不能完全防止失同步,只是把它往后推。

为了说明失同步,我们来看几个可能相互反馈的循环:

图中展示了5层循环:平台更新,处于长周期;反复进行的编码循环,处于短周期;与编码循环交互的响应式运维循环;零散的配置与优化任务;以及一个关于规范的长循环。编码循环与运维循环在代码评审的同步点上交互,除此之外是异步的。运行软件得到的经验会反馈到平台循环和规范循环,最终再影响编码循环。

这些展示了循环在评审时同步的共享点,横跨运维循环和编码循环。运维工作中的经验可以回流到平台循环和规范循环,而带有运维输入的代码评审正是这些经验被“强制执行”的场合之一。3 如果去掉这些同步点,你可以跑得更快,但各个循环也可能各自独立运转一段时间,并且彼此越离越远:

图中展示了 5 层循环:平台更新,处于长周期;反复进行的编码循环,处于短周期;与编码循环交互的响应式运维循环;零散的配置与优化任务;以及一个关于规范的长循环。编码循环和运维循环不再保有代码评审的同步点,完全异步。运行软件所得的经验会回流到平台循环和规范循环,但不再能有效反馈给编码循环,运维循环不得不重复任务来强制执行规范。

两张图之间差别不大,但我想在这里指出的是:缺少开发阶段的运维输入(在代码评审期间),可能会导致在代码逐步上线的过程中,需要携带并应用一批批重复的在途修复,中间还夹杂着额外的步骤。当底层平台或共享组件发生变更时,它们的推广同步可能会滞后,因为传播这些变更的机会减少了。如果开发速度提升得足够快,而展示代码是否合格的能力 没有 相应提升(即无法在不等待更长的生产环境时间的情况下做到),那么出现意外情况的可能性就会上升。

请记住,这只是两个高层循环之间围绕一项共享任务的一种同步方式。真实工作中有更多循环、更多节点、更多连接,以及团队和角色内部及之间许多更细微的同步点。真实的循环可能更稳健,但也更难以预测。一个有多个同步点的循环可以去掉其中一些,看起来变快了,直到剩下的少数同步点要么变慢(为了追赶),要么被取消(为了跑得快)。

同步点对不同的参与者来说,收获也不一样。比如,一个工程师从中拿到了许可(和保护),另一个人获得了感知,某个团队借此强化了合规,而管理层则宣称自己对这件事负有责任。

很容易想象出光谱的两端:一端是那些被同步步骤拖住、只为避免一切意外的组织;另一端是那些缠进并发规范之网、同一套东西的历代版本从未废弃、全部同时维护的组织,因为根本没有任何同步工作发生。

跨循环累积的漂移会制造不一致:心智模型滞后,为了跟上变化和压力被迫抄近路,我们以为发生的事和实际发生的事之间差距越拉越大。⁴它把子系统扯开,削弱它们,并促成事故——也就是意外的快速重新同步点。

我把事故看作快速重新同步点,因为事故通常正是你失同步到极点的地方:事故响应迫使你暂停惯常的结构,迅速重排优先级,掀翻路线图,并且(理想情况下)让多个团队的许多人突然更新他们对事情如何运转、如何崩坏的理解。惯常的孤岛无法照常运转,这一点本身就说明:失同步过度之后,只能强制修复。

正如 Rosa 在书中所指出的,这种加速往往比底层稳定系统所能支撑的更快,于是这些系统本身成了阻碍。当基础设施和制度所支撑的系统逐渐感到停滞或受其约束、并开始寻找替代方案时,它们就被抛弃或拆除:

[加速]借助制度性的暂停和对背景条件的保障性维护,是现代加速史的一条基本原则,也是其成功的一个根本原因。[制度]本身被豁免于变化,因而有助于创造可靠的预期、稳定的规划和可预测性。[……]只有在这样稳定的预期视域背景下,长期规划和投资才变得理性,而它们对众多现代化进程不可或缺。进一步的、可以说是“无边界”的加速导致这些制度和取向被侵蚀[……],这可能削弱它们自身的前提以及整个晚期现代社会的稳定性,从而使现代性(加速)方案陷入比反现代的减速运动更大的危险。

对减少同步的需求,并不意味着同步本身可以不再发生。跑步机从不会慢下来,系统中的参与者必须展现出韧性,重塑实践与规范以应对要求。当新的节奏带来新的挑战时,这一点尤为明显:把我们带到这里的做法,不足以支撑我们继续走下去,我们又得大改一批循环。

这个观察里有一点很有意思:某个地方的减速,可以战略性地让其他地方加速。

这种特定的慢,是好的慢还是坏的慢?

在我看来,几乎毫无疑问的是,走完一圈“写代码”循环,要比走完一圈“承受自己架构带来的后果”循环快得多——后者通常要跨越多个开发周期才能得到足够的反馈。你可以每小时发布一次代码,但所有边角情况暴露出来,轻易就要花上好几周。

当我们处在系统设计或软件架构的层面(“我们需要一套能容忍区域故障的复式记账”),往往需要理解系统的过去、对其现状与局限有像样的把握,并有能力预判未来的挑战,以此决定把变更推向哪个方向。这与日常变更(“这个功能需要在账本里加一笔事务”)是两种不同的循环,尽管两者相互关联。

这意味着,如果你面对的是一个没有历史的新代码库,而它的未来也许根本不存在(比如短期原型或实验),你很可能可以拥有彼此隔离的短循环。如果你面对的是一个有数千用户、多年积累的模式与边角情况、还有一整套组织文化要去对抗或对齐的大型平台,那你最终就得依靠长循环来为短循环提供依据。

循环之间的连接会随时间逐渐累积,而热爱短循环的人会对自己开始变得如此之慢感到非常沮丧:

然而,不可逆的决策需要远比可逆决策更周密的规划与更充分的信息收集,因此不可避免地更耗时。事实上,在其他条件相同的情况下,以下规律成立:一项决策的时间跨度越长,依据给定的实质理性标准做出该决策所需的时间就越长。这揭示了当代时间发展的悖论:我们决策的时间跨度似乎在不断扩大,而做出这些决策所需的时间资源却在以同样的幅度消失。

因此,有些人进展极快并从中获益,而另一些人却因不得不追赶而感到举步维艰,这在一定程度上可能说明我们尚未妥善处理同步与去同步的问题。但这也可能是因为,当某些工作的产出需要或提供了快速行动者所依赖的稳定性时,人们不得不刻意放慢节奏。背景层面的快速迭代——通常被视为生态系统的一部分而理所当然——进一步加剧了所有参与者加速的必要性。

在加速思维模式下,我们会设法加快每一个能加快的环节,通过优化、技术创新、流程改进、规模经济等手段。这与Rosa关于加速自我强化的整体论点相呼应。⟦5⟧在Rosa提出的众多观点中,其中之一是:我们需要将加速的需求以及由此产生的压迫感(一切都在变快,跟上更难;因此我们也必须做更多)视为一种时间_结构_,它塑造了系统运作的方式。所以,技术创新提供了加速的机会(往往由经济力量驱动),这些创新改变了社会组织的构成方式(往往通过专业化),而这又通过一种对所能达成之事的更高期待以及世界不断加速的感觉,激发出进一步加速的需求,并推动技术创新。以下是他在书中提供的图示:

三个步骤互为因果:技术加速带来社会变迁的加速,社会变迁的加速带来生活步调的加速,生活步调的加速又反过来推动技术加速。这三个步骤既能自我维持,也各自受到一个外部“驱动”的推动:经济驱动(时间就是金钱)推动技术加速,功能分化(专业化)推动社会变迁,加速的承诺(在有限人生里做更多、体验更多)推动生活步调的加速。

我们通常把加速理解为技术进步的一种_结果_,但这里的观点是:_时间结构的加速_本身就构成一种塑造社会(当然也塑造我们这个行业)的机制。加速期往往还伴随着多种形式的抵抗;其中一些只是试图把局面重新控制住的反射性反应(免得被迫经历更多适应周期),但也有一些减速是有益的,它们能提供稳定性,并延长其他加速努力的时间视野。

很少有科技公司能说清楚生产力到底指什么,但不断改进它的冲动却是真实存在的。如果不更深入地理解工作是如何发生的,我们大概会继续看到:人们对新技术如何影响自己工作的说法千差万别,无非是对随机工作回路中随机环节的胡乱砍削与加码。我认为这一整体动态可以很好地解释,为什么有些人明明能把某些任务做得快得多,却并不觉得自己整体上更有生产力,甚至觉得自己并没有省下时间,反而多出了更多工作。有时候很难分清人们主张的是哪一种减速,但把所有要求减速的呼声都归为无用的卢德式抱怨,是要不得的。6 往后看,更有用的做法或许是检查一下:你是不是正在侵蚀自己的地基,却没有准备替代品。

这东西拿来干什么?

系统式思维往往要求把注意力放在组件之间的互动上,而不是组件本身。这里提出的模型所做的,是给这些互动加上一个时间维度。我们可以把工作中完成的任务和活动,看作生产软件这件事的组成部分。这些循环之间、以及不同人之间的同步要求和反馈路径,提供了一种方式,把它们的交汇点画出来。

说到底,即便是循环模型,也是一种粗糙的过度简化。人受所处情境影响,同时又反过来影响情境,这种相互影响是持续不断的,没法被约束成定义清晰的任务和序列。现实要乱得多。这个模型可以当作一个工具,提醒我们:没有任何加速是孤立发生的。每一次努力都蕴含着失同步的可能,也蕴含着相关循环随之重组的可能。某种意义上,目标不是找出具体的问题,而是找出节奏上潜在的错配——它们指向的是适应和跟上节奏时的困难。

采取哪种分析立场,是有影响的。孤立地优化任务,有时能在单个循环内取得不错的局部结果,偶尔也能在更大的范围里奏效。但把各个循环放在一起、连同其中一团乱麻去看,更有可能让你看清什么值得加速(或者为了给别的部分提速而放慢!),坑可能在哪里,以及调整的需求会在哪里一路荡开、最终演变成什么。实验和提速的办法总会有人做,也会一直做下去,除非西方社会发生剧烈变化;在实验时对后果该看什么有更清楚的想法,不是坏事。

1:我还没有在笔记区里发布过这本书的摘要或摘录,但它绝对属于那种我一读就知道会深刻影响我如何搭建框架的书。而且,正如我对身边每个看到我在读它的人承诺过的:读完之后我会非常烦人。好吧,来了。去弄一本《社会加速:现代性的一种新理论》。Columbia University Press,2013。

2:原始报道,图 50 见第 75 页。

3:这个例子并不是要说明同步点必不可少,也不是说它是唯一的同步点,只是说它确实存在,而且会产生影响。这来自我的个人经验,但我也在代码评审或 RFC 评审中多次见到同步点:只要工作跨越团队之间的孤岛边界、项目在组织层面的范围变大,就会出现。

4:我怀疑它也可以被视为技术债之类概念的成因之一,而技术债可以理解为验证一个方案与工程上维持它的可持续性之间的脱钩。

5:我认为这还与认知系统工程中的拉伸系统定律有关,总体而言,这可能正是不同学科对相似现象给出相似但不同表述的情形之一。

6:既然提到了卢德主义,就必须按惯例提一下 Brian Merchant 的 Blood in the Machine。这本书很好地还原了卢德主义的历史语境:它是工业革命初期工人试图保住对自己工作的掌控权的一场运动。卢德分子并没有系统性地抵制或破坏所有新的自动化技术,而是有针对性地针对那些工作条件恶劣的工厂主,对其他工厂主则手下留情。

来源: ferd.ca← 返回首页