海拉姆定律——一个软件工程的观察

只要一个 API 的用户足够多,你在合约中承诺了什么并不重要:所有你的系统中可观测到的行为都会被其他人依赖。

海拉姆定律——一个软件工程的观察

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.

——原文来自https://www.hyrumslaw.com/

翻译:

只要一个 API 的用户足够多,你在合约中承诺了什么并不重要:所有你的系统中可观测到的行为都会被其他人依赖。

Hyrum 定律的作者是谷歌的工程师 Hyrum。他基于日常工作经验提出了 Hyrum 定律,并以自己的名字命名。

Hyrum 定律认为:理论上,接口应该在系统的使用者和实现者之间提供清晰的分隔。但在实践中,随着使用的增加,用户开始依赖接口有意暴露的、或是通过日常使用猜到的实现细节。这时理论就崩溃了。Avram Joel Spolsky 的“抽象泄露法则”体现了消费者对内部实现细节的依赖。

逻辑上极端来看,这导致了“隐式接口” : 如果一个接口有足够多的使用者,那么他们就会有意无意地依赖于实现的每个方面。 这一现象将对具体实现的修改产生约束:实现必须既符合显式文档化的接口,又符合在使用过程中被用户发现的“隐式接口”。

隐式接口的创建通常是逐步发生的,而接口使用者通常不会察觉到这种现象的发生。 比如,接口可能没有对性能做出承诺,但是使用者通常预期从其实现中得到一定程度的性能。 这些期望成为系统的隐式接口的一部分,以至于对系统的更改必须保持这些性能特征,以便为其使用者继续运行。

并不是所有消费者都依赖于同一个“隐式接口”,但是如果有足够的消费者,隐式接口最终将与实际实现完全匹配。 到这个时候,“接口”就完全不存在了,因为“实现”已经变成了“接口”,对它的任何更改都将违背用户的期望。


单独想拿这个定律写一篇文章是因为这个定律谈到的问题真的太常见了。举例来说,微软会让一些 Office 中的 bug 保留相当长的时间,甚至为了保证兼容性,新版本还要模拟旧版本的 bug,因为使用者很可能已经依赖了 bug。bug 已成为了 Hyrum 定律中的“隐式接口”。甚至我在自己的实际工作中也经常遇到这类问题:比如客户端的同学会看了几个服务端返回的 URL 都是同一个格式,就认为服务端返回的 URL 肯定都是同一个格式。于是没有采用标准的、健壮的方法处理 URL——比如尝试从 URL 的末尾截取文件后缀以获得文件类型,或者尝试将文件名从 URL中提取出来,并在用户的本地存储中使用这一名称。而当服务端将 URL 的规则发生了变更,文件不再具有后缀或 URL 中含有无法作为文件名使用的特殊字符时,客户端就崩溃了。服务端此时不得不对 URL 的格式做修改,以避免触发客户端的 bug——毕竟版本已经发出去了,也没法强制用户升级。


“隐式接口”的存在是大型系统有机生长的结果(俗称堆屎山)。虽然我们可能希望问题并不存在,但设计师和工程师在构建和维护复杂系统时,应该将其考虑在内。