
审视你的“意大利面条式”软件工厂
有一篇《异星工厂》(Factorio) 的 Steam 评测在社区里被奉为圭臬。这份关于工厂混乱的编号列表演变成了一段黑暗而富有诗意的独白:“一百座熔炉喷吐着浓烟,大地的黑色血液被从摇篮中强行抽出,以喂养工业的烈火。”叙述者变成了一名监视着死寂星球的半机械人。结尾的副歌“工厂必须成长”(The factory grows) 成了一句咒语。305条评论,几乎每一条都是多年后人们对这句口号的吟诵。
它之所以奏效,是因为它映射了游戏对你所做的一切。你从优化一个小东西开始,在不知不觉中,你就已经吞噬了一整颗星球,并且无法停下。那位评测者在玩了94小时后写下了这段话。后来他总共玩了235小时。
每个用 AI Agent 进行构建的人,都在以“速通”的方式重演这一体验。
你已经在建造一座工厂
你从一个 Claude Code 会话开始。然后是两个。接着是四个并行会话,每个都在自己的工作树中。你添加了一个 CLAUDE.md。你编写技能。你连接 MCP。你并非刻意要建立一座工厂,但你确实建成了一座。而且像所有没有计划就建成的工厂一样,事情很快就会纠缠在一起。没关系,意大利面条式的工厂依然是工厂。
问题在于它们生产的东西。布朗大学计算机教授 Shriram Krishnamurthi 发表了一份拆解报告,分析了 Claude Code 为一个书店应用生成的代码。涉及金钱计算的浮点运算。用自定义 CSV 解析器代替标准库。函数明明叫 filterByTitle,实际执行的却是搜索功能。每一个都是小问题。合在一起,就形成了一个教导下一个 Agent 会话写出更烂代码的代码库。垃圾产出垃圾 (Slop breeds slop)。
Joseph Ruscio 为这种趋势命名为:只写不读代码 (write-only code)。“很大且比例不断增加的生产代码从未被人类阅读过。”业界对此的反应是“更快的马”:AI 代码审查机器人、稍微快一点的审查工具。审查成了新的瓶颈。
我们曾经解决过这个问题。不是 AI 部分,而是结构部分。行之有效的模式是测试和持续集成 (CI)。
记录下来
第一直觉是正确的:Markdown。AGENTS.md 文件、规范、技能、计划。你在编码你的标准。你关心的安全实践。存在于你脑海中的命名约定。Agent 们不断逾越的架构边界。
编码不等于强制执行。你可以写出世界上最好的技能,但没有人必须使用它。你的队友不会,Agent 不会,凌晨三点打开 PR 的后台进程也不会。
在本地运行检查
真正的转变在于你编写检查并让你的编码 Agent 来运行它们。Markdown 文件,通俗易懂的英语,每一条都描述一个单一标准。每一个检查都作为一个完整的 AI Agent 运行,不仅仅是扫描差异,而是读取文件、运行命令、行使判断力。如果发现问题,它会标记失败并提供修复建议。如果一切清爽,它就会静默通过。
这在概念上并不新鲜。这就是测试。针对 Lint 工具无法表达的内容进行语义测试:“每个新端点都需要身份验证中间件”、“不要在没有理由的情况下添加依赖项”、“如果此 PR 涉及指标管道,请验证仪表板是否未损坏”。
我们曾有一个遥测完整性检查沉默了几周。然后,一个 Agent 修改了一个为核心仪表板提供驱动的事件名称。检查捕捉到了它,标记了影响,并建议了修复。一个约定检查阻止了硬编码颜色泄露到我们的设计系统中。这些不是假设,而是真实发生的。
在没有你的情况下运行检查
测试也是从本地开始的。开发人员在自己的机器上运行测试,并相信其他人也会这样做。但这种方式在今天同样不适用了,原因与当前检查面临的问题一致:“在我机器上能跑通”的差异(你的本地 Agent 具有不同的提示词构造、不同的上下文窗口、与你队友不同的工具配置)、荣誉准则的失效(人们会忘记、跳过、匆忙)、以及沉默的漂移。如果一个糟糕的约定在被发现前溜进了三个 PR,Agent 就会把该代码视为标准,并写出更多类似的代码。代码库会从内部退化。
Martin Fowler 在他最初的 CI 文章中定义了这一点。CI 更深层的洞见在于:结果必须对整个团队可见,且是即时的。红色的构建意味着每个人都知道。检查也需要同样的可见性,在 PR 上,在合并前,在问题恶化前。
亲手构建
下一步是把这些检查放入 CI,这样它们就会在每个 PR 上运行。GitHub Actions 是显而易见的选项:你编写一个工作流,启动 Claude Code,指向你的 .continue/checks/ 目录,让它评估 diff。现在的 AI 足够好,你相信 Agent 也能构建这个基础设施。它可以。80 行 YAML 代码。周一就能用。
第一个月:你正在管理用于并行执行的后台进程,将 stdout 路由到独立文件,序列化 API 调用以避免速率限制。Agent 并不总是能生成有效的 JSON,所以你添加了回退解析。一个检查失败了,你想知道原因,但会话是一个在已回收 VM 上终止的进程。第二个月:有人构建了一个仪表板,因为没人想深入查看 Actions 日志。然后它需要身份验证、diff 查看器、“接受修复”按钮、循环检测、历史记录存储。你为了不买 SaaS 产品,自己动手做了一个 SaaS。第三个月:检查已经漂移。没有指标来衡量哪些检查捕捉到了真正的问题,哪些只是虚惊一场。没有反馈循环。检查要么僵化,要么产生噪音,直到有人要求关掉它们。
有些问题不是 YAML 能解决的。你无法追踪那个做出判断的 Agent。你无法跟踪不稳定性、执行全组织范围的政策,或者从社区检查开始,而不是从零开始编写每一个检查。那个本应防止“意大利面条代码”的系统,最终成了它自己的“意大利面条工厂”。
随着你的工厂越来越快地生产代码,检查系统的可靠性就成了承重基础设施。一个对组织中每个 PR 进行质量控制的系统,绝不是副业。它是你需要随时待命维护的基础设施。你不会自己构建 CI,原因也是一样的:不是因为你做不到,而是因为可靠性本身就是产品,你宁愿这成为别人的全职工作。
添加任务控制中心
或者你可以跳过上一节。
同样的 Markdown 文件存放在 .continue/checks/ 中。在每个 PR 上进行完整的 Agent 执行。原生的 GitHub 状态检查。没有 YAML。不需要构建仪表板。它看起来简单,是因为那些棘手的问题(非确定性、判决解析、并发执行、凭据隔离、循环检测)都在底层解决了。在第三个月之前,你甚至不知道自己不知道这些。
第一个月,某个检查失败了,你带着反馈拒绝了它。“第 42 行是故意的,不要标记这种模式。”反馈优化了检查。因为你使用了它,检查变得更好了。
第二个月,干预率指标显示了人类覆盖了哪些检查,误报聚集在何处。这些数据推动了检查的演进,而不是僵化。你从社区检查开始,直接站在别人迭代了 15 次的成果之上。
第三个月,你意识到“PR 打开”仅仅是第一个触发点。你需要扫描整个代码库的机制,以便在引入新标准时进行检查。需要一些标记,将注意力引向敏感更改(身份验证、迁移、计费)而不进行阻塞。在 PR 合并时检查,而不仅仅是在 PR 打开时。根据 Sentry 警报和 Snyk 发现的结果进行触发。在“任务控制中心”中,这些都是你可以设置的 Agent。而在 YAML 中,每一个都是需要维护的额外工作流。
工厂必须成长
我们自己也走过这段旅程。然后我们开始阅读关于 丰田生产方式 的内容,意识到轮子早在几十年前就被发明出来了。自働化 (Jidoka):带有人类触感的自动化。改善 (Kaizen):持续改进。安灯 (Andon):停止生产线的报警绳。捕捉到损坏仪表板的检查就是一根“安灯绳”。根据过去 20 个 PR 演进出的检查就是“改善”。整个系统(由人类编码标准、由 AI 执行、由人类决策)就是“自働化”。
我们观察到每个团队的演进历程:他们从针对最痛苦的标准进行的一项检查开始。他们看到它捕捉到了真实的问题。他们又写了三项。几周之内,.continue/checks/ 就成了他们工程品味的编码体现。
那篇《异星工厂》的评测以一个监视死寂星球的半机械人告终。软件工厂不必以那种方式结束。工厂必须成长,没错。但当你审视它时,它会成长得更好。