你的 API 文件寫得再清楚也沒用——Hyrum’s Law(海倫姆定律)告訴你為什麼你的使用者不在乎

如果你的功能被大量使用,就別指望使用者會遵守你的限制——因為所有情境都有可能發生。

出處:Software Engineering at Google 中提到的 Hyrum’s Law

這篇文章是我閱讀 What I learned from the book Software Engineering at Google 整理的筆記

什麼是 Hyrum’s Law?

Hyrum’s Law(海倫姆定律)指出:當 API 的使用者數量足夠多時,你在合約中承諾什麼並不重要,系統所有可觀察的行為都會被某些人依賴。

這意味著在設計與維護系統時,不能只看規格文件,還必須考慮使用者實際上怎麼使用你的系統。

為什麼這條定律會成立?

使用者不會去讀完整的 API 文件,他們是透過「觀察行為」來理解系統的。當某個未定義的行為在多次執行中表現穩定時,使用者就會將它視為隱式契約(implicit contract),並在自己的程式碼中依賴它。使用的人越多,這種隱式契約被建立的可能性就越高。

案例一:Java HashMap 的迭代順序

HashMap 文件中明確提到不保證順序性,但就是有工程師用 containsElementInOrder() 對輸出做斷言,甚至有人把迭代順序當作隨機數產生器。

以 TypeScript 來模擬這個情境:

// 模擬 Java HashMap 的行為(不保證順序)
function unstableHash(key: string): number {
  // 不同版本的 hash 演算法可能給出不同結果
  return key.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0) % 4
}

// 把 hash 迭代順序當「隨機數產生器」
function ghettoRandom(seed: string): number {
  const map = new Map<number, number>();
  for (let i = 0; i < 100; i++) {
    map.set(unstableHash(seed + i), i);
  }
  return [...map.keys()][0]; // ← 拿第一個 key 當隨機值
}

這段程式碼的問題在於:我們根本不能保證結果一定會是不同的亂數,hash 碰撞會讓 key 重複覆蓋,但就是有人這樣寫了。

當 Google 升級 Java 版本後,hash 演算法改變導致順序不同,許多程式在測試中大量失敗。

Google 的解決方式是採用防禦性隨機化——每次執行都打亂順序,讓人從根本上無法依賴迭代順序。Python 跟 Go 後來也獨立採用了相同的做法。

案例二:Recall.ai S3 URL 冒號事件(2024)

起因

Recall.ai 發布了一個更新,在 S3 URL 路徑中加入了 :(冒號)。URL 編碼的冒號在 URL 中是完全合法的字元,而且客戶根本不需要解析 URL,只是用 GET 請求從 S3 下載影片而已。他們認為這不可能會有問題。

發生問題

部署後立刻收到影片下載失敗的回報。調查後發現只有一小部分客戶受到影響,但這些客戶 100% 的請求都會失敗。

根本原因

受影響的客戶都使用 aiohttp 這個 HTTP client,而 aiohttp 依賴 yarl 這個 library 做 URL 解析。yarl 預設會對 URL 進行規範化(normalization),自動把「安全字元」做 URL 解碼。這導致實際發出的請求路徑跟 S3 簽章的路徑不一致,S3 判定簽章無效,回傳 403 拒絕請求。

解法

Recall.ai 嘗試繞過 yarl 的行為但失敗了,最後因為受影響的客戶數量少,選擇通知客戶加上一行程式碼關閉 yarl 的規範化:

python

yarl.URL(url, encoded=True)

這跟 Hyrum’s Law 有什麼關係?

Recall.ai 的 URL 在這次更新之前「剛好」從來沒包含過需要編碼的安全字元,所以 yarl 的規範化行為從來沒被觸發過。客戶端——甚至客戶自己都不知道的第三層依賴——已經隱式地依賴了「URL 不會被修改」這個未承諾的行為。一旦 URL 格式稍有變化,這個隱式契約就被打破了。

所以我們該怎麼做?

既然所有情境都有可能發生,指責使用者不閱讀文件並不能解決問題。真正有效的做法是從設計層面讓使用者無法依賴未承諾的行為,就像 Google 的防禦性隨機化一樣。

當你的功能被廣泛使用時,任何變更都應該假設「所有可觀察的行為都已經被某些人依賴了」,用設計來防範,而不是用文件來約束使用者。

charlie ku
charlie ku

程式撰寫是我的專業,思考與成長則是我永不停歇的追求。在這裡,我分享一些對生活和技術的思考,從技術探討到閱讀心得,這些都是我在探索自己和這個世界時所發現的靈感。希望我的分享能夠啟發你,也讓我們在彼此的交流中共同進步。

文章: 18

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *