repo 得当成 agent 的 API 来设计
这几个月我基本不怎么自己写代码了,改动基本上全是 agent 写的。具体的工作流程大概是这样的:先让 agent 通过 research 去做 codebase mapping(如果是旧的 codebase);接着通过我和它的 brainstorm discussion,结合它自己的 audit 和 three-pass review,定好一个 plan——这一步我一般会用 fable 来写。plan 定好之后,交给另外一个 agent(比如 gpt-5.6)去执行。
反正现在已经不是人去写代码了,代码也不是人去读。人去做的是 high-level 的决策,去输出自己的判断和认知,执行层已经全部换成了 AI。
这件事对怎么维护一个 repo 的影响,比我一开始以为的大。代码的读者换成 agent 之后,仓库就不再是写给人看的文档了,它更接近一个接口:agent 每次干活,都要从这个接口把对整个系统的理解重新读出来。接口设计得好不好,直接决定它每次干活的质量。
tribal knowledge 现在可以直接分发了
人维护代码有很多这种 tribal knowledge,搞明白以后也很难 share 给别人。比如 auth 为什么拆成两块、哪个 config 不能乱动,这些东西一般都在踩过坑的人脑子里,新人要么再踩一遍,要么去问人。
但对 agent,只要把这个记录下来,你就可以随便去分发。就跟软件一样,你可以直接分发这种认知。对 AI 来说,这就是一种 skill,或者说一个 md 文件,直接可以去分发这种认知。
含糊税:人踩一次就记住,agent 每个会话重踩
反过来也一样成立:没记录下来的东西,对 agent 就是不存在。人踩一次坑会长记性,agent 没有跨 session 的记忆,每次开工都是重新开始,同一个坑这个 session 踩完,下个 session 还会再踩。所以以前那种「大家都懂,不用写」的默契,现在得全部写下来。这个活不难,就是把你脑子里的东西 dump 成文件,但它现在是维护 repo 最重要的活之一。
我现在每个改动上线前会检查一件事:一个全新的 agent session,只给它这个仓库,它能不能得出正确的理解、做出正确的下一步改动?如果答案要靠我脑子里的某个东西才成立,那就说明还有认知没被记录下来,哪怕测试全绿。
仓库其实暴露了三个 API
仓库对 agent 暴露三个 API:导航 / 约束 / 校验
把仓库当接口看的话,它对 agent 暴露的其实是三个不同的 API。坏法不一样,设计的方法也不一样。
目录结构是导航 API。 agent 干活第一步是读路径,文件还没打开,目录结构已经告诉它东西在哪、归谁管、要改去哪改。比如 features/billing/engine/calculateInvoice.ts 这条路径,它自己就说清楚了:这是 billing 模块的纯逻辑,大概有单测,不应该 import React、不应该碰网络。这些 agent 不用打开文件就能推出来。
反过来,一个叫 Dashboard.tsx 的文件,里面又拉数据、又读 localStorage、又算定价、又渲染图表,路径就没给 agent 任何有用的信息,甚至是误导的:它说自己是个 dashboard 组件,实际上是半个应用。让 agent 去改一条定价规则,它得读两千行才能找到规则藏在哪,然后大概率把新规则也加进同一个文件,越搞越大。
lib、helpers、misc、common 这类文件夹,就是导航 API 里的坏 endpoint:不说明归属,不说明边界,最后全变成垃圾场,因为名字本身没给 agent 任何「别往这儿放」的理由。
契约和 import 边界是约束 API。 导航 API 告诉 agent 去哪,约束 API 告诉它到了之后能干什么:谁能 import 谁,哪些值是别人依赖的,数据跨边界的时候长什么样。
这里最容易出事的是隐性契约,也就是只存在于 tribal knowledge 里的那种。只在解析代码里出现过的 CSV 列顺序,十几个文件里当裸字符串写的事件名 'user.updated',三个功能都在读写、但没人管的 storage key。人能应付,因为有人记得。agent 改其中一个,根本不知道自己弄坏了另一个服务里的消费者,因为 repo 里没有任何东西告诉它这条边界存在。这块我单独写了一篇:把默契变成契约,是这套东西里杠杆最高的一招。
验证命令是校验 API。 最后,agent 得知道自己做完没有。人看 diff 大概能有数;agent 自己改自己验,会过拟合到自己的假设上,然后报告成功——它写代码的时候就带着「我是对的」这个信念,再读一遍当然还是觉得对。
所以要给它一个确定性的方式去问仓库「这真的做完了吗」:一条命令,把 typecheck、lint、测试、契约检查全跑一遍,返回一个 agent 说了不算的 exit code。「做完」从一种感觉变成一个 exit code。这个也单独写了一篇:agent 自己说做完了,不算数。
这跟给人设计是同一套
我花了挺久才接受一件事:上面这些其实一点都不新。我一直以为「给 AI agent 设计架构」会需要什么全新的东西,实际上没有。
对 agent 友好的结构,就是以前对新人友好的结构——一个记性很好、但完全没听过你们内部黑话的新人。纯逻辑和副作用分开,它就能不搭环境直接测;模块边界清楚,改动就是局部的、可回滚的;命名说实话;验证一条命令跑完。这些本来就知道是对的,只是以前可以偷懒:有资深的人在,缺的结构他脑子里能补上。
agent 补不上。所以以前被强团队默默消化掉的乱,现在会变成 bug,一个 session 一次,按 agent 干活的速度往外冒。
我觉得这算好消息:不用学一门新手艺,就是把本来就该做的事情做了。我在 别写代码了,学管 AI 吧 里写过,工作在从「自己写」变成「管那个会写的东西」,这篇是同一件事在 repo 这层的样子:你以前偷的懒,它全都会踩到。
所以现在我看仓库的方式很简单:它不是「你做过什么」的记录,它是 agent 干活时要对着编程的接口。接口干净,agent 就显得聪明——不是模型变强了,是它不用每次都把你的烂摊子重新推导一遍。模型是租来的,它按自己的节奏变强。接口是你自己的,这部分你能设计,而且越来越是决定 agent 能走多远的那部分。
这是"给 AI agent 盖一个能维护的仓库"系列的第一篇,一共四篇。下一篇讲 为什么每条边界都得变成契约。它跟 光有聪明的大脑还不够 那篇 runtime 的讨论是一套的,也跟 软件变成日抛品了 互为正反面——后者是从产品侧看同一场迁移。
