AI 一进老代码库就迷路?两个 Skill 理清它

让 AI 写代码,真正的难关,常出在它第一次踏进一个已有的代码库。这库往往累积已久,结构早已混乱,它就在文件堆里打转。Matt Pocock 公开了两个 skill,正好治这个毛病。
  • codebase-design

    ,管代码怎么分块,做的是深模块。

  • domain-modeling

    ,管词怎么定准,定的是领域术语。

图片


一、没有记忆的 AI 同事,爱迷路

团队来了一位新同事,是一位 AI 助手。面试时它应答如流,什么需求都接得住。可真到了上岗第一天,把它领进项目,它站在原地不动了。

让它改一个取消订单的 bug。它翻了十分钟,回头问,取消逻辑在哪个文件?指给它。它又翻,回头再问,这里改了,会不会影响别处?耐着性子答。第三次,它问的还是同一件事。

原因不复杂。AI 助手没有长期记忆,每次新对话,它都等于第一天上班的新人。代码库结构早已混乱,文件互相纠缠,它就在里面打转。

Matt Pocock 在 GitHub 上公开了一整套给 AI 用的 skill。其中有两个,专门治这件事。

  • codebase-design

    ,管代码怎么分块。它讲的是深模块(deep module),接口小,实现大。接口越窄,调用者要记的东西越少;实现越厚,复杂细节越不外泄。

  • domain-modeling

    ,管词怎么定准。代码里的「账户」,有人指客户,有人指登录用户,各说各话。它把这些歧义逐一定准,写进项目术语表(CONTEXT.md),让每个词只有一个确定的含义。

一个给代码画边界,一个给概念编词典。

先把话说在前面,这两个 skill 都不直接改代码。codebase-design 是一把纯尺子,什么都不产出,只把「深不深」「接口该收多窄」的标准讲清楚,供别的 skill 查。improve-codebase-architecture 扫完代码库,拿它挑该加深的模块;tdd 在接口没定时,拿它定接口。开发者自己重构时想按深模块来,也能直接调它给建议。domain-modeling 不一样,它在项目里写两个文件,CONTEXT.md 术语表和 docs/adr/ 下的决策记录,但同样不碰业务代码。

图片


二、先认识「深模块」

这两个 skill 背后,站着同一个词,深模块(deep module)。

这个词不算新。斯坦福的 John Ousterhout 讲了它很多年,2018 年写进了《A Philosophy of Software Design》(中译名《软件设计的哲学》)。

任何一个模块,都能拆成两半。

接口(interface),调用者为了正确使用它,必须知道的一切。

实现(implementation),藏在接口后面,真正干活的代码。

深模块和浅模块的差别,一句话说不清,看这组对比就明白。

  • 深模块

    ,接口小,实现大。像冰山,水面只露出一小角,底下压着一大块。

  • 浅模块

    ,接口大得吓人,实现薄得可怜。像包装纸,外面印满注意事项,拆开一看,里面几乎没干活,只是把参数原样转给了下一个函数。

深模块带来两个好处,作者各起了一个名字。

杠杆(leverage),调用者学会一小块接口,就能撬动背后一大片行为。

局部性(locality),相关的改动、缺陷、知识、验证,都收在同一个地方,改一次,处处生效。

图片


三、管代码的 codebase-design,先立一条规矩

第一个 skill,codebase-design,管代码怎么分块。它最较真的一条规矩,是用词不许随便换。

  • module

     不能叫 component,也不能叫 service。

  • interface

     不能叫 API,也不能叫 signature。

  • seam

     不能叫 boundary。

为什么这么死板?原作者把理由写在文档第一段,只有一句。

Consistent language is the whole point. 一致的语言,才是这件事的全部意义。

道理很直白。让 AI 和开发者之间、也让 AI 的每一次对话之间,都用同一套词指代同一个概念,这套方法才能生效。词一乱,沟通就失真。

这套词里,最反直觉的是 interface。它的范围,比 TypeScript 里那个 interface 关键字宽得多,也远不止一组公开方法。

它指的是,调用者想正确使用一个模块,必须记住的全部内容。

类型签名只是其中一小块,另有这些。

  • 不变量(invariant,程序运行中始终不能打破的规矩,比如购物车商品数量永远不能是负数)

  • 调用顺序

  • 会抛什么错

  • 需要哪些配置

  • 性能怎么样

另有一个容易被忽略的词,seam(接缝)。它来自 Michael Feathers 在《修改代码的艺术》一书里提出的概念,指程序里「不用改那一处代码,就能改变它行为」的位置。

拿 processOrder(order, paymentGateway) 来说,paymentGateway 就是一条缝。想让它真扣钱,传真实的支付网关进去;测试时想让它别真扣,传个假的进去。行为变了,processOrder 自己的代码一个字没动。

adapter(适配器),就是坐在某条 seam 上、去满足某个接口的那个具体实现。还是上面那条缝,StripeGateway 真连银行扣钱,FakeGateway 假装扣了只做记录,它俩都满足「能扣款」这个接口,就是两个 adapter。

图片


四、怎么判断一个模块够不够深

设计接口时,可以反复问自己三个问题。

  • 方法能不能再少几个?

  • 参数能不能再做简单点?

  • 复杂度能不能再往里藏一点?

另有一个更实用的招,叫删除测试。想象把这个模块整个删掉。

这里有个小故事。公司里有两位员工。

  • 一位是传声筒,他的工作是把别人说的话,原样转给下一个人。把他裁掉,工作照常运转,什么都不会乱。

  • 另一位是老师傅,几十年的经验都装在他脑子里,他一走,一大片活儿都没人会干。

删除测试就是这一套。

  • 删掉一个模块,如果它的复杂度也跟着消失,那它就是个传声筒,删掉正好。

  • 如果它的复杂度会在 N 个调用者那里重新长出来,那它一直在扛真实的分量,值得留。

这套判断的好处在于,它衡量的标准只有一条,调用者要背多少东西。一个模块内部尽可以堆满小零件,但只要它们不暴露到接口上,调用者只学一个入口,就能撬动背后一大片行为。

图片


五、深模块天生好测

深模块的另一面,是它天然好测。办法有三条。

第一条,接受依赖,别自己 new。

这里讲个修水管的故事。水管工来修水管,如果水管是能拧下来的接头,他带着自己的工具就能工作,换个接头就行。如果水管是焊死在墙里的,想修就得拆墙。

代码同理。processOrder(order, paymentGateway) 这种写法好测,传入一个假的支付网关就能测。processOrder(order) 这种写法,函数内部自己 new 一个 StripeGateway,测试时根本换不掉它,几乎没法测。

第二条,返回结果,别改现场。

calculateDiscount 返回一个值,测试只看返回值对不对。applyDiscount 直接往购物车里减钱,测完还得把现场还原回去。

第三条,表面积小。

方法少了,要写的测试就少;参数少了,搭测试环境也省力。

seam 是好东西,却不可滥用。原作者补过一句提醒,一个 adapter 只能算「假想的 seam」,两个 adapter 才算「真正的 seam」。意思是,不要为一个还没发生的变化,提前抽个接口出来放着。

图片


六、Design It Twice,让三个 agent 比一比

这个 skill 最深的一招,叫 Design It Twice(设计两次)。名字同样来自 Ousterhout。逻辑很朴素,拿出的第一个设计,多半仍有改进空间,所以不要只做一个方案。

做法像一场小型比赛。同时放三个子 agent 出去,每个给一个相反的约束。

  • 第一个,尽力压缩接口,目标只留一两个入口。

  • 第二个,尽力追求灵活,尽量多兼容几种用法。

  • 第三个,只照顾最常出现的那类调用者,把默认场景做到最顺手。

三个设计摆在一起,从三个角度比较,深度够不够、局部性聚不聚、seam 放在哪里最合适。最后给出推荐,而且敢下结论,不模棱两可。

图片


七、管词的 domain-modeling

结构这半边交给 codebase-design。另一半,词,交给 domain-modeling。

它做的事很具体,而且很较真。

这里有个常见的小故事。团队里两个人聊「账户」。

  • 一个人说的是客户账户。

  • 另一个人说的是登录用户。

两人聊了许久,都以为对方明白自己的意思,结果做出来的功能对不上。

domain-modeling 专门治这个。当用到的词,和 CONTEXT.md 里已有的定义冲突,它当场指出。当提到 account(账户),它追问指的是 Customer(客户)还是 User(用户)。当描述某个功能怎么运作,它去翻代码,确认实情到底是不是这样。

先解释一下 CONTEXT.md。它是放在项目里的一份文档,记录这个项目的术语、约定和背景,让 AI 每次进门,都能快速搞清楚「这里的词是什么意思、有什么规矩」。

词一旦定准,就地写进 CONTEXT.md。这份文件是纯术语表,不要往里写实现细节,也不要把规格说明写进去。

建 ADR 更要克制。ADR 是 Architecture Decision Record(架构决策记录),用来记那些重要、又不那么容易看懂的架构决定。三条同时满足才动笔。

  • 这个决定难以逆转,改起来代价大。

  • 不记下来,后来读代码的人会纳闷为什么这么干。

  • 它真是权衡出来的,存在别的选项,而挑了这一个。

三条缺一条,就跳过。

图片


八、先定词,再拆块

真正用起来,这两个 skill 是一条线。先让 domain-modeling 把词定准,术语写入 CONTEXT.md;再让 codebase-design 照着这些词去分块、开 seam、定接口。一个定语义,一个定结构。

以下单模块为例,走一遍。

词先定下来。

  • order

     是订单。

  • customer

     是客户。

  • cancellation

     是取消。

结构再跟上,把取消逻辑藏进一个深模块,接口只留一个 cancel(orderId, reason),实现里那堆校验、退款、通知,全藏在接口后面。

结果就是,调用者只学一个方法,测试只打一个接口,将来改取消逻辑,也只改一个地方。

图片


九、它俩在整套流程里的位置

这两个 skill 有点特别。它们不占主流程上某个固定环节,被别的 skill 按需拉出来用。

Matt 的主流程,一条线跑下来。

  • grill-with-docs 把问题问清楚。

  • to-spec 写规格。

  • to-tickets 拆任务。

  • implement 施工。

  • code-review 收尾。

domain-modeling 主要跟在 grill-with-docs 后面。grill-with-docs 的做法,就是跑一轮提问,同时用上 domain-modeling 这套方法。它在对话途中把术语磨准,就地更新 CONTEXT.md,遇到难以逆转的决定才建 ADR。

codebase-design 主要被 improve-codebase-architecture 请出。improve-codebase-architecture 扫描代码库,找出还能往深做的模块,把候选写进一份 HTML 报告。tdd 在接口形状还没定的时候,也把 codebase-design 当参考词典查。

所以,这两个 skill,是整条流程里共用的「词典」和「尺子」。一个定词,一个定结构。词不乱,块不散,AI 从进门到交付,每一步才走得清楚。

图片


小结

回到开头那个新来的 AI 同事。

第一天,它站在代码库门口打转,反复问取消逻辑在哪个文件。递给它两样东西,一本词典(CONTEXT.md),一把尺子(深模块)。

第二天,把它领进同一个项目,让它改一下取消逻辑。它没再问从哪开始。

让 AI 帮你写深模块,归结起来一句话,小接口藏大行为,术语先定准,再分块。

  • 如果代码里接口越写越多、参数越传越长,先上 codebase-design。

  • 如果团队里同一个词各说各话,评审总在概念上纠缠,先上 domain-modeling。

两个都装上,一个管结构,一个管词。值得收藏,下次让 AI 进代码库之前,先看一眼。

图片


附录 术语对照与来源说明

术语

说明

skill

交给 AI 的一份操作规程

深模块(deep module)

接口小、实现大的模块,像冰山

浅模块

接口大、实现薄的模块,像包装纸

接口(interface)

调用者为了正确使用模块必须知道的一切

实现(implementation)

藏在接口后面、真正干活的代码

杠杆(leverage)

学会一小块接口,就能撬动一大片行为

局部性(locality)

相关改动、缺陷、知识都收在同一个地方

seam(接缝)

不用改那一处代码、就能改变其行为的位置

adapter(适配器)

坐在 seam 上、满足某个接口的具体实现

删除测试

想象删掉模块,看复杂度是否跟着消失

Design It Twice

一次出三个反向约束的设计,再比较取舍

CONTEXT.md

记录项目术语、约定和背景的文档

ADR

架构决策记录,记那些难以逆转的重要决定

素材来源 本文基于 Matt Pocock 的 skills 仓库中 codebase-design 与 domain-modeling 两个 skill 的源码整理,为独立写作,非原文翻译。

#深模块 #代码设计 #AgentSkill #ClaudeCode #MattPocock #接口设计 #领域建模 #CONTEXTmd #ADR #可测试性

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐