原文:Abenezer Belachew
最近,我在探索 Go 代码库时偶然发现了一个有趣的注释:
“根据海勒姆定律(Hyrum’s law),这文本不可更改。”
1
2
3
4
5
| func (e *MaxBytesError) Error() string {
// Due to Hyrum's law, this text cannot be changed.
// 根据海勒姆定律,这文本不可更改。
return "http: request body too large"
}
|
- 在此之前,我从未听说过海勒姆定律。
- 简单搜索后得知,这是一个以 Hyrum Wright 命名的原则,他是 Google 的一名软件工程师。
这个“定律”非常简单:
当一个 API 拥有足够多的用户时,无论你在接口合约中承诺了什么:系统的所有可观察行为都会被某些人依赖。
换句话说,代码中任何可以被观察到的行为——无论是有意的还是偶然的——最终都会成为某个地方、某个人依赖的东西。
因此在上述代码中,作者明确指出该错误信息无法更改,因为可能已经有人在某处依赖它。虽然看似微不足道的更改,例如调整错误信息的措辞,却可能对依赖于这条特定消息的代码造成意想不到的问题。在这种情况下,一个看似微小的改动可能会破坏现有代码,因为有人依赖于 “http: request body too large
” 的确切措辞。
例如,以下是一些开源代码库,如果更改错误消息将会受到影响:
这并不是唯一的例子。我在 Go 的 crypto/rsa
和 internal/weak
包中也发现了引用海勒姆定律的类似注释。
一些例子
crypto/rsa/rsa.go
1
2
3
4
5
6
7
8
9
10
| func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
// Note that while we don't commit to deterministic execution with respect
// to the random stream, we also don't apply MaybeReadByte, so per Hyrum's
// Law it's probably relied upon by some. It's a tolerable promise because a
// well-specified number of random bytes is included in the ciphertext, in a
// well-specified way.
// 请注意,虽然我们没有承诺随机流的执行是确定性的,但我们也没有调用 MaybeReadByte,
// 因此根据海勒姆定律,这可能已经被某些人依赖了。
// 这是一个可以接受的承诺,因为在密文中以明确的方式包含了一个明确数量的随机字节。
}
|
crypto/rsa/pss.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, opts *PSSOptions) ([]byte, error) {
// Note that while we don't commit to deterministic execution with respect
// to the rand stream, we also don't apply MaybeReadByte, so per Hyrum's Law
// it's probably relied upon by some. It's a tolerable promise because a
// well-specified number of random bytes is included in the signature, in a
// well-specified way.
// 请注意,虽然我们没有承诺随机流的执行是确定性的,但我们也没有调用 MaybeReadByte,
// 因此根据海勒姆定律,这可能已经被某些人依赖了。
// 这是一个可以接受的承诺,因为在签名中以明确的方式包含了一个明确数量的随机字节。
if opts != nil && opts.Hash != 0 {
hash = opts.Hash
}
}
|
internal/weak
1
2
3
4
5
| Using go:linkname to access this package and the functions it references is explicitly forbidden by the toolchain because the semantics of this package have not gone through the proposal process. By exposing this functionality, we risk locking in the existing semantics due to Hyrum's Law.
使用 go:linkname 访问该包及其引用的函数是被工具链明确禁止的,
因为该包的语义尚未经过提案流程。如果公开了此功能,
我们可能会因为海勒姆定律而锁定现有的语义。
|
观察
显然,这不仅仅是 Go 语言的专属现象,它在其他代码库中也有提及。
坦白说,这让我想起 JavaScript 在过去几年中的演变,它的许多奇怪且意料之外的行为因为被广泛依赖而变成了事实上的标准。现在,我终于知道该如何称呼这种现象了——海勒姆定律。
最后的思考
- 这是一则很好的提醒:当你修改别人可能依赖的代码时需要格外小心——并尽量设计出不会意外锁定奇怪行为的系统。
- 更好的做法是,从一开始就设计可以最大程度减少意外行为被依赖可能性的系统。
- 毕竟,你知道那句老话:“仅仅一个小小的改动就足以让整个系统崩溃。”
😶🌫️️