SuperAPI AI 工具集

codebase-design

编程与开发

查看项目

更新于 10-15 16:31

技能介绍

用途

Codebase Design 是一套用于设计“深模块”(deep modules)的共享词汇与原则。它的目标是在任何设计或重构代码的场合,提供一致的语言:让调用方获得杠杆(leverage),让维护者获得局部性(locality),让所有人获得可测试性(testability)。

所谓深模块,是指“大量行为藏在小接口之后,放在一个干净的接缝上,并可通过该接口进行测试”。

术语表

该技能要求严格使用以下术语,不要用 component、service、API、boundary 等词替代,因为“一致的语言就是全部意义所在”。

  • Module(模块):任何具有接口和实现的东西。刻意与规模无关:可以是一个函数、类、包,或跨层切片。避免使用 unit、component、service。
  • Interface(接口):调用方正确使用模块所必须知道的一切:类型签名,还包括不变量、顺序约束、错误模式、必需配置和性能特征。避免使用 API、signature(过于狭窄,仅指类型层面的表面)。
  • Implementation(实现):模块内部的东西,即其代码主体。与 Adapter 不同:一个东西可以是小适配器配大实现(如 Postgres 仓库),也可以是大适配器配小实现(如内存假实现)。当话题是接缝时用“adapter”,否则用“implementation”。
  • Depth(深度):接口处的杠杆。调用方(或测试)每学习一单位接口所能行使的行为量。当大量行为位于小接口之后时,模块是深的;当接口几乎与实现一样复杂时,模块是浅的。
  • Seam(接缝,Michael Feathers):一个可以在不编辑该处的情况下改变行为的地方;模块接口所在的位置。接缝放在哪里本身就是一个设计决策,与接缝后面放什么不同。避免使用 boundary(与 DDD 的限界上下文混淆)。
  • Adapter(适配器):在接缝处满足接口的具体事物。描述的是角色(填补哪个槽位),而非实质(内部是什么)。
  • Leverage(杠杆):调用方从深度中获得的东西。每学习一单位接口获得更多能力。一个实现可在 N 个调用点和 M 个测试中得到回报。
  • Locality(局部性):维护者从深度中获得的东西。变更、缺陷、知识和验证集中在一处,而不是散布到各调用方。修一次,处处修复。

深与浅

深模块 = 小接口 + 大量实现:接口方法少、参数简单,复杂逻辑被隐藏。

浅模块 = 大接口 + 少量实现(应避免):接口方法多、参数复杂,实现只是透传。

设计接口时,可以自问:

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

原则

  • 深度是接口的属性,不是实现的属性。深模块内部可以由小的、可 mock、可替换的部分组成,只是它们不属于接口。模块可以有内部接缝(对其实现私有,供自身测试使用),也可以有位于其接口处的外部接缝。
  • 删除测试:想象删除该模块。如果复杂度消失了,它就是个透传;如果复杂度在 N 个调用方处重新出现,它就是在发挥价值。
  • 接口即测试面:调用方和测试跨越同一个接缝。如果你想越过接口去测试,模块的形状很可能不对。
  • 一个适配器意味着假想接缝,两个适配器才意味着真实接缝。除非确实有东西在接缝两侧变化,否则不要引入接缝。

为可测试性而设计

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

  • 接受依赖,不要创建依赖。可测试写法:function processOrder(order, paymentGateway) {};难测试写法:function processOrder(order) { const gateway = new StripeGateway(); }。
  • 返回结果,不要产生副作用。可测试写法:function calculateDiscount(cart): Discount {};难测试写法:function applyDiscount(cart): void { cart.total -= discount; }。
  • 小表面面积:方法越少,需要的测试越少;参数越少,测试设置越简单。

关系

  • 一个 Module 恰好有一个 Interface(它呈现给调用方和测试的表面)。
  • Depth 是 Module 的属性,相对于其 Interface 衡量。
  • Seam 是 Module 的 Interface 所在之处。
  • Adapter 位于 Seam 处并满足 Interface。
  • Depth 为调用方产生 Leverage,为维护者产生 Locality。

被拒绝的框架

  • 把深度视为实现行数与接口行数的比率(Ousterhout):这会奖励填充实现。本技能改用“深度即杠杆”。
  • 把“Interface”理解为 TypeScript 的 interface 关键字或类的公共方法:过于狭窄;这里的接口包括调用方必须知道的一切事实。
  • “Boundary”:与 DDD 的限界上下文混淆。应说 seam 或 interface。

深入

  • 给定依赖深化一簇模块,参见 DEEPENING.md:依赖类别、接缝纪律,以及“替换而非分层”的测试方式。
  • 探索替代接口,参见 DESIGN-IT-TWICE.md:启动并行子代理,以几种截然不同的方式设计接口,然后按深度、局部性和接缝位置进行比较。

适用场景

当用户想要设计或改进某个模块的接口、寻找深化机会、决定接缝放在哪里,或对代码进行设计与重构时,可使用该技能。

限制

资料未提供该技能的安装方式、平台支持、收费或价格信息,暂未提供。