Go 里的海勒姆定律?非必要不修改......
大家好,我是煎魚(yú)。
在我們?nèi)粘9ぷ髦?,只要你維護(hù)過(guò)歷史比較悠久的項(xiàng)目??倳?huì)遇到一些神奇的代碼。其中最莫過(guò)于在老代碼上,前人讓你不要改這塊邏輯的注釋。
在 Go 中也有一些約定俗成的代碼。周末看到了還有點(diǎn)意思,分享給大家。
“該文本不可變更”
在 net/http 標(biāo)準(zhǔn)庫(kù)中,有以下這段代碼:
func (e *MaxBytesError) Error() string {
// Due to Hyrum's law, this text cannot be changed.
return "http: request body too large"
}
注意看上面的注釋:“Due to Hyrum's law, this text cannot be changed.”。翻譯過(guò)來(lái)中文含義是:根據(jù)海勒姆定律,此文本不可更改。
大家看到可能會(huì)犯迷糊。海勒姆定律是什么?教科書(shū)上可沒(méi)有教過(guò)這玩意。
海勒姆定律是什么
海勒姆定律(Hyrum's Law) 是一種軟件開(kāi)發(fā)領(lǐng)域的概念。由 Google 工程師 Hyrum Wright 提出,他在一次演講中討論接口設(shè)計(jì)和演化問(wèn)題時(shí)提出了這一觀察并得到了驗(yàn)證。
核心描述是:
- 無(wú)論接口的官方文檔或開(kāi)發(fā)者如何嚴(yán)格定義接口的行為,接口的實(shí)際行為都會(huì)影響其用戶。
- 所有可能的用戶依賴接口實(shí)際行為的方式,最終都會(huì)受到接口的實(shí)現(xiàn)約束,而不僅僅是其文檔描述。
簡(jiǎn)單來(lái)講,接口的所有行為,無(wú)論是顯式定義的還是隱式存在的,都會(huì)被用戶依賴。
即便某些行為不是接口規(guī)范的一部分,只要接口表現(xiàn)出了某種行為,使用它的系統(tǒng)或代碼可能就會(huì)開(kāi)始依賴這種行為。
Go 源碼中的案例
結(jié)合前面的案例來(lái)看,原作者意識(shí)到錯(cuò)誤消息無(wú)法隨意更改,因?yàn)榭赡苡心承┑胤降哪承┯脩粢蕾囉谠撳e(cuò)誤消息。
雖然調(diào)整錯(cuò)誤消息看起來(lái)微不足道,但這種改動(dòng)可能會(huì)對(duì)依賴這一特定消息的用戶造成意想不到的問(wèn)題。
這種情況下,一個(gè)看似無(wú)關(guān)緊要的描述修改:"http: request body too large",可能會(huì)導(dǎo)致用戶的業(yè)務(wù)代碼的中斷。從而影響 Go1 的兼容性保障和程序運(yùn)行。
那 Go 源碼里還有沒(méi)有其他地方有類似的描述呢?挺多的。如下幾個(gè),請(qǐng)看注解中的 “Hyrum's Law” 部分。
1、crypto/rsa/rsa.go[1]:
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
// ...
2、crypto/rsa/pss.go[2]:
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
// ...
3、internal/weak[3]:
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.
總結(jié)
“海勒姆定律” 和 Go 源碼中的 “被動(dòng)實(shí)踐” 都很好的印證了。只要你開(kāi)放出去的東西,無(wú)論是接口,又或是參數(shù)。都有可能神不知鬼不覺(jué)中被用戶依賴了。當(dāng)你一旦作出變更時(shí),就有可能產(chǎn)生不兼容。導(dǎo)致第三方系統(tǒng)出錯(cuò)或者產(chǎn)生臟數(shù)據(jù),要進(jìn)行洗數(shù)據(jù)。
為什么我會(huì)知道?因?yàn)樽罱矣幸粋€(gè)朋友他遇到了(doge。當(dāng)然,最好在設(shè)計(jì)系統(tǒng)或接口時(shí),就要盡可能的減少依賴非預(yù)期行為的可能性!
參考資料
[1]crypto/rsa/rsa.go: https://github.com/golang/go/blob/5123f38e050c5ee7130d459ea247d998a838b5a1/src/crypto/rsa/rsa.go#L517
[2]crypto/rsa/pss.go: https://github.com/golang/go/blob/5123f38e050c5ee7130d459ea247d998a838b5a1/src/crypto/rsa/pss.go#L294
[3]internal/weak: https://github.com/golang/go/blob/5123f38e050c5ee7130d459ea247d998a838b5a1/src/internal/weak/pointer.go#L24