软件工厂是围绕软件工作的一个可重复循环。如果你正在构建软件工厂,即使代码已经好到可以交付,仍然需要人类的品味和主导权。我们会讨论这一点,包括你是否 真的 现在就需要一个工厂。
如果需要:
你要构建自己的工厂,让人类品味的某些方面被编码进环境里,让代理为其工作的正确性提供证据,同时也让人类依然“拥有”最终进入生产环境的内容。
根据我的经验,使用现成的编码工具链,你已经能走得相当远! 也就是 Claude Code 或 Codex,多轮会话,带有内置验证和约束的高质量 SPEC。你甚至可以把一批 GitHub issue 丢给它们,并附上实现和人类参与标准;但当这个系统需要可重复且由事件驱动时,软件工厂就会很有帮助。

所以我一开始就说过,软件工厂是围绕软件工作的一个可重复循环。其实我们可以看一个提示词,它展示了一个非常小的工厂闭环:
在修改代码之前,请先阅读 GitHub issue #123 和仓库说明。 只实现所述验收标准。不要修改认证、 计费、迁移或现有测试断言。请在分支中工作,并保持 diff 便于审查。 运行 npm run lint、npm test 和 npm run build。如果某个必需的检查无法运行, 请停止并说明原因。创建一个草稿 PR,写明你运行了哪些检查、 仍然存在的风险,以及仍需人类做出的任何决定。不要合并。
一个目标可以让这个流程一直推进,直到检查全部通过;而我们可以每天早上轮询 GitHub issues,查看特定标签,或者审查已打开的 pull request。分支保护可以强制设置合并边界,而人类则可以通过决定哪些内容已准备就绪、进行审查并做出最终合并决定等方式持续参与闭环。
当你需要一个事件驱动的工作队列(例如 Slack 触发、GitHub issues、Linear、待办池)在隔离的云环境中运行,以便在一定程度的人类看护下处理分诊、实现和测试时,就应该引入软件工厂。有些系统会在闭环末尾加一个监控代理,观察生产环境并创建 issue,然后再进入新一轮分诊。
根据我的经验,当真正困难的部分变成:如何让不同的运行保持一致、如何在代理之间交接工作、如何避免不同会话认领同一个 issue、如何保留证据,以及在人类审查跟不上的时候如何阻止生产发布,这时软件工厂就开始变得有用。
解决这些问题的方法听起来可能有点平淡。比如,Warp 提到要把每个进入的 issue 分诊到四种状态之一——ready-to-implement、ready-to-spec、needs-info、wait-to-implement——而标签就是触发下一个代理的开关。

这个标签一举多用:它既是队列,又是锁;而且由于一个会话只会接手标记为 ready 的内容,它也成了人类可以暂时搁置某些事项、而不是永久拒绝它们的地方。
从工作流角度看,它和单纯使用 Claude/Codex 有一些相似之处,也有一些不同:
在一个好的工厂里,人类并不局限于最后只在末尾审查并批准最终 diff。他们可以在早期塑造工作,在实现过程中进行引导,在交接环节介入,或者阻止其发布到生产环境。
验证是负责任的工厂投入大量时间的地方。后面我们会更详细地讲。

如果你决定确实需要软件工厂,自己从零搭建并不是唯一选择。为了让工厂规模化,搭建基础设施可能是一项大工程,你可能需要考虑买现成的还是自己构建。Factory、Warp 和 HumanLayer 都在做这方面的工作。
过去一年里,我对软件开发的日常体验改变了很多。我一直在谈越来越多地与代理并行工作,朝着一个点亮式软件工厂的方向前进。很多人也一直在问我,这些到底是什么意思?你在构建什么?你会把这些东西用在哪些项目上?大致就是这样:

所以在一个非常普通的日子里,我会有一个更简单的点亮式软件工厂。我可以让一些任务在云端运行。这些任务里有一半可能是在为我合作的一些小公司做生产级客户端应用。这些应用会有真实用户,会有真实的认证、支付、订阅,以及各种必须小心处理的重大风险。你不能只是对代理说一句“去做吧”,而不先把测试、约束和质量检查都安排好。
我可以做开源项目。我可以给我的书做配套网站。我可以开发工具。我可以构建自己的应用。而这些全都是非常非常不同类型的应用。我有时候甚至只是换了一个工具在做事,对吧?也许我在处理一次迁移;别的时候,我可能在做真正沉甸甸的功能开发。而这些工作的影响范围也可能非常非常不同。
所以,当你开始思考如何走向越来越多的并行工作、如何提升速度、如何提升生产力、如何提升自治性,也就是把系统推进到一个我们对它更信任的状态时,你确实要思考:哪些地方绝对需要人工代码审查、人工输入。
其中很大一部分需要在前期完成,对吧?当你在定义规范、需求时,产品的设计会是什么样子?产品意图会是什么样子?然后,你又如何验证代理确实把工作做好了?如何验证它们没有破坏原本已经存在的系统?你又如何确保它达到了你的质量标准?
所以,生成代码本身未必是你最该担心的部分。只要上下文足够,代理可以替我们写实现、运行测试、检查失败并修改代码。我们需要走到这样一个状态:环境中编码了足够多的人类品味,我们能够信任正在构建的东西,从而让人类注意力集中到最需要的地方。
现在,也有人提出反对意见:嘿,我不相信你能把这类事情自动化掉很多。并不是说我们要把一切都自动化掉,对吧?但考虑到正在生成的代码量,我认为让人类把所有内容都读一遍并不现实,尤其是在我们大多数时候并不是在造火箭,对吧?我们是在做 UI,在做全栈应用。
我们的判断和品味,最好集中在最需要的地方。比如,系统中哪些部分最危险?我们需要把人类品味应用到哪里?这可以是在前端,也可以是在系统的工作方式上。没必要覆盖 100%。
现实是,是的,我们现在可以并行启动几十、几百、几千个代理,但你自己的认知带宽并不会以同样的方式扩展。这会进一步积累成我之前提到过的认知债务或理解债务。

如果你回想五年、十年前,工程界曾经大量讨论上下文切换及其成本。我们会说,当同事或别人中途走到你桌边打断你时,你会有多反感。因为你得花很长时间才能重新进入心流状态——你得重新想起来:我刚才在哪?我在做什么?即便脑子里还残留一点点印象,重新接上也还是需要时间。
而我们现在的上下文切换比以前还要多。如果我某天在软件工厂之外工作,我同一时间可能会和代理一起处理五个、十个不同项目,或者在同一个项目里同时推进五个、十个不同功能。你甚至可以说,我会同时开五个、十个不同会话。
这意味着,我必须至少能跟住其中的一部分。如果我对某些任务已经有足够信任,觉得自己把任务定义得足够好、把结果定义得足够清楚、把如何验证完成度也定义得足够好,那么我就有可能提高这些任务的自治程度。但也会有一些任务,我不一定有这种感觉,它们风险更高,或者更需要权衡。我就必须更关注。
考虑针对你的审查者来优化软件工厂。因为不管采用哪种方案,最终输出都还是会落到一个人的注意力上,所以你应该问:工厂究竟让你仍然需要做的那些决定,变得有多便宜了?
我记得有一次,我和代理并行处理多个项目的时候,曾经不小心做过这样的事:也许我在一个 Web 应用里想加深色模式,所以我脑子里已经有了想法,知道大概应该是什么形状。但我误切到了另一个项目的会话,然后开始输入同样的提示词。
于是我开始给一个根本不需要深色模式的东西实现深色模式。所以这种错误我是会犯的。我不希望我的软件工厂犯同样的错误。
你真的需要从系统的角度来思考这个问题。你实际上是在试图把一种软件工程文化、一种团队文化编码进系统里,让它拥有相同类型的行为,让责任有明确的归属,让总有人要对发生的事情负责,并且你要非常明确地说明你是如何看待这些事情的。
即使在这些系统里,你也必须非常谨慎,对吧?我们很多人都见过这样的情况:当你让 AI 帮你通过某个测试,比如程序测试、单元测试,它可能会修改单元测试来满足条件,也可能会修改代码逻辑来通过条件。但这并不意味着它真的遵循了你的意图,同时兼顾了功能行为和测试本来要验证的内容,对吧?

软件工厂显示一切都是绿色,并不意味着它真的一切都绿,尤其是在刚搭建这些东西的时候。你需要非常仔细地确认你的检查项、验证项,所有这些都被设计得正确。它们确实在按你预期的方式工作。你不希望它们误导你。
你不希望出现这样的情况:测试告诉你,嘿,我实际上已经改了支持哪些认证提供方。你让我要添加 GitHub 作为认证提供方,但嘿,我的 UI 只够放三个,所以我把另外一个删掉了。顺便说一句,被删掉的那个恰好是你的客户真正想要的。所以你必须非常明确地规定这些系统该如何工作。
顺带一提,安全也超级重要。如果你的工厂会读取不受信任的输入,比如 GitHub issue/Slack 消息,那它可能带有敌意,并包含供应链攻击之类的问题。因此,一些关于软件工厂的探索,比如 Vercel,会在隔离沙箱中运行代理,并且只保留任务所需的密钥。这样,即使某次运行被攻破,也无法接触到任务不需要的东西。你的防御最终会是分层的。
我也认为,如今我们工作方式中的很大一部分,是在决定什么应该存在。回想很多年前、AI 还没出现的时候,有那么多被搁置的软件工程项目、周末项目、个人项目,因为我们没有时间把它们做完,它们根本不会发布。我们没有足够的带宽去优先把它们推向市场,因为它们对我们来说没那么重要,或者我们就是找不到时间。
现在,把这些项目完成对我们来说相当容易,但同样的人类判断问题又出现了。这些项目值得存在吗?它们应该发布吗?因为你把它们放到世界上,即使只有五个用户,也许你就得维护它。也许你现在就需要维持一个质量标准。
我知道这些年来我有很多 GitHub 项目,现在一旦有了代理,我做的第一件事就是先让它能构建起来。当然,你一克隆下来,它就不能构建了,因为依赖都变了。半数东西都过时了,或者到处都是安全漏洞,所以你得先更新这些。
然后,如果你原本没有测试,就得补上测试,这样你才能知道,如果以某种方式升级项目,或者把它迁移到更现代的语言、框架之类的东西上时,行为至少还能保留下来。
接着你会开始问自己,也许举个傻一点的例子,但也许我当年用了 Twitter Bootstrap,现在大家都在用 Tailwind 和 shadcn,所以我得重做 UI。你会发现,突然之间这件事花的时间更多了,对吧?是的,代理确实能更快完成很多工作,但你现在还得把产品感、品味以及所有这些因素都考虑进去。
你仍然会问:这到底是给谁用的?它有市场吗?是给我自己的吗?还是给别人的?如果我把它放到世界上,在如今任何人都能这么快把这类东西搭起来的情况下,它还会一样有吸引力吗?
所以我觉得,关于这些东西是否值得存在、我们如何融入自己的品味和判断,这个问题依然极其重要。人类注意力这个稀缺资源,依然在这里真正发挥作用。过去,我们一天只有有限的时间。我们有会议。我们必须为设计、编码等等预留时间。
现在有了代理帮忙,我认为你必须非常明确地说明:你把时间花在了哪里,以及为什么。
接下来我想讲讲那个 82 分钟的工厂运行。很长一段时间以来,大家一直问我:“我该怎么构建软件工厂?”或者“我习惯用 Claude Code 或 Codex。我该如何把自己的配置演进到软件工厂?”
所以我最先一直在说的是:“你可能已经够用了。你的工作实际上可能完全不需要工厂。”不过我确实想给大家一个可参考的配置,方便他们去看。所以我做了一个名为 Factory 的仓库,你可以去看看。我还做了一个 演示应用 和 工作坊。
过去几年里,我做很多事情时最常用的演示应用一直是一个电影应用。我非常喜欢电影。我总是在看电影,所以我有一个演示应用,它最开始其实就是一个非常简单的电影应用。而我希望工厂能够做的,是继续实现一系列功能。这里有几个不同的功能。我想要一个收藏功能。我还希望它能做搜索,另外我也想加一个深色主题之类的功能。
于是我让工厂开始处理这些事情。你可以去查看实现过程。它的一个好处其实是捕捉到了真实问题。这些问题未必是我如果只让它一次性实现时会发现的。
大概到 60 分钟左右时,我开始觉得:“哇,这进度异常地慢。”我就问工厂工具链:“为什么这么慢?”它说:“其实完全正常。所有验证器还在运行。”
你可能会以为单个任务只要 10 分钟、15 分钟、20 分钟,但一旦把验证、重试、浏览器检查、人类审查以及其他任何额外延迟都算进去,它们可能会花上两到四倍的时间。
我确实认为,这些东西最终会带来更高的质量和更强的系统信任。从度量角度看,你可以把每个合并 PR 的成本和代码保鲜期看作理解债务指标。
你还需要思考,哪些是有价值的延迟,哪些只是工厂开销。就我而言,验证器确实捕捉到了一些真实问题。有一部分时间可能确实花在了生成我想要的证据上;另一部分则是工厂运行本身的开销。我其实没有花时间去优化它,但一个只是跑了很多你并不觉得有价值的检查的工厂,并不意味着它就是高质量的。
你应该研究这些重复检查:它们是不是无关?是不是噪音太大?它们真的让系统更安全吗?
我看待验证预算的方式,基本上就是我们在讨论的这个东西。我们在讨论的是一个验证预算。我对它的理解,就像我过去长期看待性能预算一样。

在软件开发生命周期的早期,你可以运行某些类型的检查;也会有一些检查非常重,但价值巨大,因此你会希望在后期运行。比如一些快速检查、lint 检查、类型检查,这些都是相对较快、可以尽早运行的检查。
而完整的测试套件,则可以放到更接近起草 PR 之前,或者之后再运行。这些可以包括变异测试、浏览器测试、安全检查,以及类似的任何内容。
我认为你不一定要用摘要来替代这些检查。你仍然需要真实测试,只是你要确保把它们安排在合适的位置,因为你不希望拖慢开发闭环。我当然也不想让我的开发闭环变慢。快速迭代对我来说很重要,但我也仍然希望保留这些制衡。
上面我写的很多内容都和检查有关。
在他们的软件工厂里,Vercel 会把每次代理运行标记为 “success”、“flawed”、“blocked” 或 “manual”,只有 “success” 才会发布到生产环境。其余状态会重新进入系统。我一直在用类似的术语思考这些运行。

这里的 “flawed” 表示实现了错误的内容,或者可能上下文不完整,所以必须修正。blocked 表示环境里可能缺少某个凭证,因此你得补上。manual 是工厂暂时还不能跨越的边界。
这三类中的两类可能有机械性的修复办法,而最后一类关乎信任。
不过,虽然这种分类很好,但它没有展示成本。回到我用 TMDB 应用做的工厂实现,那个没有被拒绝的快速查找只用了 7 分钟。收藏功能则因为中间有两次拒绝和一次人类决策,花了 56 分钟。同一个工厂。所以我会把分类法和各阶段耗时结合起来看,否则你只知道某次运行返回了 flawed,却不知道为此付出了什么代价。另一个我会修正的地方是边界处的交接:我的样例工厂先停止了 issue,再把它移到 factory:needs-info,这本身是对的,但我不知道该把我的答案放到哪里。manual 运行并不是在工厂停下来时结束,而是在人类知道下一步该做什么时才算结束。
我前几周写过一篇关于代理式自治的文章,以及如何理解自治,因为自治不会对每一个项目都只是一个单一设置。
验证买来的,是信任;也是把更多自治授予代理的能力。所以比如说,如果我正在做一个不算简单的变更,但我已经布置了若干检查,一切都验证正确了,而且我自己也手工检查过。那么下次在同一个项目里再做类似任务时,我可能就会更愿意给代理更多一点自治权。
这就是你在构建这些软件工厂时需要思考的事情。你的验证会随着风险而变化。你的目标是获得最佳信噪比。你不只是想跑一个很长的清单。
有一天,我一直拖着没做一个功能,当时我在 Claude 里开了多个会话,同时还在几个不同项目、每个项目的几个不同功能之间来回切换。于是 Claude 已经实现了我正在做的那个功能。看起来测试都通过了。我对验证没有花很多心思,但测试是通过的,所以我以为它没问题。我就把它合并了。
那是一个收藏功能。我当时觉得这其实挺不错的。我还试着在浏览器里看了一下,似乎也还行,但几天后,我真的又回到了这段代码,因为我觉得也许还能做些小调整。
我不想只是直接让代理改,因为它的工作方式很微妙。你点一下那个图标,它在点击时不会显示正确的效果,所以我想稍微调一下。我还想弄懂它是怎么工作的,这样我就能正确引导我的代理。
我回到代码后,竟然无法向你解释这个功能是怎么工作的。这是我的仓库,对吧?我批准了这个改动。我理解其中很多部分是怎么工作的,仓库里的很多东西我也懂,但我的理解已经跟不上不断堆积起来的代码了。
我没能真正吸收的是:这个后来被加上的功能到底是怎么工作的,UI 是怎么工作的,效果又是怎么作用于它的。我不得不把这个功能重新做一遍,真的一步一步来:“这到底怎么运作?我该怎么理解它?”
当你做并行工作时,这个整体问题会被放大;而当你在软件工厂里做并行工作时,它会被进一步放大。你开着五到十个会话时,产生的问题远不止审查量增加这么简单。它们会形成好几个心智模型,而这些模型在你处理别的事情时可能会迅速“冷掉”。
我们历史上一直在谈上下文切换带来的挑战;一旦聊天内容被压缩,你拒绝了一些方案,尝试了不同方法,又在和代理配对,你会很难记住会话里发生的一切。
你可以往上翻,但随着压缩不断发生,那里不会保留所有内容,你也不可能全都装在脑子里。代码往往保留了一个决策,但不一定保留这个决策为什么会被做出。
我觉得这对你可能是个有用的启发:在重要的地方,可以考虑让代理实际存储关于其轨迹的信息,或者它处理某个问题时得到的有意思的经验教训,这样你之后可以再回看。
这可以,也可以不,可以是你决定提交到仓库里的内容。如果你愿意,可以只保留在本地;如果你愿意,也可以和团队共享;但这样一来,之后就可以拿来查阅,而不是依赖它可能还留在某个会话里,或者你之后也许还能记得。

P.S. - 如果代理正在向你的主分支推送代码,它们也需要和你一样的质量标准。SonarQube 会确定性地把控每一次提交:人类和代理同样的标准,适用于每一个 PR。试试 Sonar → ( https://fandf.co/4g7DP8r ) · 本内容由 Sonar 赞助。
这一切背后有一个更大的原则。
人类亲手输入的代码比例,也许会大幅下降。但我不认为人类的责任归属也必须随之下降。
当最终的系统出问题时,“是代理写的”并不能算数。这就是为什么我认为,软件工程的未来,最好不要描述成人类退出闭环。相反,人类判断正在被重新安放。

P.S.——如果智能体(agents)要直接推送到你的主分支,它们也需要和你一样的质量标准。SonarQube 会以确定性的方式为每一次提交设置质量门禁:无论是人类还是智能体,每个 PR 都遵循同一套标准。试试 Sonar → ( https://fandf.co/4g7DP8r ) · 由 Sonar 赞助。
这一切背后还有一个更宏观的原则。
由人类亲手输入的代码比例,也许会大幅下降。但我并不认为,人类对代码的所有权也必须随之下降。
而当最终系统失败时,“是智能体写的”并不能成为借口。这就是为什么我不认为软件工程的未来,最适合被描述为人类退出循环。相反,人类判断正在被重新安放。
我们应该把人从那些机器能够产生更强、更快、更确定性信号的环节中移开。同时,把人集中在那些最需要上下文、品味、风险判断和长期所有权的地方。
最好的软件工厂,不会以“它们消除了多少人类参与”来定义。
而会以“它们多么聪明地安放了人类参与”来定义。
让人类判断留在上游:负责意图、系统形态和质量标准。在自动化反馈变弱,或后果变得带有主观性的地方,再由人审查代码。尽可能把每一种确定性的信号,尽早、持续地推进到循环中。随着系统赢得或失去信任,审慎地收紧或放宽约束。
最终要对即将发布的代码负责的,仍然必须是人。足以发布的代码,也始于此。
