SuperAPI AI 工具集

codebase-design

编程与开发

查看项目

更新于 10-16 09:12

技能介绍

用途

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

核心概念

深模块指“小接口 + 大量实现”:少量方法、简单参数,复杂逻辑隐藏在内部。浅模块则相反,接口几乎和实现一样复杂,应当避免。

设计接口时可以自问:能否减少方法数量?能否简化参数?能否把更多复杂度藏进内部?

术语表

资料要求严格使用以下术语,不要用 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) {} 可测试,而内部 new StripeGateway() 则难以测试。
  • 返回结果,不要产生副作用。例如 function calculateDiscount(cart): Discount {} 可测试,而直接修改 cart.total 则难以测试。
  • 缩小接口面积:方法越少,需要的测试越少;参数越少,测试设置越简单。

关系

一个 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,启动并行子代理以几种截然不同的方式设计接口,然后按深度、局部性和接缝位置进行比较。