mcpField
C
codebase-design

codebase-design

github.com/leandrocfe开发41102 次安装13 周前更新
代码仓库

关于 codebase-design

面向编码 Agent 的设计技能,将深模块原则用于代码设计与重构。它强调用小接口隐藏大量行为,提升调用方的杠杆效益和维护局部性,并让测试通过相同的接缝进行。

主要功能

  1. 01以小接口设计深模块
  2. 02区分模块、接口与实现
  3. 03审慎选择接缝和适配器
  4. 04用删除测试评估模块
  5. 05设计便于测试的接口
  6. 06比较不同的接口设计

适用场景

  1. 01围绕清晰接缝重构代码
  2. 02减少调用方需要面对的复杂度
  3. 03判断模块是否只是透传
  4. 04通过接口设计提升可测试性

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/leandrocfe/skills --skill codebase-design

安装后在 Claude Code 中输入/codebase-design确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

DEEPENING.md
DESIGN-IT-TWICE.md
agents/
openai.yaml

详细介绍

代码库设计

设计深模块(deep modules):在一个简洁的接口后封装大量行为,并放置在清晰的 seam 上,可通过该接口进行测试。设计或重构代码时,始终使用这套术语和原则。目标是为调用者提供 leverage,为维护者提供 locality,并让所有人都能轻松测试。

术语表

请严格使用这些术语——不要用「component」「service」「API」或「boundary」替代。术语一致是首要目标。

Module — 任何具有接口和实现的事物。刻意不限定规模:可以是函数、类、包,也可以是横跨多个层的 slice。避免使用:unit、component、service。

Interface — 调用者正确使用模块所需了解的一切:类型签名,以及不变量、顺序约束、错误模式、所需配置和性能特征。避免使用:API、signature(范围过窄——仅指类型表面)。

Implementation — 模块内部的内容,即其代码体。它不同于 Adapter:一个事物可以是实现很大的小型 adapter(Postgres 仓库),也可以是实现很小的大型 adapter(内存中的 fake)。讨论 seam 时使用「adapter」;其他情况下使用「implementation」。

Depth — 接口提供的 leverage:调用者(或测试)通过每单位需要学习的接口所能运用的行为量。当大量行为封装在简洁的接口后,模块就是深的;当接口几乎与实现一样复杂时,模块就是浅的。

Seam (Michael Feathers) — 一个可以在不修改该处代码的情况下改变行为的位置;也就是模块接口所在的位置。seam 的放置位置本身就是一项独立的设计决策,与 seam 后封装的内容无关。避免使用:boundary(容易与 DDD 中的 bounded context 混淆)。

Adapter — 在 seam 上满足某个接口的具体事物。描述的是角色(填补哪个槽位),而非实质(内部有什么)。

Leverage — 调用者从 depth 中获得的收益:每单位所学接口所能获得的更多能力。一项实现可以在 N 个调用点和 M 个测试中带来收益。

Locality — 维护者从 depth 中获得的收益:更改、bug、知识和验证都集中在一个地方,而不是散布在调用者中。修复一次,处处修复。

深模块与浅模块

深模块 = 简洁的接口 + 大量实现:

┌─────────────────────┐
│   Small Interface   │  ← Poucos métodos, parâmetros simples
├─────────────────────┤
│                     │
│  Deep Implementation│  ← Lógica complexa escondida
│                     │
└─────────────────────┘

浅模块 = 庞大的接口 + 少量实现(应避免):

┌─────────────────────────────────┐
│       Large Interface           │  ← Muitos métodos, parâmetros complexos
├─────────────────────────────────┤
│  Thin Implementation            │  ← Apenas repassa
└─────────────────────────────────┘

设计接口时,请思考:

  • 我能减少方法数量吗?
  • 我能简化参数吗?
  • 我能在内部隐藏更多复杂性吗?

原则

  • Depth 是接口的属性,而非实现的属性。 深模块的内部可以由小型、可模拟且可替换的部分组成——它们只是不属于接口。模块可以拥有 internal seams(仅供其实现内部使用的私有 seam,由模块自身的测试使用),也可以拥有接口上的 external seam。
  • 删除测试。 想象一下删除这个模块。如果复杂性随之消失,它只是一个透传层。如果复杂性在 N 个调用者中再次出现,那么它就发挥了作用。
  • 接口就是测试面。 调用者和测试会跨越同一个 seam。如果你想在接口之外测试,这个模块的设计可能就不合适。
  • 一个 adapter 意味着一个假设的 seam。两个 adapter 意味着一个真实的 seam。 除非确实有事物会在 seam 上变化,否则不要引入 seam。

面向可测试性进行设计

优秀的接口让测试变得自然而然:

  1. 接受依赖,而不是创建依赖。

    // Testável
    function processOrder(order, paymentGateway) {}
    
    // Difícil de testar
    function processOrder(order) {
      const gateway = new StripeGateway();
    }
    
  2. 返回结果,而不是产生副作用。

    // Testável
    function calculateDiscount(cart): Discount {}
    
    // Difícil de testar
    function applyDiscount(cart): void {
      cart.total -= discount;
    }
    
  3. 保持表面精简。 方法越少,需要的测试就越少。参数越少,测试准备就越简单。

关系

  • 一个模块恰好有一个接口(它呈现给调用方和测试的表面)。
  • 深度是模块的属性,以其接口为衡量基准。
  • 接缝是模块的接口所在之处。
  • 适配器位于接缝上,并满足接口。
  • 深度为调用方带来杠杆效应,为维护者带来局部性。

不采用的表述

  • 将深度视为实现代码行数与接口代码行数的比率(Ousterhout):这会鼓励膨胀实现。我们将深度视为杠杆效应,而非比率。
  • 将“接口”视为 TypeScript 的 interface 关键字或类的公共方法:范围太窄——这里的接口包括调用方需要了解的所有信息。
  • “边界”:这个词与 DDD 中的有界上下文含义重叠。请使用接缝或接口。

深入了解

  • 结合依赖关系加深集群——参见 DEEPENING.md:依赖类别、接缝规范,以及测试中的“替换而非叠加”策略。
  • 探索替代接口——参见 DESIGN-IT-TWICE.md:并行启动多个子 Agent,以截然不同的方式设计接口,然后从深度、局部性和接缝位置等方面进行比较。

评论

支持 Markdown