Hyrum 定律 = API 实际行为依赖
解决什么问题:API 升级时总有用户被破坏—— Hyrum 定律指出 API 的"实际行为"包含所有可观察行为,不是文档说的。
业务问题
真实场景
场景 1:API"内部实现"变成"外部依赖"
python
# API 文档说:"返回的错误码是 INVALID_INPUT"
# 但用户代码:
try:
result = api.create_order(...)
except ApiError as e:
if e.code == "INVALID_INPUT":
# 文档说这是 INVALID_INPUT
retry_with_fix()
elif e.code == "INVALID_INPUT_OLD": # 旧版本代码
# 没人说要支持这个,但总有人依赖
log.warning("old code path")问题:你只是改了错误码的拼写,用户代码全部崩了。
Hyrum Wright 原话:
"With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody."
失败模式
| 失败模式 | 后果 |
|---|---|
| "只是实现细节"改了 | 用户依赖了"实现细节"被破坏 |
| 错误码语义改变 | 用户分支判断错 |
| 响应字段顺序改变 | 用户反序列化失败 |
| 性能特征改变 | 用户超时假设失效 |
控制论视角
Ch 4 §4.6:"不变量必须被维护" + Ch 21:"实际系统的行为 ≠ 设计意图"
Hyrum 定律 = 控制论的"实际不变量"
- 设计意图 ≠ 实际行为
- 用户依赖 = 实际不变量
- 改变 = 破坏不变量 = 违反契约
原则:
- 文档说的 = 设计意图(不是 SSOT)
- 用户依赖的 = 实际不变量(才是 SSOT)对位:
- API = 控制律的输入端口
- 用户依赖 = 系统已知的反馈模式
- 升级 = 改变控制律(必须考虑所有已知反馈)
核心概念
Hyrum 定律 4 推论
1. 所有可观察行为都是契约
- 返回值
- 错误码
- 性能特征
- 资源消耗
- 副作用顺序
2. 用户越多 = 依赖越多
- 用户数 ↑ → 行为不变 = 风险 ↑
- 任何"小改动" = 大破坏
3. 隐藏行为 ≠ 没有依赖
- 内部实现 = 用户也会读源码
- "私有" ≠ "不被观察"
4. 弃用是唯一方案
- 不破坏 = 不升级
- 升级 = 弃用 + 迁移应对策略
策略 1:少暴露
- 只暴露必要的行为
- 隐藏实现细节
- 内部 API vs 公共 API
策略 2:明确稳定层
- 一级 API(承诺稳定)
- 二级 API(可能变)
- 内部 API(可随时改)
策略 3:弃用周期
- 标记 deprecated
- 给迁移路径
- 监控使用
- 关闭
策略 4:版本控制
- SemVer
- 重大变更 = Major
- 多个 API 版本共存工程实践
决策 1:API 是 stable 还是 experimental?
判断标准:
- 公开 API 给外部? → stable
- 内部使用? → experimental
- 会被引用多次? → stable
- 一次性使用? → experimental决策 2:如何处理"实现细节"
选项 A:完全隐藏
- 用 private / internal 修饰
- 文档不提及
- 强制封装
选项 B:标记为"未承诺"
- 文档说明"可能变化"
- 但用户还是会依赖
选项 C:明确稳定
- 承诺 = 必须维护
- 选择更慎重实战 1:API 设计原则
原则:
- 最小化 API 表面
- 不可变数据结构
- 显式错误码(不依赖异常)
- 文档化所有行为(包括性能)
- SemVer 严格反模式 vs 正模式
| 反模式 | 正模式 |
|---|---|
| "只是重命名" | 视为破坏性变更 |
| "应该没人依赖" | 假设有人依赖 |
| "快速修复" | 完整弃用周期 |
| "改一下实现" | 改实现 = 改变行为 = 通知用户 |
性能指标
| 指标 | 衡量 | 健康值 |
|---|---|---|
| API 表面 | 公共 API 数量 | < 100 |
| 破坏性变更频率 | 每季度 | 0(除非 Major 版本) |
| 弃用完成率 | deprecated → removed | 100% |
| 迁移时间 | 用户迁移平均时间 | < Major 版本周期 |
边界判断
何时用 SemVer(★ 强烈推荐)
- 公共 API
- 库 / 框架
- 多用户依赖
何时可宽松
- 内部 API
- 实验性功能
- 一次性工具
何时升级
| 当前 | 升级到 | 触发条件 |
|---|---|---|
| 自由修改 | SemVer | 多用户 |
| 无弃用周期 | 4 阶段弃用 | 大型项目 |
关键洞察
- Hyrum 定律是分布式系统的工程现实——任何 API 一旦有用户 = 不可破坏。
- 文档说 = 设计意图,用户依赖 = 实际契约——后者才是 SSOT。
- 隐藏 ≠ 没有依赖——内部实现也会被读源码。
- 弃用比删除更难——但它是唯一的"无破坏升级"路径。
- 与 cybernetics 同构——Ch 4 §4.6 不变量边界 = 用户依赖的实际边界。