Skill 的整洁之道:软件架构原则在 AI 时代的新生

软件开发领域有两本经典:《代码整洁之道》和《架构整洁之道》。前者讲怎么写好一个函数,后者讲怎么组织一个系统。

AI 时代来了,编码交给 AI 了,《代码整洁之道》的直接受益者变成了 AI——在函数命名、结构分层这个层面,AI 的输出已经相当稳定,你只需要给它提示和约束。

但《架构整洁之道》的命运不同。它没有被 AI 取代,而是在一个新的层面上重新活了一遍

程序员的 scope 在扩张

程序员当然一直都在理解需求、拆解任务、设计方案——这些不是 AI 带来的新事物。

变化的是时间分配。编码执行这件事,以前占据了工作日的大部分;现在这部分被 AI 大幅压缩了,人的精力自然向其他环节倾斜。同样是”全栈”,以前意味着你要写前端也写后端,现在意味着你要覆盖从需求到上线的整条链路——不是因为你更厉害了,而是执行层有了 AI 来扛。

更关键的变化是形式。这不是简单地”用 AI 工具辅助”,而是一种 AI native 的形式:把每个环节封装成 skill,让 Claude Code 之类的 agent 作为执行主体,人退到设计和调度的位置。

这种模式下,”程序员”实际上在做的事情变了:

  • 不再只是写代码,而是设计执行代码的 agent 的工作方式
  • 不再只是管理函数和模块,而是管理 skill 的分工和协作
  • 不再只是思考技术架构,而是同时思考 agent 团队的架构

当 skill 多了之后,新的问题出现了:skill 怎么分工?skill 之间怎么协作?agent 怎么组织?这是一个新的层面的”架构”问题。

而这时候,《架构整洁之道》里那些原则,开始显示出它们真正的价值。

一个真实的例子

先从一个具体的问题说起。

我们在做一个 AI 辅助研发工作流的探索,其中有一个环节是提测前用例检查:在代码提测之前,用 AI 自动从测试用例管理平台拉取用例,然后对照代码实现逐条检查,发现遗漏或错误。

最初,这个功能被写成一个 skill,叫 code-checker。它的工作流程是:

  1. 从测试平台拉取用例列表和详情
  2. 按组件分组,生成检查批次
  3. 读取对应代码文件,逐批次检查
  4. 输出检查报告

这个 skill 能用,跑通了。

但当我们把这个功能迁移到更完整的工作流平台时,发现了一个问题:拉取用例这个操作,不只有代码检查这一个下游。测试报告生成、用例覆盖率统计、自动化测试执行——这些流程都需要先拉用例。

如果每个下游 skill 都自己实现一遍”拉用例”,代码重复是小事,真正的问题是:测试平台的接口一旦变化,所有 skill 都要改

所以我们做了一个拆分:

  • base/test-case-fetcher:只负责从测试平台拉取用例,输出结构化 JSON
  • code-checker:依赖 test-case-fetcher 的输出,只负责业务相关的代码检查逻辑

拆分之后,test-case-fetcher 成了一个通用的基础 skill,任何需要用例数据的流程都可以复用。code-checker 专注于业务知识,不再关心数据怎么来。

这个拆分,其实就是经典的单一职责原则

把架构原则翻译到 skill 层面

《架构整洁之道》里的 SOLID 原则,每一条都能直接映射到 skill 设计上。

单一职责:一个 skill 只做一件事

原则:一个模块应该只有一个变化的理由。

翻译到 skill:一个 skill 的职责应该单一,它的变化原因应该只有一个。

上面的例子里,原来的 code-checker 有两个变化的理由:测试平台接口变了(数据获取逻辑要改),或者检查规则变了(业务逻辑要改)。拆分之后,各自只有一个变化理由。

实践中容易犯的错误是把”方便”当”内聚”。”反正这两件事经常一起做,放一个 skill 里更方便”——这个逻辑在功能少的时候成立,skill 多了之后会变成维护噩梦。

开闭原则:base skill 是稳定的接口

原则:对扩展开放,对修改关闭。

翻译到 skill:base skill 定义稳定的输出格式(接口),上层 skill 通过扩展来满足新需求,而不是修改 base skill。

test-case-fetcher 的输出格式一旦稳定下来,就不应该随意改变——因为所有依赖它的下游 skill 都在依赖这个格式。新的需求应该通过新增字段(向后兼容)或者新建 skill 来满足,而不是修改现有输出格式。

这和 API 设计的道理完全一样。base skill 就是你的内部 API。

依赖倒置:业务 skill 依赖协议,不依赖具体实现

原则:高层模块不应该依赖低层模块,两者都应该依赖抽象。

翻译到 skill:业务 skill 不应该关心数据怎么来、工具怎么调用,它只关心输入数据的格式和输出的结果格式。

这里的”抽象”在 skill 场景里,对应的是数据格式约定——文件路径、JSON schema、字段命名。code-checker 不关心用例是通过 HTTP 接口拉的还是从本地缓存读的,它只关心 .check-work/cases.json 里有什么。这个文件路径和格式约定,就是两个 skill 之间共同依赖的”接口”。

一旦建立了这个约定,你可以随时替换 test-case-fetcher 的实现——换个接口、改个认证方式、加个缓存——下游 skill 完全不需要改。

精简上下文:只传 skill 真正需要的信息

SOLID 里有接口隔离原则(ISP):不应该强迫客户端依赖它不使用的接口。在 skill 设计里,这个精神体现在另一个维度:传给 skill 的上下文要精准,不要把整个项目背景都塞进去。

这在 AI agent 的场景里格外重要。上下文越大,token 消耗越高,更关键的是,AI 的注意力是有限的,无关信息会稀释它对关键信息的关注。

一个好的 skill 设计应该明确说明:它需要什么输入,产出什么输出,中间不依赖任何隐式的环境状态。这样的 skill 是可以独立测试的,也是可以在不同项目里复用的。

agent 团队也是团队

Conway’s Law 说的是:系统的架构,往往是设计这个系统的组织的沟通结构的镜像。

原话来自 Melvin Conway,大意是:如果你有四个团队来开发一个编译器,你会得到一个四遍的编译器。这个规律成立,是因为跨组织边界的沟通成本高,接口自然沿着摩擦最小的地方形成——而那个地方通常就是组织边界。

agent 之间没有这种沟通摩擦,所以 Conway’s Law 的原始机制在这里并不直接适用。但有一个相关的工程实践,叫 Inverse Conway Maneuver(逆康威操作):不是让架构被动地跟随组织,而是主动设计组织结构,来驱动你想要的系统架构

这个思路在 agent 设计里非常有用。

Anthropic 工程师在设计多 agent 编程系统时,把分工设计成 Planner-Generator-Evaluator 三角(见这篇博客):Planner 把模糊需求扩展成具体规格,Generator 按 sprint 逐功能实现,Evaluator 用 Playwright 点击运行中的应用逐条验证。这三个 agent 的分工,直接对应了传统团队里的产品经理、开发工程师、测试工程师。

这不是偶然。agent 团队的分工方式,决定了系统的架构形态——因为设计 agent 团队的人是人类,人类习惯用自己熟悉的组织模型来切分职责。想要什么样的系统架构,就先想清楚你要组建一个什么样的 agent 团队。

技术架构服务于组织架构

有一个观察我觉得很准确:技术架构是服务于组织架构的,组织架构怎么设计,技术架构就会往那个方向演化。

换句话说,真正的架构决策权在组织设计者手里,而不只在技术人员手里。

这个逻辑在 AI 时代有了新的含义。

传统软件开发里,组织架构的变化是慢的——重组一个团队需要几个月,调整汇报关系需要走流程,跨部门协作需要建立正式的接口协议。技术架构跟着组织架构走,但有相当大的滞后。

AI agent 团队的组织架构变化是快的。你今天可以让 agent 扮演 QA,明天可以让它扮演架构师,后天可以给它加一个新的专业角色。组织结构的调整成本接近零。

这意味着架构决策的频率和粒度都在提高。你不再是每隔几年做一次大的架构调整,而是每隔几天就在做小的 agent 团队组织决策。

这对”程序员”的要求变了:不只是会写代码,还要会设计组织——哪些工作应该由哪类 agent 负责,agent 之间的接口怎么定义,职责边界在哪里。

这是一种新的架构思维,但它的底层原则和《架构整洁之道》是一脉相承的。

几个具体的 skill 架构范式

把上面的原则落地,有几个在实践中有用的模式。

分层架构

把 skill 分成三层:

1
2
3
4
5
业务 skill(知道"做什么",不知道"怎么做"

通用 skill(知道"怎么做",不知道"为什么做"

工具 skill(只是工具的包装,没有业务知识)

code-checker 是业务 skill,它知道”要检查用例对应的代码实现”,但不关心用例怎么拉取。test-case-fetcher 是通用 skill,它知道”怎么从测试平台拉数据”,但不关心数据被用来做什么。底层的 HTTP 请求、认证处理,是工具层的事。

这个分层的好处是:业务逻辑的变化不会影响通用层,通用层的优化不需要改业务层。

明确的交接协议

skill 之间通过文件或结构化数据交接,而不是隐式的环境状态。

test-case-fetcher 输出到 .check-work/cases.json.check-work/case-details/,这是一个明确的约定。code-checker 从这个位置读数据,不关心数据是怎么生成的。

明确的交接协议有几个好处:可以单独测试每个 skill,可以缓存中间结果,可以替换任意一个 skill 的实现而不影响其他 skill。

生成与验证分离

这是 Anthropic 工程师总结的核心模式,也是 SOLID 原则在 AI 场景里最重要的体现。

让 AI 自我评估刚生成的内容,几乎总是给好评。把生成和验证分给两个独立的 agent,用不同的 prompt 调教,效果要好得多。

这不只是工程技巧,背后是职责分离的原则:生成 skill 的职责是”尽可能生成好的结果”,验证 skill 的职责是”尽可能发现问题”。这两个目标天然有张力,放在同一个 skill 里会相互妥协。

渐进式细化

不要一开始就把 skill 设计得很细。先做一个能跑通的粗粒度 skill,在实际使用中发现哪些部分需要复用、哪些部分变化频繁,再做有针对性的拆分。

上面的例子就是这样来的:先有一个能跑通的 code-checker,在迁移过程中发现拉取用例这个部分需要复用,才做了拆分。过早的细化往往是错的细化。

脚本与 AI 的边界

skill 内部同样需要分工:结果确定、逻辑固定的步骤交给脚本,需要理解和判断的步骤交给 AI。

判断准则很简单——问一句”这个步骤有唯一正确答案吗?”有的话用脚本,没有的话用 AI。构建产物、拉取数据、读写文件,答案确定,脚本做;理解代码逻辑、判断设计质量、推断边界场景,答案模糊,AI 做。

混淆的代价是真实的。用 AI 做确定性操作,token 浪费,结果还不如一行脚本稳定。用脚本处理模糊判断,规则越写越长,最后发现边界情况永远枚举不完,只能靠人工兜底——这本身就是信号:这个步骤该交给 AI。

这不是新知识,是旧知识的新应用

我在想这些问题的时候,有一种奇怪的感觉:这些原则都不新,它们在《架构整洁之道》里写得很清楚。但当我把它们应用到 skill 设计上的时候,感觉像是第一次真正理解了它们。

也许是因为,代码层面的架构问题通常很抽象——“高内聚低耦合”听起来像废话,直到你真的在一个大系统里被耦合折磨过。

skill 层面的架构问题更具体,更直接。一个 skill 做了两件事,你会在两周后感受到痛苦:测试平台接口变了,你发现要改三个 skill。一个 skill 的上下文太大,你会立刻看到 token 消耗翻倍。

这种即时反馈,让架构原则从抽象变成了具体。

AI 时代没有废掉《架构整洁之道》,它只是把战场从函数和模块移到了 skill 和 agent 团队。

原则还是那些原则。只是现在,你有机会以更快的速度、更低的成本,在一个新的层面上把它们应用一遍。