产品研发规范库

设计示例与反例

以下是为本规范编写的工程示例,不是书中案例。用于边界判断,不能把示例里的技术选型当作所有项目的要求。

示例一:领取权益,封装调用协议

需求:用户领取一项权益,不能重复发放,也不能超发。

容易泄漏细节的接口让每个调用方依次调用:

findClaim → checkQuota → decrementQuota → insertClaim

调用方必须理解事务、并发和失败后的状态。只把这些方法移动进一个 ClaimManager,仍然没有减少调用者需要掌握的知识。

可考虑的抽象是:

claimBenefit(userId, benefitId) → 已领取的凭证或明确的业务失败

在本例业务契约下,模块负责原子地处理额度与领取记录;同一用户重复领取返回已有凭证,不再次扣减。实际实现依据项目的唯一约束、事务或其他一致性机制,不能仅凭这个接口名称宣称具备并发安全性。

验证首次领取、重复领取、额度不足,以及并发条件下不超发、不重复扣减。资格判断如果依赖独立业务政策,应由明确的业务边界负责,不能偷偷嵌入通用存储组件。

示例二:导入配置,按知识划分

两个模块分别读取和写入配置,都知道字段别名、版本规则及转义方式。修改格式时必须同时改两边,这是知识分散的证据。

把双向转换规则放在配置编解码模块,由它统一解释外部格式;文件读取和写入可以继续由独立 I/O 能力完成。运行时使用清晰的配置模型,无需处理转义细节。

验证合法样本、非法格式和项目要求支持的版本。判断收益看格式变更涉及的位置与运行时的使用方式,不看是否减少文件数。

示例三:短方法是否应该删除

一个业务服务只是原样转发存储方法,需要检查它是否提供实际边界。以下情形不能仅因代码短就删除:

  • 方法是明确的事务入口。
  • 包装层执行权限检查或隔离第三方协议。
  • 项目约定依赖该入口,或已发布接口处在明确的兼容窗口。

反过来,“以后也许会用到”本身不足以证明新增包装层的价值。优先使用现有结构,只有能说明当前职责时才增加层。

示例四:注释表达契约

对 expiresAt = issuedAt + validitySeconds 写“计算过期时间”几乎没有增加信息。

更有价值的注释可能是:

有效期从首次签发时刻计算,重复领取不续期;时间采用 UTC 秒级时间戳。

该注释说明调用方和维护者必须知道的规则。只有实现与业务证据确实支持时才能写入;不允许为了让文档完整而发明契约。

示例五:抽象增量与过度设计的分界

需求是让现有导出功能支持一个新的日期筛选条件。如果查询边界清晰,只需扩展现有参数并覆盖边界用例,不必新增查询接口、导出插件注册表或通用报表引擎。

如果三个实际导出入口分别维护同一套日期范围解释规则,则可以集中这项规则,并让现有入口使用它;输出格式等无关差异仍留在各自边界。

验收同时检查用户得到正确结果、日期规则有一致语义。不要以“抽象还没完善”为由只交付空接口;也不要以“先让功能跑起来”为由继续复制已经明确重复的规则。