mcpField
C
codebase-design

codebase-design

github.com/niekcandaele开发649 次安装6 天前更新
代码仓库

关于 codebase-design

codebase-design 是面向开发者的代码设计技能,适用于设计或重构代码。它强调以精简接口封装丰富行为,审慎选择模块接缝,并通过接口进行测试。

主要功能

  1. 01设计深模块
  2. 02精简接口复杂度
  3. 03选择模块接缝
  4. 04通过接口测试
  5. 05依赖注入与结果导向设计

适用场景

  1. 01围绕清晰模块重构代码
  2. 02简化调用方使用的接口
  3. 03提升行为的可测试性

安装

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

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

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

附带文件DEEPENING.mdINTERFACE-DESIGN.md

详细介绍

代码库设计

设计深模块:在简洁的接口后封装大量行为,将其放在清晰的接缝处,并可通过该接口进行测试。设计或重构代码时,应始终采用这套语言和原则。目标是让调用方获得更多杠杆效益,让维护者拥有更好的局部性,并让所有人都更容易测试。

术语表

请准确使用这些术语——不要用“组件”“服务”“API”或“边界”替代。统一用语正是本意。

模块 任何具有接口和实现的事物。刻意不限定规模——同样适用于函数、类、包或跨层切片。 避免使用:单元、组件、服务。

接口 调用方正确使用模块所必须了解的一切。包括类型签名,也包括不变量、顺序约束、错误模式、必需的配置以及性能特征。 避免使用:API、签名(范围太窄——这两个词仅指类型层面的表面)。

实现 模块内部的内容——也就是代码主体。它与适配器不同:一个东西可以是实现很大的小型适配器(Postgres 仓库),也可以是实现很小的大型适配器(内存中的假对象)。当讨论接缝时,使用“适配器”;其他情况下使用“实现”。

深度 接口所带来的杠杆效益——调用方(或测试)每学习一个单位的接口,就能使用多少行为。大量行为藏在小型接口后面的模块称为深模块。接口几乎和实现一样复杂的模块称为浅模块。

接缝 (源自 Michael Feathers) 可以在不修改该位置代码的情况下改变行为的地方。也就是模块接口所在的位置。决定把接缝放在哪里,是一项独立于决定在接缝后封装什么的设计决策。 避免使用:边界(此词已被 DDD 中的有界上下文占用)。

适配器 在接缝处满足某个接口的具体事物。描述的是角色(填补什么位置),而不是实质(内部包含什么)。

杠杆效益 调用方从深度中获得的收益。每单位需要学习的接口都能带来更多能力。一份实现可以惠及 N 个调用点和 M 个测试。

局部性 维护者从深度中获得的收益。变更、漏洞、知识和验证都集中在一个地方,而不是分散到各个调用方。修复一次,处处生效。

深模块与浅模块

深模块 = 小型接口 + 大量实现:

┌─────────────────────┐
│   Small Interface   │  ← Few methods, simple params
├─────────────────────┤
│                     │
│  Deep Implementation│  ← Complex logic hidden
│                     │
└─────────────────────┘

浅模块 = 大型接口 + 少量实现:

┌─────────────────────────────────┐
│       Large Interface           │  ← Many methods, complex params
├─────────────────────────────────┤
│  Thin Implementation            │  ← Just passes through
└─────────────────────────────────┘

设计接口时,可以问自己:

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

原则

  • 深度是接口的属性,而非实现的属性。 深模块内部可以由小型、可模拟、可替换的部分组成——只要它们不属于接口。模块既可以在内部拥有内部接缝(仅供实现内部使用,由其自身的测试使用),也可以在接口处拥有外部接缝。
  • 删除测试。 想象删除这个模块。如果复杂性随之消失,那么该模块并未隐藏任何东西(它只是透传)。如果复杂性重新出现在 N 个调用方中,那么该模块就发挥了价值。
  • 接口就是测试面。 调用方和测试都要穿过同一个接缝。如果你想在接口之外进行测试,这个模块的形态可能就不合适。
  • 一个适配器意味着接缝尚属假设;两个适配器意味着接缝确实存在。 除非接缝两侧真的存在差异,否则不要引入接缝。

为可测试性而设计

好的接口能让测试变得自然:

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

    // Testable
    function processOrder(order, paymentGateway) {}
    
    // Hard to test
    function processOrder(order) {
      const gateway = new StripeGateway();
    }
    
  2. 返回结果,而不是产生副作用。

    // Testable
    function calculateDiscount(cart): Discount {}
    
    // Hard to test
    function applyDiscount(cart): void {
      cart.total -= discount;
    }
    
  3. 缩小表面积。 方法越少,需要的测试就越少。参数越少,测试设置就越简单。

关系

  • 一个 Module 恰好有一个 Interface(它向调用方和测试呈现的表面)。
  • Depth 是 Module 的属性,以其 Interface 为基准进行衡量。
  • Seam 是 Module 的 Interface 所在之处。
  • Adapter 位于 Seam,并满足 Interface。
  • Depth 为调用方带来 Leverage,为维护者带来 Locality。

不采用的表述

  • 将 Depth 定义为实现代码行数与接口代码行数之比(Ousterhout):这种定义会鼓励堆砌实现代码。我们采用的是将 Depth 定义为杠杆效应。
  • 将 “Interface” 理解为 TypeScript 的 interface 关键字或类的公共方法:范围太窄——这里的 Interface 包括调用方必须了解的所有事实。
  • “Boundary”:这个词与 DDD 的限界上下文概念重叠。请使用 seam 或 interface。

深入了解

  • 根据依赖关系深化集群——请参阅 DEEPENING.md:依赖类别、seam 规范,以及替换而非叠加的测试方式。
  • 探索不同的接口设计方案——请参阅 INTERFACE-DESIGN.md:启动多个并行的子 Agent,以多种截然不同的方式设计接口,然后从深度、局部性和 seam 位置等方面进行比较。

改编自 mattpocock/skills,MIT © 2026 Matt Pocock。复制此文件时请保留此行。

评论

支持 Markdown