Post content
Protobuffers 的設計是錯的(2018) (★ 103 分) 這篇文章主張 Protocol Buffers(簡稱 Protobuf)是一個設計不良的序列化技術。作者指出 Protobuf 的型別系統薄弱且充滿限制,許多功能組合不良,顯示其設計是隨興拼湊而來。他批評像是 `map` 與 `oneof` 等關鍵結構不但缺乏一致性,還製造出許多不合理的限制,例如 `oneof` 不能重複、`map` 不能巢狀以及列舉型別(enum) 不能作為鍵值,這使得系統難以維護並阻礙了程式碼的泛型化。文章建議,若改採更嚴謹且現代化的型別系統,如將訊息設為「乘積型別」(product types)、`oneof` 為「和型別」(coproduct types)、並支援參數化,便能大幅減少複雜度與限制。 作者也批評 Protobuf 在處理「可選欄位」與「預設值」上的設計帶來嚴重後果。因為所有純量型別欄位都會被預設初始化,導致系統難以區分「未設定值」與「預設值」,對於需要前後相容的 API 來說是一場噩夢。此外,其訊息型別的存取行為異常,會在不經意間建立新物件或覆寫欄位,使得類似 `msg.foo = msg.foo` 這樣的操作不再是無副作用的。這些問題不僅讓 API 設計變得危險,也使應用程式難以保證一致性。 至於 Protobuf 向外界宣稱的前後相容性,作者認為其本質上是「過度寬鬆」而非真正解決方案。它能處理未知欄位,看似支援不破壞性的轉換,但實務上大多程式並不會真正保留這些未知欄位,反而造成大量額外的驗證與防禦性程式碼,這將問題分散到整個程式庫,使維護更為混亂。作者強調,Google 之所以推廣這樣的設計,與其特殊規模與成本考量有關,但對於一般新創或中小型公司而言,花人力處理這些折衷反而代價更高。 最後,作者強調 Protobuf 最大的危害是不斷滲透程式碼庫。由於它的資料結構設計並不自然貼近應用邏輯,開發者往往要在「維護一組內部資料型別」與「直接將 Protobuf 用於業務邏輯」之間做痛苦選擇。實務上後者更常發生,導致整個系統充滿難以維護的代碼生成與不合語言慣例的結構,結果是寫出的程式龐雜又脆弱。作者最後語帶諷刺地警告「凡引入 Protobuf 的專案皆將失去希望」。 在 Hacker News 的討論中,不少人認同作者指出的問題,例如產生的程式碼笨拙、不支援「必填欄位」、以及鬆散型別設計讓 API 消費者必須額外檢查資料。然而,也有許多工程師認為作者過於苛刻。有人指出 Protobuf 本來就不是要成為一個完美的型別系統,而是要在跨語言、跨平台傳遞高效資料時提供足夠的兼容性與速度。許多評論者分享經驗,指出雖然 Protobuf 設計有缺陷,但其「簡單、好用、效能佳」足以應付大多數產業場景,特別是在需要前後兼容部署的分散式系統裡。此外,Protobuf 的廣泛支援與工具鏈,讓它在現實世界中仍比 MessagePack、CBOR、Thrift 等替代方案更實用。 也有人提出,雖然 Protobuf 不是理想的資料建模工具,但若僅將其限制在序列化和通訊格式,而避免讓其滲透進應用邏輯,問題會減少許多。像 Cap’n Proto、FlatBuffers 或 Typical 等新興方案被提及,但社群普遍認為沒有一個替代品能同時提供 Protobuf 現有的跨語言支援與完整生態。因此,爭論的核心在於「理論優雅」與「工程實用性」之間的張力。總結來說,多數工程師雖然承認 Protobuf 有很多怪異甚至令人痛苦的設計,但也普遍認為它在真實世界的妥協仍有其價值。 👥110 則討論、評論 💬 https://news.ycombinator.com/item?id=45139656