八个人并行写一个Mac桌宠,为什么反而更难

1 评论 545 浏览 0 收藏 19 分钟

8人两周开发Mac桌宠,看似高效分工却可能陷入集成困境。本文从《人月神话》到NASA事故,揭示并行开发中接口契约与共享事实的关键作用,教你如何将“完成”定义为“可安全依赖”,确保AI Agent协作不失控。

8 个人、两周时间,做一只住在 Mac 桌面上的像素小黑龙:它能发起录音,把会议内容交给 AI 整理成候选行动项,等用户确认后写入任务清单,再在临近截止时间时出现提醒。

听起来,人不少,范围也明确。把功能平均切成 8 份,大家同时开工,似乎很快就能拿到一个 MVP。

但这类项目最危险的结果,不是某个人没写完,而是 8 个人都写完了,最后却得到 8 个局部正确、无法拼起来的项目。

八个人写出了八个局部正确

平均分工能提高同时开工的人数,却不一定缩短项目的关键路径。

这只桌宠横跨 Tauri、React、Rust、SQLite、录音与 AI 六类技术环节。窗口不仅承载宠物交互,还是录音入口的上游;录音结果要进入 AI 处理链路;AI 生成的摘要和候选行动项要写入本地数据层;任务页面、提醒机制和宠物状态又必须读取同一份事实。

这些工作看起来可以拆开,实际上有非常明确的先后依赖。

如果桌面运行时没有形成稳定入口,录音模块即使单独运行,也没有可靠的挂载位置。录音格式和状态定义没有确定,后面的识别与处理就只能先猜。AI 输出结构没有约束,数据层也无法确定该保存什么,更别说让页面和提醒安全消费。

这时候,继续增加并行任务,并不会自动缩短主链路。它只是让更多人同时等待,或者同时基于假设往前写。

Fred Brooks 在《人月神话》中提醒过软件团队:“向一个已经延期的软件项目增加人手,只会让它更晚。”[1] 原因并不是后来加入的人没有产出,而是培训、沟通和任务重新切分都需要成本。

如果团队有 n 名成员,潜在的两两沟通关系最多是 n(n-1)/2。8 个人对应 28 条潜在关系。[1] 这不代表团队每天真的要发生 28 次沟通,但它足以戳破一个直觉:把工作平均切成 8 份,并不会天然得到 8 倍速度。

你给 8 个人各派一艘船出海,不难。难的是船回港的时候,甲板上应该有什么,谁来清点货舱,怎么算数。如果这些没有提前说清楚,8 艘船满载而归,也可能没有一件货能直接交给下游。

所以,项目早期真正需要优化的,不是“多少人已经开工”,而是关键路径上的每一棒,能不能被下一棒稳定接住。

真正的瓶颈藏在接口之间

既然平均切分无法缩短关键路径,再往下追一层,问题就不是人分得够不够开,而是共享事实有没有被钉死。

AI 确实擅长加速边界清楚的局部任务。GitHub 在 2022 年的一项受控实验中,让 95 名开发者完成同一项 HTTP 服务器编码任务。使用 GitHub Copilot 的一组平均耗时约为 1 小时 11 分钟,未使用组约为 2 小时 41 分钟,前者快约 55%。[2]

这个结果很有价值,但它只能证明:当任务边界清楚、反馈快速时,AI 可以显著提高局部编码效率。

它不能被直接外推成“一个 8 人项目也会整体快 55%”。架构决策、接口协商、代码评审、测试和系统集成,仍然可能卡住整个项目。局部编码越快,甚至越容易把含糊规范的代价放大。

说白了,AI 像一个执行速度很快的新人。没有 Memory 的 Agent,每次都是从零开始;没有稳定契约的 Agent,则会根据各自看到的上下文,迅速写出多套都“看似合理”的实现。

一个模块把时间保存成字符串,另一个模块按时间戳读取;一个人把 RecordingStarted 当作执行请求,另一个人把它理解为已经发生的事实;一套实现默认 AI 输出可以直接创建任务,另一套实现坚持必须经过用户确认。每个局部都能自圆其说,但系统没有共同答案。

这不是抽象的工程洁癖。

NASA 的火星气候轨道器在 1999 年失联。调查报告指出,一个团队提供的冲量数据使用英制单位,另一部分软件却按公制单位处理,接口要求没有被正确落实和验证。该任务成本通常被引用为约 1.25 亿美元。[3]

这个案例不能证明所有接口错误都会造成同等损失,却清楚说明了一件事:组件分别运行正常,不等于共享契约正确。

所以,真正的反转在这里。

AI Agent 越能高速并行,团队越不能只管理“谁在做什么”。如果输入、输出、状态语义和验收方式仍然含糊,高速产出只会让错误实现更早、更多地进入集成阶段。

并行失控,不是因为执行者太多,而是因为大家正在依赖不同版本的事实。

把“完成”改写成“可安全依赖”

共享事实没有钉死,下一步就不能继续催产出,而要重新定义什么叫完成。

真正往前推的那一步,是先画依赖图,再决定任务怎么切。依赖图不是为了把项目管理做得更漂亮,而是为了找到那些“一旦没稳定,下游就无法安全开工”的节点。

对这只小黑龙来说,窗口、录音、AI 处理、本地存储、任务展示和提醒不是几个平铺的页面,而是一条有方向的链路。任务切片应该顺着这条链路,交付可被验证的能力。

一张有效的任务卡,至少要回答:

  • 上游会提供什么输入;
  • 当前任务承诺产生什么输出;
  • 下游从什么时候开始可以依赖;
  • 哪些公共类型、Command、Event 或 Schema 会被触碰;
  • 用什么证据证明这部分已经成立。

这里最重要的,不是把任务卡写得更长,而是改变分工的最小单位。

页面并不天然是坏的分工单位。数据契约已经稳定、页面之间依赖较少时,一个包含 UI、状态、数据适配和测试的垂直页面切片,完全可以高效推进。真正有问题的是,只按视觉区域分任务,却没有把输入、输出和责任边界一起交付。

David Parnas 在讨论模块分解时提出,模块应该围绕需要隐藏的设计决策来划分,而不只是按处理步骤拆开。[4] 放到多 Agent 协作里也是一样:分工单位不该只是“你写设置页,我写任务页”,而应该是一个对外承诺稳定、内部实现可以独立变化的能力。

这也是为什么 Commit、PR、测试和 CI 不是附加文书。它们把“我说完成了”转换成“别人能够检查”。

DORA 长期研究关注部署频率、变更前置时间、变更失败率和故障恢复时间等指标,其核心发现并不是速度必然牺牲稳定性,而是高绩效团队通常能够兼顾吞吐量与可靠性。[5] 对早期项目来说,这意味着验证机制不是拖慢交付的刹车,而是让下游敢于继续加速的路面。

Project 可以理解成专门为一个项目开的房间。但房间里不能只有聊天记录,还要有公共类型、接口样例、测试结果和变更规则。否则大家虽然坐在同一个房间里,看的仍然是不同版本的菜谱。

因此,“完成”至少应该区分四种状态:探索中、可评审、已合并、已验证。Draft PR 能提前暴露接口和实现方向,但不能被统计成已经交付;代码已经合并,也不代表完整链路已经通过验证。

“我写完了”描述的是个人动作,“下游可以安全依赖”才描述了团队进展。

关键不变量需要额外护栏

当“完成”被改写成可安全依赖,接下来要保护的就不是所有细节,而是那些一旦出错,会直接破坏用户信任的关键不变量。

以 AI 处理链路为例。

一次录音可能触发耗时的 AI 请求。如果请求失败后重试,旧响应可能比新响应更晚回来。此时仅仅把结果写进数据库,会出现两个不同问题:摘要、候选行动项与处理状态可能只写入一部分;旧响应也可能覆盖用户已经更新过的内容。

这两个问题看起来都叫“数据错了”,根因却不同。

原子事务解决的是完整性。SQLite 事务中的修改要么全部提交,要么全部回滚。[6] 因此,可以把摘要、候选行动项和处理终态收进一次 commitProcessingResult,避免摘要已经保存、行动项却没有落库的半完成状态。

但事务不能判断响应的新旧。

要阻止迟到响应覆盖当前记录,还需要 revision、处理请求 ID 或条件更新。每次有效修改都让版本递增,写入时声明“仅当当前版本仍为 N 时提交”。如果版本已经变化,旧响应就必须被拒绝,并进入明确的丢弃或重试策略。

提醒链路也是同一个问题。任务的截止时间已经修改,旧提醒就不应该继续触发;同一个 AI 请求被重复提交,也不应该创建两组相同行动项。这里需要的是版本条件、稳定请求 ID 或去重键,而不是指望执行过程永远只发生一次。

当然,两周 MVP 不应该提前造一套完整的分布式任务系统。这个反对意见是成立的。

控制平面的价值,不是给每个字段加审批,而是让护栏强度匹配错误代价。如果 AI 结果未经用户确认就变成正式任务,或者已经修改的旧提醒仍然出现,用户会直接怀疑整套系统是否可信。这类不变量值得保护。至于组件内部命名、样式组织和可逆实现,没有必要全部上升成公共规则。

内存 Fake 也应该放在这个边界里理解。它适合让前端和业务逻辑提前验证契约,却无法复现 SQLite 的锁、事务、迁移和约束行为。Fake 通过,只能说明调用双方暂时说着同一种语言,不能说明真实数据库已经可靠。关键路径仍需补充少量真实 SQLite 集成测试。

护栏不是越多越安全。真正有效的护栏,只拦那些掉下去代价很高的地方。

控制平面不能变成审批机器

关键不变量需要保护,但这里很容易走向另一个极端:把控制平面做成一台审批机器。

如果每个变量名、组件样式和局部重构,都必须等待组长更新任务卡、批准方案,控制层本身就会成为新的单点瓶颈。原本想减少协调,最后却把所有协调集中到一个人身上。

所以,有效治理不是集中所有决策,而是根据变更的影响半径分配决策权。

Schema、跨进程事件、持久化语义以及用户确认边界,会影响多个下游,应该严格评审。组件内部结构、局部样式和容易回滚的实现细节,则应该交给执行者自主决定。

这像给船队规定航线:不许抢商船凑数,不能闯入未授权海域,回港时要按统一规则清点货舱。但船长在既定航线上如何调整风帆,没有必要每次都向港口申请。

页面切分同样不该被一棍子打死。当公共数据契约已经稳定,页面能够独立验收,按页面形成垂直切片反而很高效。问题从来不是“页面”这个词,而是任务有没有带着可验证边界一起交付。

状态汇报也是如此。

“当前完成 80%”“基本没问题”“还差一点联调”,这些话看似在同步进度,实际很难支持下游决策。固定汇报模板如果只增加文字,还会制造表演性进度。

更有效的状态同步,应该绑定可检查证据:

  • 当前已经可用什么;
  • 哪些部分尚未完成;
  • 下游现在可以依赖什么;
  • 是否存在未声明的破坏性变化;
  • 最新验证对应哪个 Commit、PR、测试结果或复现步骤。

模板的价值不是让每个人写一份更像样的日报,而是尽早暴露依赖是否稳定。Linux 内核的大规模协作也不是依赖所有贡献者实时同步,而是通过补丁、维护者、评审规则、子系统边界和自动化检查,让变更能够被追踪、审查和回退。[7]

控制平面真正要减少的,是猜测,不是自由。

组长维护的是团队可验证性

控制平面不能集中所有决策,那么组长真正需要维护的,也就不是每个人的每一步,而是团队的可验证性。

项目出现阻塞并不等于系统失控。更值得警惕的是,阻塞无法被发现,影响范围没人说清,恢复条件也没有留下记录。

当上游没有形成稳定底座时,下游复制一份代码继续开发,看起来进度更快,却会制造两个事实来源。等上游恢复,团队还要判断哪份实现才算数、差异如何合并、测试该相信谁。相比之下,把依赖、影响和恢复方案公开写进 Issue,短期看像停顿,长期看是在保护真实进展。

这也是为什么探索、评审、合并和验证必须分开。一个 Draft PR 只能说明方向已经公开;通过评审代表方案可以进入主线;完成合并意味着公共代码发生变化;只有测试和集成结果成立,下游才获得新的可靠依赖。

如果要判断多 Agent 协作是否真的提高效率,生成了多少代码并不是最可信的指标。更值得观察的是首次集成成功率、返工次数、契约破坏次数、PR 等待时间,以及任务开始后多久能让下游安全依赖。

AI Agent 确实可能省下大量执行时间,但它也会新增协调成本。任务越含糊,Agent 越可能高速制造多套局部答案;反馈越慢,错误方向积累得越深。真正应该扩大的,不只是 Agent 数量,而是团队验证承诺、发现冲突和回退变更的能力。

回到那只住在 Mac 桌面上的像素小黑龙。

它表面上是一只会录音、整理行动项和提醒任务的桌宠。可在项目早期,真正需要被驯服的并不是这只龙,而是那些高速产生、彼此冲突、又缺少证据的协作结果。

先让每一棒都能被下一棒验证,再谈让更多人和 Agent 同时起跑。

[1]: 数据来源:Fred Brooks,《The Mythical Man-Month》,1975 年。

[2]: 数据来源:GitHub Research,《Research: Quantifying GitHub Copilot’s Impact on Developer Productivity and Happiness》,2022 年。

[3]: 数据来源:NASA,Mars Climate Orbiter Mishap Investigation Board Phase I Report,1999 年。

[4]: 理论来源:David Parnas,《On the Criteria To Be Used in Decomposing Systems into Modules》,1972 年。

[5]: 研究来源:DORA,历年 State of DevOps 研究报告;Nicole Forsgren、Jez Humble、Gene Kim,《Accelerate》,2018 年。

[6]: 技术来源:SQLite 官方文档,《Transaction》与《Atomic Commit》。

[7]: 案例来源:Linux Kernel 官方文档,《Development Process》。

本文由 @伊森vibe 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自Pexels,基于CC0协议

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
海报
评论
评论请登录
  1. “完成是可安全依赖”这个定义很有用,但它解决的是集成问题,不是需求定义问题。两周MVP还没跑通之前,很多接口本来就不该早早锁死,锁死了反而让产品失去调整空间。

    来自广东 回复