vatt'ghern jaskier's ballads

Bun 的 GitHub repo 現在堆著五千多個沒人合併的 PR,React 只有 441 個。距離上一次穩定版,也已經三個月沒有動靜。

PR 排到五千個,「下一版」說了三個月

場從 Zig 全面改寫成 Rust 的工程,Bun 官方給的理由是記憶體安全。照理說,重寫完應該更穩——但寫作當下,距離上一次穩定版已經三個月,而且數字還在往上跳,是 Bun 自 2022 年以來最長的一次空窗期。獨立部落客 Tero Piirainen 追蹤 Bun 的 GitHub repo 一段時間後,寫下了這篇質疑。看起來像是單純的專案管理問題:PR 排得比較久,版本晚一點出,沒什麼特別。把三條線索攤開來看,事情沒那麼簡單。

三條線索各自看都可以有無害的解釋:估計時間本來就會失準、專案變大 PR 自然變多、開源專案本來就常常靠自動化工具跑 CI。真正讓 Piirainen 停下來寫這篇文章的,是三條線索擺在一起之後,指向的方向完全一致——不是巧合,是同一個原因在三個不同的地方留下痕跡。這篇文章要做的事,就是把這三條線索拆開來,一個一個檢查。

三個月沒有穩定版:Bun v1.4 卡在哪裡

第一個具體數字是 PR 堆積量:Bun 這個專案現在開著超過五千個未合併的 PR。拿類似規模的開源專案比一比,對照組並不誇張——OpenClaw 開著 2,200 個,React 只有 441 個。三者都是活躍度很高的專案,Bun 的堆積量卻是另一個量級。這不是拿一個冷門小專案去對照巨頭,OpenClaw 跟 React 本身都是持續有新功能推進、社群貢獻量不小的專案,拿來當基準線是合理的。

PR 堆得多,通常代表兩件事之一:專案真的變大了,或者合併的速度跟不上生成的速度。這個區別不是文字遊戲——前者是專案受歡迎的副作用,後者是維運能力出了缺口。對正在評估要不要導入一個還在重寫中的專案的團隊來說,兩種情況該做的判斷完全不同:前者頂多是等一等,後者代表核心團隊可能已經看不住自己專案的品質。哪一個才是 Bun 的真實情況,值得拆開來查——先從最直接的解釋開始。

再往下拆之前,先把「三個月沒有穩定版」這件事放回 Bun 自己的歷史裡看。Piirainen 特別點出這是自 2022 年以來最長的一次空窗期——不是「這個專案一向如此」,而是一個相對於自身過去節奏的明顯異常。一個習慣穩定發版的專案突然卡住三個月,跟一個本來就發版不規律的專案卡住三個月,代表的意義不一樣;前者更值得往下追。

對一個 JavaScript runtime 來說,發版節奏本身就是一種訊號,跟功能清單同樣重要。用 Bun 的團隊每天要靠它跑測試、跑建置、跑生產環境的服務,穩定版遲遲不出,代表這段時間裡任何 bug 修復、任何安全性更新,都只能停在 nightly 或 canary 管道,要嘛冒險先用未經完整驗證的版本,要嘛乾脆卡在舊版本上等——不管選哪一邊,都是額外的營運成本。

只是又一次抓錯時間?

官方是不是只是把交付日期抓得太樂觀?單一次抓錯時間不算什麼大問題,工程估算本來就常常失準,哪個團隊都遇過。但 Piirainen 記錄下來的,是 Jarred Sumner 從六月到八月一連串的公開承諾——放在時間軸上攤開,整理出來至少有七次,不是一次抓錯。

六月二十四日,Sumner 給出的是一個明確日期:「Bun v1.4 ships July 7th」。七月七日沒有出,七月四日的說法已經鬆動成「Bun v1.4 hopefully Tuesday」——從肯定句退成祈使句,日期本身也不見了。七月二十九日換了一種說法,不談交付時間,改談「Bun v1.4 fixes over 3000 issues over v1.3」,用修了多少 issue 來撐住進度感。八月七日又回到具體條件:「1 more PR to merge then it'll be time for Bun v1.4」,聽起來只差臨門一腳。八月十三日說「Bun v1.4 is compiling」,八月十五日承認「Bun v1.4 is delayed until Monday」。七次公開發言,時間跨度不到兩個月。

拖曳時間軸看每一次承諾 · 7 次跳票紀錄

6.24
6.24「Bun v1.4 ships July 7th」
六到八月間 Jarred Sumner 對 Bun v1.4 的七次公開承諾,日期為原文標註的實際發佈間隔。資料來源:tipiirai.com。

七次,沒有一次照著日期兌現。八月十七日,連 Sumner 自己都在承諾裡先打了預防針——「if I say a date you won't believe me but let's say tomorrow」。他自己都知道,再給一個日期也不會有人相信。這句話值得多停留一秒:一個專案的負責人,在公開發言裡預先承認自己的信用已經不值得相信,這不是單純的「這次比較忙」,是溝通機制本身已經失效到連當事人都放棄修補的地步。

單一次估錯時間可以歸咎於運氣不好,複雜系統的交付日期本來就難抓準。但連續兩個月、七次都沒兌現,而且承諾的內容從明確日期一路退到「issue 修復數量」「還差一個 PR」這種聽起來很接近、實際上完全無法驗證進度的說法,更像是溝通機制本身出了問題——這解釋了「為什麼一直跳票」,卻沒有解釋「為什麼會一直跳票」。跳票本身是症狀;接下來兩個候選解釋,要找的是症狀底下的原因。

只是專案變大了,PR 自然會堆積?

另一個解釋是規模:Bun 這幾年成長很快,使用者變多,issue 跟 PR 自然也變多,五千多個聽起來嚇人,但如果只是跟專案成長成比例的堆積,倒也說得過去。問題是,拿實際數字一比,這個解釋撐不住。

規模論成立的前提,是堆積量跟合併能力同步成長——用的人變多,貢獻的人也變多,核心維護者的數量與經驗理論上也會跟著補上,堆積量頂多是暫時性的落後,而不是持續擴大的缺口。要判斷 Bun 是不是這種情況,最直接的方法就是找同樣活躍、同樣有大量使用者的專案,看它們的 PR 堆積量落在哪個範圍。

React 是前端生態裡活躍度數一數二的專案,常年有大量社群貢獻,PR 堆積量卻只有 441。OpenClaw 也是持續有新功能推進的專案,堆積量是 2,200。Bun 的五千多,不是「比別人多一點」,而是同量級專案裡的異數。如果只是專案變大,堆積速度應該跟著使用者數與貢獻者數一起走;真正該問的是——這些 PR 是誰開的,又是誰在合併?

對一個開源專案來說,PR 堆積量本身也是外部貢獻者最容易感受到的溫度計。堆積量一旦大到某個程度,新提交的 PR 排在後面,被看到的機率就會被稀釋——這跟「專案受歡迎」造成的堆積不是同一種現象:受歡迎造成的堆積會隨著維護者增加而慢慢消化,跟不上生成速度造成的堆積只會越積越厚。判斷哪一種在發生,看的不是總量,是總量隨時間的走向,還有背後真正在動手合併的是誰。

規模論這條路走到這裡已經卡住:數字對照顯示 Bun 的堆積量不合比例,但單看堆積量,還是說不出「合併的人是誰、為什麼合併不過來」。前面的時間軸給了一個線索——承諾一直跳票,卻沒說出具體卡在哪一關;規模對照給了第二個線索——堆積量遠超同量級專案。兩條線索都指向維運能力,但都還沒指到根本原因。要往下追,得直接去看合併這件事,實際上是誰在做。

誰在寫 Bun 的程式碼

時間軸解釋了「什麼在發生」,但沒有解釋「為什麼會這樣」。PR 排到五千個、承諾一次次跳票,背後如果只是團隊人力不足,通常會反映在合併速度緩慢但穩定地往前爬;如果背後是別的機制在運作,速度曲線會長得不一樣。人力不足的專案,PR 數量的成長曲線通常會跟著徵才、跟著新維護者上手的速度慢慢往上;如果曲線在某個時間點突然變陡,那就不是「人不夠」,是「多了一個不受人力速度限制的生成來源」。Piirainen 給出的答案,就藏在貢獻者結構裡。

過去一個月 Bun repo 的 commit 來源。資料來源:Tero Piirainen 對 GitHub 紀錄的整理,tipiirai.com。

過去一個月,帳號 robobun 貢獻了 15,800 次 commit,autofix-ci[bot] 貢獻 1,600 次,而身為 Bun 負責人的 Jarred Sumner 本人,只有 790 次。autofix-ci[bot] 從名字看得出是自動化 pipeline;robobun 的身分文章沒有明講,但合理的推測是它同樣是自動化 agent 帳號——三個數字放在一起看,人類負責人的 commit 量,只有這個帳號的二十分之一。這個比例,比「規模論」與「時間估算論」加起來更能解釋堆積量為什麼會失控——當寫程式碼的主力不再是人,PR 開出的速度自然不會再受限於人類寫程式碼的節奏。

790 次拆開來看其實不算少——平均一天超過二十次 commit,不是負責人放著專案不管。問題不在 Jarred 有沒有在做事,是產出結構已經被自動化 agent 徹底改變比例:robobun 一個帳號的量,就是他本人的二十倍。中間那格 autofix-ci[bot] 貢獻的 1,600 次,性質又不太一樣——從名字看更像是自動修 lint、格式或既有錯誤的流水線工具,跟 robobun 這種主動生成新功能、新程式碼的 agent,扮演的角色不是同一種。三個數字放在一起,畫出來的是一條清楚的分工線:真正在「決定要寫什麼」的,越來越不是人。

這不是外部猜測,是 Sumner 自己說的:「6 months ago, most of Bun's PRs came from people prompting Claude. Nowadays, most of Bun's PRs come from Claude prompting Claude.」六個月前,大多數 PR 還是人在 prompt Claude;現在,大多數 PR 是 Claude 在 prompt Claude。人類的角色,從「下指令的人」退到「看著 agent 自己接力」。這句話裡藏著一個關鍵的轉變:如果 prompt 的對象從人變成另一個 agent,代表原本每一次生成前該有的人類判斷——這個方向對不對、這個做法值不值得——也跟著被跳過了一層。

把這個結構放回 PR 堆積量看,合理的推測是:agent 生成程式碼的速度,遠遠超過人類 review 跟合併的速度。五千多個 PR 未必是因為 Bun 突然變大,更可能是生成端跑得比審核端快太多——backlog 本身就是這個落差的殘留物。review 這一關本來就無法無限加速:讀懂一個 PR 在做什麼、判斷它有沒有副作用,是需要時間的人類工作,agent 產出的速度再快,也沒辦法讓這一關跟著變快。

合理的推測是,這不會是 Bun 一個專案獨有的處境。coding agent 越來越普及之後,任何開源專案只要放手讓 agent 自動開 PR、自動 commit,理論上都會撞上同一種落差:生成端可以無限加速,審核端卡在人類的認知頻寬,速度差距只會越拉越大。Bun 目前的處境比較特別的地方,是這個落差已經大到反映在版本號延期跟公開道歉式的承諾裡——這種風險其實一直都在,只是這次終於大到讓外部觀察者也看得見。

拖曳調整合併速度 · 感受五千多個 PR 的量體

每週 100 個

照這個速度,要 50 週(約 11.5 個月)才能清完現有的五千多個 PR。

五千多個未合併的 PR 是 Piirainen 記錄的實際數字;合併速度由讀者自訂,用來感受這個數字的量體,不是 Bun 團隊公佈的實際合併節奏。

拖曳到偏保守的每週幾十個,答案會落在一年以上;拖到樂觀的每週幾百個,也要兩三個月才清得完。而且這還是最理想的靜態演算法——假設接下來完全沒有新 PR 流入,backlog 只出不進。現實裡,只要生成端還在以 agent 的速度開新 PR,這個清空的時間只會被不斷往後推。數字本身沒有那麼重要,重要的是它清楚顯示:五千多個早就超過「加派幾個人手就能消化」的量級——這是結構性的落差。

外部批評也不是只有 Piirainen 一個人在講。Zig 語言的原作者 Andrew Kelley 公開評論過 Bun 的程式碼:「We became increasingly horrified at the programming practices we saw in Bun's codebase. Hacks on top of hacks. Abuse of assertions.」這句話來自語言本身的創造者,分量不一樣——他看到的不是效能數字,是程式碼寫法本身的紀律問題。一個語言的原作者去看用自己語言寫的專案,看的角度通常比外部評論者更貼近實作細節;「hacks on top of hacks」這種說法,指向的是長期維護成本,不是一次性的效能取捨。

「abuse of assertions」這個措辭也值得留意:assertion 本來是拿來在開發階段標記「這裡的假設不該被打破」,如果被濫用成掩蓋邏輯漏洞的手段,代表的往往不是某一行程式碼寫錯,而是整個程式碼庫對「什麼情況該處理、什麼情況直接放棄」這件事,已經沒有一致的判斷標準——這正是外部語言作者才容易一眼看穿的那種問題,日常維護者反而容易看習慣。

Rust 版本裡大量的 unsafe 區塊,也讓人懷疑這場重寫有沒有真的達成當初宣稱的記憶體安全目標。如果重寫的理由本身沒有兌現,更該問的是:這場重寫真正想解決的問題是什麼?合理的推測是,Zig 從來不是問題本身——這場重寫更像是拿來示範「AI agent 能自己寫一個 runtime」的案例,語言選擇只是附帶的包裝。

Piirainen 不是隨口酸——他在文章裡承認自己「I thought he was a true Zig talent」,曾經很看好 Sumner 的技術判斷。正因為出發點是欣賞而不是敵意,這篇文章記錄的每一個數字才更值得認真看待:三個月的空窗期、七次跳票、二十比一的 commit 落差,加上 Zig 創造者本人的公開批評,這些線索沒有一條單獨能說明全部,但放在一起,指向同一件事:重寫本身或許不是問題,把大量程式碼交給 agent 生成、卻沒有相應的人類審核跟上,才是。

對還在觀望要不要導入 Bun 的團隊來說,這幾條訊號合起來提供了一個比「Zig 快還是 Rust 快」更值得先問的問題:這個專案現在的品質把關,是誰在做、做得夠不夠。語言選擇是可逆的決定,一個 runtime 的核心維護紀律一旦鬆動,往往要花更久才能補回來。

把這篇文章拆解出來的三條線索倒過來看,也是一組可以套用在任何「重寫中專案」身上的檢查項:發版節奏有沒有明顯偏離這個專案自己過去的歷史;PR 堆積量跟同量級專案比起來是不是異常;核心維護者的實際 commit 占比,跟自動化工具或 agent 比起來,還剩下多少。三個都正常,重寫大概只是單純在趕工;三個都不正常,值得先觀望,而不是急著把生產環境押上去。

回到文章開頭那個對照——五千多個 PR、React 的 441 個。單看這一個數字,很容易被當成「Bun 太受歡迎,維護跟不上」的正面煩惱。拆開三條線索之後,比較站得住腳的讀法是:受歡迎從來不是問題;真正的問題,是生成程式碼的速度遠遠甩開了審核能力。這也是為什麼三個月沒有穩定版這件事,值得認真當一回事,而不是當成單純的工程延遲。

The lesson:評估要不要把一個重寫中的專案導入正式環境,PR 合併速度與核心維護者的實際 commit 占比,比新語言選得對不對更早該檢查。