Frontier · Bun Blog

用 Rust 重寫 Bun

Jarred Sumner 解釋了 Bun 的捆綁器從 Zig 重寫為 Rust 的原因和方式,包括代理編碼循環、對抗性審查、測試和遷移背後的效能工作。

本文由英文原文翻譯。

文字版說明:原文中的數據圖、互動視覺化與流程示意未在本頁重製,完整圖表請前往原文查看。

揭露:Bun 於 2025 年 12 月被 Anthropic 收購。我和 Bun 團隊的其他人在 <x​​10/> 工作。我使用了 Claude Fable 5 的預發行版本來進行 Rust 重寫的大部分內容。

Bun 以 esbuild 的 JavaScript 和 TypeScript 轉譯器的逐行埠啟動,從 Go 到 Zig。我於 2021 年 4 月 16 日 寫了第一行 Zig。在看到 Hacker News 上的單頁 Zig 語言參考 並對底層控制和對效能的關注感到非常興奮後,我押注於 Zig。

從一開始,Bun 的範圍就很大:

  • JavaScript、TypeScript 和 CSS 轉譯器、壓縮器和捆綁器
  • npm 相容的套件管理器
  • Jest類似測試運行器
  • Node.js 和 TypeScript 相容模組解析
  • HTTP/1.1 和 WebSocket 用戶端
  • Node.js API 實現,例如 fsnettls 和許多其他模組

Bun 的初始版本是我在LLM之前的Zig時期,在奧克蘭一間狹窄的公寓裡用一年時間編寫的。像 Bun 這樣規模宏大的專案的預設結果是加入 GitHub 個人資料頁面上的死角專案的墓地。 Zig 使 Bun 成為可能。如果沒有Zig,我永遠不可能在一年內建造這麼多。

如今,Bun 的 CLI 每月下載量超過 2,200 萬次。 Claude Code 和 OpenCode 等流行工具將 Bun 作為它們的運行時。 Vercel、Railway、DigitalOcean 等為 Bun 提供第一方支援。

Bun 的範圍也對穩定性構成了挑戰。以下是我們在 Bun v1.3.14 中修復的一小部分錯誤:

  • 當非同步 .write() 仍在執行緒池上進行時,在 zlib、Brotli 或 Zstd 流上呼叫 .reset() 時,node:zlib 中的堆使用後釋放崩潰
  • onerror回呼在本機句柄上發出可重入write(),然後發出close()時,node:zlib中的use-after-free崩潰
  • 當可重入的 JS 回呼(例如超時偵聽器、選項 getter 或寫入回調內的 session.request())觸發雜湊圖重新雜湊時,node:http2 中的 use-after-free 崩潰,從而使內部流指針無效
  • UDPSocket.send()sendMany() 中的 use-after-free,其中 valueOf()toString() 回呼中的使用者程式碼可能會在負載擷取和實際發送之間分離 ArrayBuffer
  • valueOf 回呼在參數強制期間分離或調整底層 ArrayBuffer 的大小時,Buffer#copyBuffer#fill 中的讀取會發生崩潰和越界
  • 當套接字的連線狀態透過使用者 JS 回呼在迭代中變更時,UDPSocket.sendMany() 中的堆疊越界寫入
  • crypto.scrypt 中存在記憶體洩漏,當輸出緩衝區分配失敗時,回呼和受保護的密碼/鹽緩衝區從未被釋放
  • SSLWrapper.init 在錯誤路徑上洩漏了 strdup 的密碼
  • tlsSocket.setSession() 中的記憶體洩漏,由於 d2i_SSL_SESSION 之後缺少 SSL_SESSION_free,每次呼叫都會洩漏一個 SSL_SESSION(每個呼叫約 6.5 KB)
  • 記憶體洩漏,其中fs.watch()觀察者在.close()之後從未被垃圾回收,這是由於引用計數下溢將每個觀察者永久固定為GC根
  • background-clip 具有供應商前綴和多層背景時,CSS 解析器會發生雙重釋放崩潰
  • DuplexUpgradeContext 從未被釋放 - 每個 tls.connect({ socket: duplex }) 完全洩漏
  • MessageEvent 中的競爭條件崩潰,其中 GC 標記線程在從 BroadcastChannelMessagePort 並發訪問期間可能會觀察到 m_data 中的撕裂變體

我們本可以永久地一次性修復此類錯誤,但我們應該感謝我們的用戶,他們希望我們做得更好,並系統地防止此類錯誤再次發生。

我們已經在做什麼

  • 我們修補了 Zig 編譯器以新增 Address Sanitizer 支援。我們在每次提交時都使用 ASAN 執行測試套件。
  • 我們在 Windows 上發佈 Zig 經過安全檢查的 ReleaseSafe 版本
  • 我們使用 Fuzzilli 對 Bun 的運行時 API 進行 24/7 模糊測試,V8 和 JavaScriptCore 使用的 JavaScript 引擎模糊器
  • 我們進行了大量的端對端記憶體洩漏測試

這比許多項目都多。

只要非常聰明,不要犯錯?

我們的錯誤修復清單感覺很糟糕,我厭倦了睡覺時擔心Bun中的崩潰。我不會為此責怪 Zig - Zig 的其他用戶沒有我們所遇到的錯誤,並且將 GC 與手動管理內存混合對於軟體來說是一件不常見的事情,沒有語言真正為它設計。如果沒有 Zig,我們就不會走到這一步,我將永遠感激不盡。直到最近,對於像 Bun 這樣的項目,程式語言的選擇還是單向決策。

JavaScript 是一種垃圾收集語言,現代 JavaScript 引擎(例如 JavaScriptCore)(和 V8)對於異常處理和垃圾收集器有嚴格的規則。 Zig 與 C 一樣,不為您管理內存,這是一種權衡,對於許多項目來說,這是使用 Zig 的一個重要原因。 Zig 沒有建構函數/析構函數,且大多數清理工作預計在每個呼叫站點使用 defer 明確編寫。

對於 Bun 來說,正確處理垃圾收集值和手動管理值的生命週期一直是穩定性問題的主要根源 - 最常見的是小記憶體洩漏,偶爾還會發生崩潰。每個記憶體分配都必須仔細檢查。這些位元組在哪裡被釋放?我們如何確保它只被釋放一次?我們是否正確檢查了 JavaScript 異常?這個垃圾收集指針對保守的堆疊掃描器可見嗎?這是垃圾回收的記憶體還是手動管理的記憶體?

對於穩定性問題,儘早了解是最好的。模糊測試發生在程式碼合併之後。 CI 在推送程式碼時發生。運行時安全檢查和地址清理程序在程式碼運行時發生(希望在開發中,在 CI 之前)。

減少此類問題的常見方法是確保清理程式碼始終為需要它的程式碼執行一次。 Zig 被設計為一種沒有隱藏控制流的簡單語言,因此它更喜歡使用明確 defer 關鍵字在作用域末尾運行程式碼,而不是 C++ 的隱式 ~Destructor 或 Rust 的隱式 Drop

語言清理
Zigdefererrdefer
C++~析構函數,&&移動
Rust放下

對於Zig程式碼,我們到底該在什麼時候執行清理程式碼?如果我們將相同的 *T 傳遞給許多不同的函數,我們如何知道它何時不再可訪問並且可以被清理?當有些函數調用完後需要繼續引用記憶體時是如何運作的?我們目前的方法是以下方法的組合:

  • arena 生命週期,可存取的範圍是明確的(解析器狀態不會轉義呼叫函數,因此 AST 節點是一個不錯的選擇)
  • 引用計數
  • 密切注意

許多項目選擇透過風格指南來回答此類問題。 TigerBeetle 的 TigerStyle 是 Zig 中的範例,Google 的 31,000 字的 C++ 樣式指南 是另一個範例。風格指南的挑戰在於執行。您如何確保遵循風格指南?從歷史上看,程式碼審查是透過 linter 和靜態分析器盡力執行的答案。

對於 Bun 來說,擁有一個嚴格的風格指南,並在類型系統中明確闡明明確的所有權期望,是一個真正的選擇。由於 Zig 沒有運算子重載,因此我們可能會得到很多如下所示的程式碼:

fn foo(a_ptr: SharedPtr(TCPSocket)) !void {
  const a: *TCPSocket = a_ptr.get();
  defer a_ptr.deref();

  const b = try do_something_with_a(a);
  defer b.deref();

  // ...
}

這比我們預期的 Zig 更符合人體工學:

fn foo(a: *TCPSocket) !void {
  const b = try do_something_with_a(a);
  // ...
}

C/C++ 呢?

大約 20% 的 Bun 程式碼是用 C++ 寫的,並且 Bun 嵌入了多個 C/C++ 函式庫:

  • JavaScriptCore,為 Safari 提供支援的 JavaScript 引擎
  • uWebSockets 和 usockets - 我們的 HTTP/WebSocket 伺服器和事件循環
  • lshpack 和 lsquic - HPACK 和 HTTP/3 函式庫
  • BoringSSL,Google 的 OpenSSL 分支
  • SQLite

C++ 而不是 Zig 是一個合理的選擇。我們會得到建構子和析構函數。我們可以刪除大量 extern "C" 包裝器程式碼。

但是,我們仍然依賴透過程式碼審查強制執行的樣式指南,即使使用 ASAN,記憶體損壞和記憶體洩漏仍然會發生。

為什麼Rust?

該清單中的大部分錯誤都是釋放後使用、雙重釋放以及錯誤路徑中的「忘記釋放」。在安全的 Rust 中,這些是編譯器錯誤和使用 Drop 進行類似 RAII 的自動清理。編譯器錯誤是比樣式指南更好的回饋循環。

從歷史上看,重寫是一個糟糕的主意。不包括註釋,Bun 是 Zig 的 535,496 行。用另一種語言重寫需要一小群工程師花費一整年的時間。這意味著凍結錯誤修復、安全修復或功能開發。獲得可交付產品的風險最小的方法是從 Zig 到 Rust 的機械端口,以最少的行為變化,使用我們已經用於測試 Bun 的完全相同的測試套件。

幸運的是,Bun自己的測試套件是用TypeScript編寫的,這意味著它不依賴運行時的程式語言。

一年對使用者的影響為零並不是我們可以考慮的現實選擇。因此,透過程式碼風格強制執行來解決穩定性問題是我們最好的選擇,也是我們在將 Rust 啟發的智慧指標 新增至 Bun 的程式碼庫時的計畫。

但說實話,我不想這麼做。國產智慧指針的人體工學效果比 Rust 更差,而且沒有任何保證。

如果我花一週時間測試 Anthropic 的新模型是否可以在 Rust 中重寫 Bun 呢?

一開始,我沒想到它會起作用。幾天后,測試套件的大部分開始通過,我看到新的 Rust 程式碼與原始 Zig 程式碼庫的匹配程度。我的意見從「這值得嘗試」變成「我要合併這個」。

Claude,將Bun改寫為Rust。

有很多方法可以把這件事做得很糟。例如,提示Claude「用Rust重寫Bun。不要犯任何錯誤。」然後祈禱它會起作用並不是我所做的。

想想一個人會如何做到這一點。第一個大問題是:

增量重寫?或者,一次完成所有事情?

根據我將 esbuild 的轉譯器從 Go 移植到 Zig 的 Bun 初始版本(沒有 LLM)的經驗,一切都更好。增量重寫會新增臨時程式碼,您希望這些程式碼最終會被刪除,並且在中短期內會很痛苦。

第二個大問題:如何?

我們如何讓 Rust 中的 Bun 與先前的 Bun 保持相同,具有相同的架構、效能和功能集,同時也獲得 Rust 的語言功能(如借用檢視器)?如何保證重寫後團隊仍能維持?

進行重寫,看起來就像我們將 Zig 程式碼轉譯為 Rust。我們可以逐漸重構它以減少 unsafe 的使用,並在 Bun v1.4 發布後看起來更像慣用的 Rust。

這是僅有的兩個大問題。其他一切都是策略。

編寫和審查程式碼的循環

軟體工程師的許多日常工程工作可能會被過度簡化為循環。

// Pseudocode, not real code:
let task;
while ((task = todoList.pop())) {
  const result = task();
  const feedback = await Promise.all([review(result), review(result)]);
  await apply(feedback, result);
}

task 有一些與之關聯的上下文(Jira 票證、GitHub 問題等)。 result 是您為修復它而編寫的程式碼。程式碼審查者review檢查回歸和正確性的變更。然後你處理回饋。

我在 Rust 中重寫了 Bun,使用 Claude Code 中的約 50 個動態工作流程在 11 天的時間內連續運作。

每個動態工作流程都是這樣的循環 - 工作流程:

  • 產生將 Zig 模式和類型對應到 Rust 模式和類型的移植指南
  • 將每個 .zig 文件機械地移植到 .rs 文件,匹配 PORTING.md 和 LIFETIMES.tsv
  • 修正每個 crate 的編譯器錯誤
  • bun testbun build 等子指令發揮作用
  • 讓Bun整個測試套件中的每個測試都通過
  • 幾個大型重構和清理過程

在這 11 天(及之後)的大部分時間裡,我監控工作流程 - 手動讀取輸出以檢查問題和錯誤,並提示 Claude 編輯循環以修復問題。

您如何審核新增了 100 萬行以上的 PR?您如何開始建立負責任地合併大量 LLM 編寫的程式碼所需的信心?

獨立於語言的測試套件,具有一百萬個斷言、對抗性程式碼審查,當出現問題時,修復生成程式碼的過程,而不是手動修復程式碼。

對抗性審查

對抗性審查要求 Claude(在單獨的上下文視窗中)詳盡地提出更改會產生錯誤或不起作用的原因。

分割上下文視窗

通常對人類來說,審查程式碼的人不是編寫程式碼的人。編寫程式碼的人想要合併程式碼,這可能會導致他們的操作在程式碼準備好之前就發布。

Claude也是一樣。編寫程式碼的Claude希望程式碼能夠被接受。進行審查的Claude想要找到程式碼中的問題。

1 個實施者,每個實施者有 2 個或更多對抗性審核者。審閱者唯一的工作:找出錯誤以及程式碼不起作用的原因。實施者不審查。審核者未實施。

bug 1 of 3 · 非同步關閉

它的背景:.zig 原始版本、移植計劃、它自己的推理

其上下文:僅差異。被告知假設程式碼是錯誤的。

src/runtime/api/bun/js_bun_spawn_bindings.rs · 編譯乾淨

對於 [spawned_stdout, spawned_stderr] 中的 stdio {

匹配標準輸入輸出{

StdioResult::Buffer(mut 管道) => {

// pipeline: Boxuv::Pipe — 將其交給 libuv 來關閉

pipe.close(Subprocess::on_pipe_close)

}

StdioResult::Fd(fd) => fd.close(),

StdioResult::不可用 => {}

}

}

uv_close 是非同步的:libuv 保留原始句柄指標直到下一個循環標記,然後呼叫 on_pipe_close,這會釋放分配。但是 `pipe` 是一個在匹配臂結束時掉落的 Box — libuv 保留已釋放的內存,然後 close 回調會第二次釋放它。釋放後使用,然後雙重釋放。

Box::leak(pipe).close(Subprocess::on_pipe_close)

f0a454376c7 · win-review: js_bun_spawn_bindings.rs 在異步 uv_close 之前洩漏 Boxuv::Pipe 以避免 on_pipe_close 中的 UAF/雙重釋放

敵對審閱者實際上發現了三個錯誤 - 每個引用的提交都在主題行中帶有其審閱屬性。三者都已編譯;這三個看起來似乎都有道理。審查者是自己的上下文視窗中的第二個Claude:它只獲得差異,沒有其他任何東西 - 沒有實現者的推理 - 並被告知找出錯誤的方式。程式碼是從引用的提交中濃縮而來的;相同的錯誤,相同的修復。

這看起來像什麼?

如果您要做一些大型且昂貴的事情,首先降低風險可以節省時間和金錢。

準備工作

在編寫任何程式碼之前,我花了大約 3 個小時與 Claude 討論如何將 Zig 程式碼庫中的模式緊密地映射到 Rust。 Claude 將這次討論序列化為 PORTING.md 文檔,最終發佈在 Hacker News 上。

下一個問題:如何將 Rust 生命週期加入手動管理記憶體的程式碼?

這就是我向 Claude 發出這樣的提示:

我:讓我們開始一個動態工作流程來分析程式碼庫中每個結構體欄位的正確生命週期。此工作流程應讀取每個文件中的每個結構欄位並追蹤控制流。首先,在 Rust 中尋找 express 具有複雜生命週期的結構體字段,然後為該字段提出一個生命週期,然後使用 2 個對抗性審查代理來審查該生命週期,然後應用任何反饋並序列化到 LIFETIMES.tsv 中以供其他Claude查看。

然後對 PORTING.mdLIFETIMES.tsv 進行一輪對抗性審查,以解決任何衝突的建議並仔細檢查所有內容。我也手動讀了一下。

試運轉

在要求 Claude 將所有 1,448 個 .zig 檔案轉換為 .rs 檔案之前,我只從 3 個檔案開始。對於這 3 個文件中的每一個,1 個實施者編寫了新的 .rs 文件,2 個敵對審查者檢查 .rs 文件與 .zig 文件的行為是否匹配,並且它遵循 PORTING.mdLIFETIMES.tsv。此後,1 名修復者採納了所有建議。

錯誤開始

我要求 Claude 在所有 1,448 個 .zig 檔案上循環工作流程,大約 2 分鐘後,一個 Claude 在提交之前運行了 git stash。另一個運行git stash pop。然後git reset HEAD --hard。他們互相踩著對方!如果我將每個 Claude 放入單獨的工作樹中,我將耗盡磁碟空間,因為 Bun 的 git 儲存庫太大,最終需要將更改一起編譯和查看。

因此,我要求 Claude 編輯工作流程,以指示 Claude 永遠不要運行 git stashgit reset 或任何不立即提交特定文件的 git 命令。也沒有cargo。根本沒有慢命令。

然後,Claude恢復了工作流程。它正在發揮作用!太慢了,所以我將其分成 4 個工作流程分片,每個分片都有自己的工作樹(總共 4 個工作樹),每個分片運行 16 個Claude提交和推送文件。

終於寫程式了

由於所有並行化和這些準備工作,Claude 在高峰期每分鐘編寫了大約 1,300 行程式碼。每行程式碼都由兩個獨立的敵對審閱者(也是Claude)進行審閱,並在提交之前經過一輪修復。絕對沒有任何效果。

11天×24小時·PDT

5,463 次提交

1695 次提交/小時

12am6am12pm6pm5 月 4 日,太平洋夏令時上午 7 點至上午 8 點 — 6 次提交,+89,278 行 5 月 4 日,太平洋夏令時上午 8 點至上午 9 點 — 2 次提交,+50,742 行令時上午 8 點至上午 9 點 — 2 次提交,+50,742 行令時上午 8 點至上午 9 點 — 2 次提交,+50,742 行令時上午 8 點至 9 點行 5 月 4 日,太平洋夏令時間上午 11 點至中午 12 點 — 1 點提交, +39,752 行 5 月 4 日中午 12 點至下午 1 點 PDT — 3 次提交,+251,616 行 5 月 4 日下午 1 點至下午 2 點 PDT 5 月4 點 PDT — 3 次提交,+136,381 行 5 月 4 日下午 5 點至下午 6 點 PDT — 5 次提交,+895 行 5 月 4 日,下午 6 點至下午 7 點(太平洋夏令時) — 5 次提交,+17,027 行令 5 月 7 點(太平洋夏令時) — 5 次提交,+17,027 行令 5 月 184 日晚上(太平洋)次提交,+106 行 5 月 4 日,晚上 9 點至晚上 10 點(太平洋夏令時)— 13 次提交,+11,661 行 5 月 4 日,晚上 11 點至上午 12 點(太平洋夏令時)— 6 次提交,+8,516 1 點 5 月 5 日行 5 月 5 日,太平洋夏令時間上午 1 點至凌晨 2 點 — 7 次提交,+1,577 行 5 月 5 日,上午 2 點至凌晨 3 點(太平洋夏令時間) — 4 次提交,+2,035 行 5 月 5 日,太平洋夏令時間) — 4 次提交,+2,035 行 5 月 5 日,太平洋夏令時間上午 3 點至 4 點4 點至上午 5 點 — 1 次提交,+2,796 行 5 月 5 日,上午 5 點至上午 6 點(太平洋夏令時間) — 2 次提交,+29,370 行 5 月 5 日,上午 8 點至上午 9 點(太平洋夏令時) — 2 次提交,+7 月夏— 2 次提交,+308 行 5 月 5 日,上午 11 點至中午 12 點(太平洋夏令時) — 2 次提交,+1,643 行 5 月 5 日,中午 12 點至下午 1 點(太平洋夏令時間) — 4 次提交, +1,452 夏令 5 月 1 點(太平洋夏令時間) — 4 次提交, +1,452 夏令 5 月 5 日下午(太平洋)次提交,+2,142 行 5 月 5 日,下午 2 點至下午 3 點(太平洋夏令時間) — 4 次提交,+7,787 行 5 月 5 日,下午 3 點至下午 4 點(太平洋夏令時間) — 2 次提交,+5,835 行 5 月 5 日,太平洋夏令時間) — 2 次提交,+5,835 行 5 月 5 日,下午夏令時間)行 5 月 5 日,下午 5 點至下午 6 點(太平洋夏令時間)— 4 次提交,+3,960 行 5 月 5 日,下午 6 點至晚上 7 點(太平洋夏令時) — 4 次提交,+9,179 行 5 月 5 日,晚上 7 點至晚上 8 點(太平洋8 點至晚上 9 點(太平洋夏令時) — 4 次提交,+18,902 行 5 月 5 日,晚上 9 點至晚上 10 點(太平洋夏令時間) — 43 次提交,+40,650 行太平洋夏令時間 5 日晚上 10 點至晚上 11 點 — 139. 12 點(太平洋夏令時) — 141 次提交,+34,814 行 5 月 6 日,太平洋夏令時間上午 12 點至凌晨 1 點 — 60 次提交,+10,417 行 5 月 6 日,太平洋夏令時間上午 1 點至凌晨 2 點 — 296 30 月,16 日凌晨 2 點點(太平洋夏令時間) — 306 次提交,+18,836 行 5 月 6 日,凌晨 3 點至凌晨 4 點(太平洋夏令時間) — 196 次提交,+10,245 行 5 月 6 日,上午 4 點至上午 5 點(太平洋夏令時間) — 86 5 月 6 日點(太平洋夏令時間) — 16 次提交,+289 行 5 月 6 日,上午 8 點至上午 9 點PDT — 5 次提交,+264 行 5 月 6 日,上午 9 點至上午 10 點 PDT — 458 次提交,+16,409 行 5 月 10 點 PDT — 458 次提交,+16,409 行 5 月 6 日次提交,+44,000 行 5 月 6 日,上午 11 點至中午 12 點 PDT — 102 次提交,+21,972 行 5 月 6 日,中午 12 點至下午 1 點 PDT — 19 次提交,+2,891 行 5 月 6 日,下午 15 點至太平洋月 6 日,下午 3 點至下午 4 點(太平洋夏令時間) — 64 次提交,+3,606 行 5 月 6 日,下午 4 點至下午 5 點(太平洋夏令時間) — 264 次提交,+60,132 行 5 月 6 日,下午 5 點至 60 點點至晚上 7 點(太平洋夏令時) — 281 次提交,+16,283 行 5 月 6 日,晚上 7 點至晚上 8 點(太平洋夏令時間) — 258 次提交,+26,654 行 5 月 6 日,晚上 8 點至晚上 9 點提交,196 月 319 月 195 月 196 日點至晚上 10 點(太平洋夏令時) — 74 次提交,+8,331 行 5 月 6 日,太平洋夏令時晚上 10 點至晚上 11 點 — 17 次提交,+2,200 行 5 月 6 日,晚上 11 點至上午 12 點 PDT — 11 行 5 月 6 日,晚上 11 點至上午 12 點 PDT — 11 點至凌晨 5 月點(太平洋夏令時間) — 17 次提交,+6,577 行 5 月 7 日,上午 1 點至凌晨 2 點 PDT — 22 次提交,+8,718 行 5 月 7 日,凌晨 2 點至凌晨 3 點PDT — 21 次提交,+11,392 點 5 月 5 月 3 月 5 月次提交,+6,476 行 5 月 7 日,上午 4 點至上午 5 點 PDT — 31 次提交,+2,356 行 5 月 7 日,上午 5 點至上午 6 點 PDT — 9 次提交,+1,787 行 5 月 7 日,上午 6 點 PDT — 9 次提交,+1,787 行 5 月 7 日,上午 6 點至上午 7 點PDT — 5 次提交,+181 行 5 月 7 日,上午 11 點至下午 12 點 PDT — 3 次提交,+421 行 5 月 7 日,中午 12 點至下午 1 點 PDT — 1 次提交,+13 行 5 月 7 日,下午 1 點至下午 52 點點至下午 3 點 PDT — 9 次提交,+2,131 行太平洋夏令時間 7 日下午 3 點至下午 4 點 — 51 次提交,+3,207 行 5 月 7 日下午 4 點至下午 5 點(太平洋夏令時) — 56 次提交,+2,647 夏令 5 月 5 月 5 月)次提交,+2,787 行 5 月 7 日,下午 6 點至下午 7 點(太平洋夏令時間) — 42 次提交,+1,590 行 5 月 7 日,晚上 7 點至晚上 8 點(太平洋夏令時間)— 46 次提交,+4,170 行 5 月 7 日晚上,夏次提交,+2,113 行 5 月 7 日,晚上 9 點至晚上 10 點(太平洋夏令時間) — 27 次提交,+1,585 行 5 月 7 日,晚上 10 點至晚上 11 點(太平洋夏令時間) — 27 次提交,+2,231 11 點 5 月時次提交, +4,987 行 5 月 8 日,太平洋夏令時間上午 12 點至凌晨 1 點 — 27 條提交,+1,196 行 5 月 8 日,凌晨 1 點至凌晨 2 點(太平洋夏令時間) — 14 條提交,+904 1 點至凌晨 2 點(太平洋夏令時間) — 14 條提交,+904 行 5 月 8 日,上午 25 點至太平洋月 8 日,上午 3 點至凌晨 4 點(太平洋夏令時間) — 13 條提交,+253 行 5 月 8 日,凌晨 4 點至凌晨 5 點PDT — 3 次提交,+771 行 5 月 8 日上午 5 點至上午 6 點 PDT — 15 次提交,+1,5 月 8 日上午 5 點至上午 6 點 PDT — 15 次提交,+1,545 行次提交,+1,965 行 5 月 8 日,上午 7 點至上午 8 點 PDT — 14 次提交,+1,866 行 5 月 8 日,上午 8 點至上午 9 點 PDT — 55 次提交,+3,622 行8 日上午 9 點至上午 9 點 PDT — 55 次提交,+3,622 行8 日上午 9 點至上午 9 點 PDT — 55 次提交,+3,622 行點至上午 11 點 PDT — 1 次提交,+0 行 5 月 8 日,中午 12 點至下午 1 點 PDT — 1 次提交,+116 行 5 月 8 日下午 1 點至下午 2 點 PDT — 2 次提交,+66 行 5 月 8 日,下午 2 點至 5 月點至 4 點(太平洋夏令時) — 26 條提交,+1,691 行 5 月 8 日,下午 4 點至下午 5 點(太平洋夏令時間) — 18 條提交,+2,751 行 5 月 8 日,下午 5 點至下午 6 點(太平洋夏令時) — 2 點至 5 月 8 日,下午 5 點至下午 6 點(太平洋夏令時) — 2 點至 5 月夏— 2 筆提交,+135 行 5 月 8 日,下午 7 點至晚上 8 點(太平洋夏令時間) — 11 條提交, 5 月 8 日晚上 8 點至晚上 9 點(太平洋夏令時)+1,763 行 — 20 次提交,+5,272 行 5 月 8 日晚上 198 日晚上 195 點(8 日晚上 195 點)行 5 月 8 日晚上 10 點至晚上 11 點(太平洋夏令時)— 2 次提交,+334 行 5 月 8 日晚上 11 點至上午 12 點(太平洋夏令時)— 6 次提交,+2,033 行 5 月 9 日,5 月 9 日,上午 12 點至凌晨 12 點至凌晨 5 月 9 月日,上午 1 點至凌晨 2 點 PDT — 9 次提交,+723 行 5 月 9 日,凌晨 2 點至凌晨 3 點 PDT — 8 次提交,+98 行 5 月 9 日,上午 3 點至凌晨 4 點 PDT — 63 次提交,+2,538 DT 5 月 5 月 18 點至 18 日提交行9 日上午 5 點至上午 6 點 PDT — 4 次提交,+42 行 5 月 9 日上午 6 點至上午 7 點 PDT — 3 次提交,+2,616 行 5 月 9 日,上午 7 點至上午 8 點 PDT — 6 次提交,+6,993 行 — 5 月 18 點 5 月次提交,+3,705 行 5 月 9 日,上午 9 點至上午 10 點 PDT — 11 次提交, +199 行 5 月 9 日上午 11 點至中午 12 點 PDT — 1 次提交,+23 行 5 月 9 日下午 12 點 PDT — 1 次提交,+23 行 5 月 9 日下午 12 點至下午 1 點 PDT. 2 點 PDT — 7 次提交,+2,080 行 5 月 9 日下午 2 點至下午 3 點 PDT — 6 次提交,+924 行 5 月 9 日下午 3 點至下午 4 點 PDT — 5提交,+248 行 5 月 9 日,下午 4 點至 58 月日,下午 5 點至下午 6 點(太平洋夏令時間) — 2 次提交,+135 行 5 月 9 日,下午 6 點至下午 7 點(太平洋夏令時) — 4 次提交,+822 行 5 月 9 日,下午 7 點至晚上 8 點(太平洋夏令時) — 10 月,10 日1 點(太平洋夏令時間)— 4 次提交,+497 行 5 月 10 日,太平洋夏令時上午 1 點至凌晨 2 點 — 2 次提交,+35 行 5 月 10 日,太平洋夏令時間上午 2 點至凌晨 3 點 — 1 次提交,+131 行 5 月 10 日凌晨,10 日凌晨 2 點行 5 月 10 日,太平洋夏令時間上午 4 點至上午 5 點 — 1 次提交,+3 行 5 月 10 日,太平洋夏令時間上午 5 點至上午 6 點 — 1提交,5 月 10 日上午 6 點至上午 7 點(太平洋夏令時)+26 行 — 2 點1 次提交,5 月 10 日上午 8 點至上午 9 點(太平洋夏令時間)+5 行 — 4 次提交,+78 行 5 月 10 日,太平洋夏令時上午 9 點至上午 10 點 — 1 次提交,+1 行 5 月 10 日太平洋夏令時間 10 點 — 1 次提交,+1 行 5 月 10 日太平洋夏令時間11 點至中午 12 點 PDT — 1 次提交,+4 行 5 月 10 日,中午 12 點至下午 1 點 PDT — 2 次提交,+413 行 5 月 10 日,下午 1 點至下午 2 點 PDT — 1 次提交,+25 行 5 月 10 日至下午 2 點 PDT — 1 次提交,+25 行 5 月 10 日下午 3 5 月,下午 25 點10 日,下午 3 點至下午 4 點 PDT — 6 次提交, +1,172 行 5 月 10 日下午 4 點至 5 點 PDT — 4 次提交,+752 行 5 月 10 日下午 5 點至下午 6 點 PDT — 3 次提交,+227 行 5 月5 月 10 日下午 7 點至晚上 8 點 PDT — 1 次提交,+306 行 5 月 10 日下午 8 點至晚上 9 點 PDT — 1提交,+54 行 5 月 10 日,晚上 9 點至晚上 10 點 PDT — 2 次提交,+75 10 日,晚上 9 點至晚上 10 點 PDT — 2 次提交,+75 1 5 月 10 點次提交,+134 行 5 月 10 日,晚上 11 點至上午 12 點 PDT — 5 次提交,+103 行 5 月 11 日,12 點至凌晨 1 點 PDT — 2 次提交,+150 行 5 月 11 日,凌晨 1 點 PDT — 2 次提交,+150 行 5 月 11 日,凌晨 1 點 PDT — 2 次提交,+150 行 5 月 11 日,凌晨 1 點 PDT 2 點提交,+150 行 5 月 11 日3 點 PDT — 2 次提交,+364 行 5 月 11 日,上午 3 點至凌晨 4 點 PDT — 3 次提交,+44 行 5 月 11 日,上午 4 點至上午 5 點 PDT — 7 次提交,+9,367 行 5 月 11 日,上午 6 1日上午 7 點至上午 8 點 — 2 次提交,+149 行 5 月 11 日上午 8 點至上午 9 點(太平洋夏令時間) — 10 次提交,+2,171 行 5 月 11 日,太平洋夏令時上午 9 點至上午 10 點 — 16 次 11 日,太平洋夏令時間上午 9 點至上午 10 點 — 16 次 11 月 5 月 5 月點 — 18 次提交,+3,356 行 5 月 11 日,上午 11 點至中午 12 點(太平洋夏令時) — 9 次提交,+861 行 5 月 11 日,下午 12 點至下午 1 點(太平洋夏令時間) — 3 次提交,+412 12 點至下午 1 點(太平洋夏令時間) — 3 次提交,+412 12 點至下午 1 點至 1 月次提交,+2,978 行 5 月 11 日,下午 2 點至下午 3 點(太平洋夏令時間) — 157 次提交,+10,700 行 5 月 11 日,下午 3 點至下午 4 點(太平洋夏令時)— 16 次提交,+1,346 1 5 月次提交,+78 行 5 月 11 日,下午 5 點至下午 6 點(太平洋夏令時間) — 41 次提交,+2,568 行 5 月 11 日,下午 6 點至下午 7 點(太平洋夏令時間) — 55 次提交,+4,912 行至下午 7 點(太平洋夏令時間) — 55 次提交,+4,912 行 5 月 11 日晚上,下午 5 點(太平洋)次提交,+3,475 行太平洋夏令時 11 日晚上 8 點至晚上 9 點 — 32 次提交,+1,732 行 5 月 11 日晚上 9 點至晚上 10 點(太平洋夏令時間) — 46 次提交,+4,506 行 5 月 11 日,晚上 10 點至太平洋時間次提交,+1,711 行 5 月 11 日,晚上 11 點至上午 12 點(太平洋夏令時) — 52 次提交,+10,850 行 5 月 12 日太平洋夏令時 12 日上午 12 點至凌晨 1 點 — 30 次 12 日太平洋夏令時 12 日上午 12 點至凌晨 1 點 — 30 次提交,+3,760 夏次提交,+9,443 行 5 月 12 日上午 2 點至凌晨 3 點(太平洋夏令時間) — 41 次提交,+1,635 行 5 月 12 日上午 3 點至凌晨 4 點(太平洋夏令時間) — 39 次提交,+788 行 3 點至凌晨 4 點(太平洋夏令時間) — 39 次提交,+788 行 5 月 12 月 5 月12 日上午 5 點至上午 6 點 PDT — 23 次提交,+779 行 5 月 12 日上午 6 點至上午 7 點 PDT — 1 次提交,+137,576 行 5 月 12 日上午 7 點至上午 8 點 PDT — 2 次提交,+81 行行 5 月 12 日,上午 9 點至上午 10 點(太平洋夏令時) — 2 次提交,+130 行 5 月 12 日,上午 10 點至上午 11 點(太平洋夏令時間) — 5 次提交,+160 行 5 月 12 日,上午 11 點至 12 月 20 月 12 月)日,下午 12 點至下午 1 點(太平洋夏令時間) — 1 次提交,+2 行 5 月 12 日,下午 1 點至下午 2 點(太平洋夏令時)— 30 次提交, +2,677 行 5 月 12 日下午 2 點至 3 點 PDT — 412 點,10 月 74 月 5 月次提交,+200 行 5 月 12 日下午 4 點至下午 5 點 PDT — 27 次提交,+1,423 行 5 月 12 日下午 5 點至下午 6 點 PDT — 19 次提交,+1,055 行 5 月 12 日,下午 6 點至下午 7 次提交,+1,055 行 5 月 12 日,下午 6 點至 7 月點至晚上 8 點 PDT — 2 次提交,+84 行 5 月 12 日,晚上 9 點至晚上 10 點 PDT — 7 次提交,+273 行 5 月 12 日,晚上 10 點至晚上 11 點 PDT — 3 次提交,+230 行 5 月 12 日晚上 11 點 PDT — 3 次提交,+230 行 5 月 12 日晚上 19 點數5 月 13 日,太平洋夏令時間 12 點至凌晨 1 點 — 2 次提交,+133 行 5 月 13 日,上午 1 點至凌晨 2 點(太平洋夏令時間) — 14 次提交,+2,177 行 5 月 13 日,上午 2 點至凌晨 3 點(太平洋夏令點至上午 5 點(太平洋夏令時) — 10 次提交,+ 657 行 5 月 13 日,上午 5 點至上午 6 點PDT — 1 次提交,+687 行 5 月 13 日,上午 6 點至上午 7 點 PDT — 11 次提交,+380 行 5 月 7次提交,+5,247 行 5 月 13 日,上午 8 點至上午 9 點 PDT — 14 次提交,+1,051 行 5 月 13 日,上午 9 點至上午 10 點 PDT — 7 次提交,+680 行13 日,上午 10 點至太平洋11 點日,上午 11 點至下午 12 點(太平洋夏令時) — 6 次提交,+314 行 5 月 13 日,中午 12 點至下午 1 點(太平洋夏令時間) — 10 次提交,+2,980 行 5 月 13 日,下午 1 點至下午 2 點(太平洋夏令時間,+ 10 日下午 5 點至下午 2 點) 3 點(太平洋夏令時) — 3提交,+439 行 5 月 13 日,下午 5 點至下午 6 點(太平洋夏令時間) — 7 次提交,+114 行 5 月 13 日,下午 6 點至晚上 7 點(太平洋夏令時) — 4 行 5 月 13 日,下午 6 點至晚上 7 點(太平洋夏令時) — 4 行 5 月 13 日,下午 6 點至晚上 7 點(太平洋夏令時) — 4 次提交,+605 行 5 月 5 月)次提交,+13 行 5 月 13 日,晚上 10 點至晚上 11 點(太平洋夏令時間) — 1 次提交,+48 行 5 月 13 日,晚上 11 點至上午 12 點(太平洋夏令時間) — 1 次提交,+8 行5 月 14 日上午 14 日行

連接埠分支上的每次提交(排除合併),按小時儲存。尖峰時段:695 次提交。

注意到不一致的時間了嗎?我忘記增加運行該執行個體的 EC2 執行個體的預設 IOPS。只需一條緩慢的 grep 命令即可使磁碟讀寫凍結幾分鐘。

作為工作佇列的編譯器錯誤

寫完所有程式碼後,我要求 Claude 寫一個工作流程來修復每個編譯器錯誤。我們一個箱子一個箱子地走。

≈15,125 個錯誤

太平洋夏令時間 5 月 6 日星期三凌晨 1:29

errors.txt88 修復提交

錯誤:src/event_loop/SpawnSyncEventLoop.rs

錯誤:src/sys/lib.rs

錯誤:runtime/timer/TimerObjectInternals.rs

錯誤:src/runtime/ffi/ffi_body.rs

錯誤:src/http/lib.rs

錯誤:src/js_parser/ast/Parser.rs

錯誤:JSPromise.rs

錯誤:standalone_graph/StandaloneModuleGraph.rs

錯誤:src/http_jsc/websocket_client.rs

錯誤:doStep5.rs

錯誤:src/install/PackageManager.rs

平分·64個Claude

工作樹1

→→

→→

→→

→→

工作樹2

→→

→→

→→

→→

工作樹3

→→

→→

→→

→→

工作樹4

→→

→→

→→

→→

1 個修復2 個審核1 個適用

→ 每箱承諾土地

bun_bundler0

bun_js_parser17

小圓麵包_css10

小圓麵包_http3

麵包_sys3

bun_alloc1

bun_sourcemap3

小圓麵包_ini0

bun_analytics1

小圓麵包_zlib0

· 階段 d(sql_jsc/mysql/protocol):修正導入、ReaderContext 邊界、WTFStringImpl 幫助程式

· 階段 d(tier0):bun_sourcemap parse_json:BabyList.len 字段,Option<StoreRef> 展開

· 階段 d(jpa):skipTypescript.rs — 移植實體,刪除 _draft/todo 存根

D 階段如何運作,從其 1,610 個真實提交(太平洋夏令時間 5 月 6 日)重播:cargo check 將 大約 16,000 個錯誤寫入文件,按 crate 分組;工作流程將它們分配給 64 個 Claude,即 4 個工作樹上的 16 個工作流程將它們分配給 64 個 Claude,即 4 個工作樹上的 16 個循環,每個 Claude。每個晶片都是一批真正的提交:它落在其實際的板條箱上,然後計數器才會移動。錯誤行是真正的提交主題。

最棘手的錯誤是循環依賴。

我們的 Zig 程式碼庫是一個編譯單元(其實是一個 crate)。我想將新的 Rust 程式碼庫拆分為 ~100 個板條箱,以便 Rust 編譯速度更快,但這需要避免循環依賴,同時最大限度地減少與原始 Zig 實作相比的變更。 我的 PR 在開始 Rust 重寫之前立即執行此操作是不夠的。我沒有重新開始,而是運行了另一個工作流程來對具有循環依賴關係的程式碼進行分類並將其全部寫下來,然後運行另一個工作流程來進行重構。

修正循環相依性發現大約 16,000 個編譯器錯誤。對於 1 個人來說這是一個巨大的數字,但同時對 64 個Claude來說並不是一個瘋狂的數字。

為了最大限度地提高並行性,工作流程在每個 crate 上循環。

  • 對於每個 crate,執行 cargo check,按檔案分組輸出並將錯誤儲存到檔案
  • 修正該套件中的所有編譯器錯誤
  • 2 個針對板條箱更改的敵對審查者
  • 1 個修復程式應用程式修復

為了防止Claude互相踩踏,cargo check只在開始時運行,並且像其他運行一樣,直到最後才運行git

Another false start

Claude 將「讓我們編譯所有的套件」解釋為「刪除有編譯錯誤的函數」。Claude也開始在文件變通辦法中加入可疑的長解釋性註釋,因此我加入了這條規則以供敵對審閱者拒絕:

如果您需要一段長的註解來證明解決方法可行的原因,則程式碼是錯誤的 - 請修復程式碼。

一次快速編輯,幾個小時後,這些事情就不再發生了。

冒煙測試

模特兒喜歡說「冒煙測試」

一旦cargo check通過,接下來就是編譯並執行bun --version。它有鏈接器錯誤。然後,它在啟動時立即出現恐慌。

下一個目標是讓它運行bun test <file>。一旦成功,我們就可以開始執行測試了!是時候進行另一個工作流程了,循環 Bun 的 CLI 子命令:

  • 將每個失敗的堆疊追蹤及其子命令儲存到檔案中
  • 對於按子命令分組的每個失敗堆疊跟踪,進行 1 個 Claude 修復
  • 2 個敵對審稿人
  • 1 個修復者應用了建議

讓測試套件在本地通過

此工作流程在測試文件上循環。

運行大約 100 個隨機測試文件,這些文件按程式碼庫中的資料夾分片到 4 個工作樹之一。對於每個失敗的測試,將堆疊追蹤和錯誤儲存到檔案中,1 個實施者提出修復方案,2 個敵對審閱者,然後 1 個修復者套用。

更多錯誤的開始

我們的測試套件有大量記憶體洩漏測試和一些可能需要一分鐘以上的整合測試 - 例如:運行 next dev 並檢查熱模組重新載入的測試可以拾取更改 100 次。其中一些測試在調試版本中超時。

我們也進行了壓力測試,用於耗盡電腦上最大數量的 TCP 套接字、對磁碟讀取和寫入 GB 位元組的測試,以及產生約 10k 進程的測試。

這需要比「請」更強的隔離,因此我們使用 systemd-run (cgroups) 來限制記憶體和 CPU 使用並隔離 pid 命名空間。機器磁碟空間不足並且崩潰了好幾次。

取得 CI 中通過的測試套件

第一次 CI 運行兩天后,失敗清單從 972 個測試檔案減少到 23 個。一天半後,Linux 完全變綠 — 第一次,感覺這個 Rust 重寫實際上可以發揮作用。

0 / 6 平台綠色

版本 #53047 · 太平洋夏令時間 5 月 9 日星期六上午 11:52

macOS x64 · 2 個分片

build #52897:碎片故障build #52932:碎片故障build #52934:碎片故障build #52938:碎片故障build #52944:碎片故障build #52946:碎片故障build #52949:碎片故障 build #52946:碎片故障build #52949:碎片故障故障:dbuild #53007:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53035:碎片故障build #53041:碎片故障build#53047:碎片故障build#53056:碎片故障build#53077:碎片故障build#53090:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53133:碎片故障build #53131:碎片故障build #5313 #53143:分片失敗build #53149:所有分片均已通過build #53159:所有分片失敗build #53164:所有分片均已通過build #53167:所有分片均已通過build #53172:所有分片均通過#53194:所有分片均已通過build #53208:所有分片均通過build #53213:分片失敗build#53214:分片失敗build#53216:沒有失敗(部分運行)build#53222:所有分片都通過了build#53229:沒有失敗(部分運行)build#53241:所有分片都通過了build#53265:所有分片都通過了build#53271:所有分片都通過了build#53304:碎片故障build #53327:碎片故障build #53340:碎片故障build #53401:碎片故障build #53431:碎片故障build #53491:所有碎片通過build #53503:所有碎片通過build #53748:所有碎片通過build #53503:所有碎片通過build #53748:所有碎片通過build #53503:所有碎片通過build #53748 #53811:所有分片通過build #53933:沒有失敗(部分運行)build #53952:所有分片通過build #53983:分片失敗build #53992:分片失敗build #53999:分片失敗build #53992:分片失敗build #53999:分片失敗4build #53992:分片失敗build #53999:分片失敗4build #54012:所有分片失敗。 #54017:所有分片通過build #54022:分片失敗build #54026:分片失敗build #54033:所有分片都透過build #54040:所有分片透過build #54047:分片失敗build #54040:所有分片都透過build #54047:分片失敗build #54049:6 #54064:所有分片透過build #54074:所有分片通过build#54093:所有分片通过build#54144:没有失败(部分运行)build#54161:分片失败build#54186:分片失败build#54189:分片失败build#54196:分片失败build#54202:所有分片通过

Linuxarm64·60個分片

build #52934: shard failuresbuild #52938: shard failuresbuild #52944: shard failuresbuild #52969: shard failuresbuild #52975: shard failuresbuild #52980: shard failuresbuild #52988: shard failuresbuild #52996: shard failuresbuild #52998: shard failuresbuild #53007: shard failuresbuild #53013: shard failuresbuild #53014: shard failuresbuild #53015: shard failuresbuild #53026: shard failuresbuild #53027: shard failuresbuild #53031: no failures (partial run)build #53032: shard failuresbuild #53035: shard failuresbuild #53041: shard failuresbuild #53047: shard failuresbuild #53056: shard failuresbuild #53059: shard failuresbuild #53077: shard failuresbuild #53083: shard failuresbuild #53086: shard failuresbuild #53090: shard failuresbuild #53095: shard failuresbuild #53106: shard failuresbuild #53109: shard failuresbuild #53123: shard failuresbuild #53127: shard failuresbuild #53130: shard failuresbuild #53131: shard failuresbuild #53133: shard failuresbuild #53134: shard failuresbuild #53135: shard failuresbuild #53143: shard failuresbuild #53149: shard failuresbuild #53159: shard failuresbuild #53164: shard failuresbuild #53167: all shards passedbuild #53172: shard failuresbuild #53176: all shards passedbuild #53188: shard failuresbuild #53194: shard failuresbuild #53208: all shards passedbuild #53212: shard failuresbuild #53213: shard failuresbuild #53214: shard failuresbuild #53216: all shards passedbuild #53222: all shards passedbuild #53229: all shards passedbuild #53236: no failures (partial run)build #53241: all shards passedbuild #53260: all shards passedbuild #53265: all shards passedbuild #53271: all shards passedbuild #53280: no failures (partial run)build #53298: no failures (partial run)build #53304: shard failuresbuild #53327: all shards passedbuild #53340: all shards passedbuild #53360: no failures (partial run)build #53419: shard failuresbuild #53431: shard failuresbuild #53458: no failures (partial run)build #53485: shard failuresbuild #53491: shard failuresbuild #53503: shard failuresbuild #53514: no failures (partial run)build #53570: shard failuresbuild #53583: shard failuresbuild #53599: shard failuresbuild #53748: shard failuresbuild #53753: all shards passedbuild #53762: no failures (partial run)build #53787: no failures (partial run)build #53811: all shards passedbuild #53852: no failures (partial run)build #53863: shard failuresbuild #53893: shard failuresbuild #53914: all shards passedbuild #53933: all shards passedbuild #53952: all shards passedbuild #53983: shard failuresbuild #53992: shard failuresbuild #53999: shard failuresbuild #54008: no failures (partial run)build #54012: all shards passedbuild #54015: all shards passedbuild #54017: all shards passedbuild #54022: shard failuresbuild #54026: all shards passedbuild #54030: shard failuresbuild #54033: all shards passedbuild #54040: all shards passedbuild #54047: shard failuresbuild #54049: shard failuresbuild #54055: shard failuresbuild #54057: shard failuresbuild #54064: all shards passedbuild #54074: all shards passedbuild #54083: no failures (partial run)build #54093: all shards passedbuild #54144: all shards passedbuild #54161: shard failuresbuild #54186: shard failuresbuild #54189: shard failuresbuild #54196: shard failuresbuild #54202: all shards passed

Linux x64 · 60 個分片

build #52934:碎片故障build #52938:碎片故障build #52944:碎片故障build #52969:碎片故障build #52975:碎片故障build #52988:碎片故障build #52996:碎片故障3d #53013:碎片故障build#53014:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#5 3032:碎片故障build#53033:碎片故障build#53035:碎片故障build#53041:碎片故障build#53047:碎片failurebuild #53056:分片故障build #53059:無故障(部分運行)build #53077:分片故障build #53083:無故障(部分運行)build #53086:分片故障build #53090:分片故障build #53095:分片故障build #53090:分片故障build #53095:分片故障#53109:分片故障build #53123:碎片故障build#53127:碎片故障build#53130:碎片故障build#53131:碎片故障build#53133:碎片故障build#5 3134:碎片故障build#53135:碎片故障build#53143:碎片故障build#53149:碎片故障build#53159:碎片failurebuild #53164: 分片失敗build #53167: 所有分片均通過build #53172: 所有分片失敗build #53167: 所有分片均通過build #53172: 所有分片失敗build #53167: 所有分片均通過build #53188: 分片失敗build #53194: 所有分片通過build #53188: 分片失敗build #53194: 所有分片通過build #53188: 分片失敗build #53194: 所有分片通過build #53188: 分片失敗build #53194: 全部通過#53213: 分片failurebuild #53214: 分片失敗build #53216: 所有分片均已通過build #53222: 所有分片失敗build #53216: 所有分片均已通過build #53222: 所有分片均已通過build #53229: 所有分片均已通過build #53236: build #53229: 所有分片通過所有分片均已通過build #53265: 所有分片通過build #53271:所有分片都通過了build#53280:沒有失敗(部分運行)build#53304:分片失敗build#53327:所有分片都通過了build#53340:所有分片都通過了build#53360:沒有失敗(部分運行)build#53419:沒有失敗(部分運行)build#53431:分片失敗build#53458:沒有失敗(部分運行)build #53485:分片失敗build #53491:所有分片都已通過build #53503:所有分片都已通過build #53514:沒有失敗(部分運行)build #53570:分片失敗build #53583:分片失敗#53753:所有分片都通過build #53759:無故障(部分運行)build #53781:分片故障build #53787:無故障(部分運行)build #53811:所有分片故障build #53787:無故障(部分運行)build #53811:所有分片故障#53914:所有分片都已通過build #53933:分片故障build #53952:所有分片通過build #53983:分片失敗build #53992:分片失敗build #53999:分片失敗build #53992:分片失敗build #53999:分片失敗build #53992:分片失敗build #53999:分片失敗build #54008:通過失敗。 #54015:所有分片通過build #54017:所有分片通過build #54022:分片failurebuild #54026:所有分片均已通過build #54030:分片故障build #54033:所有分片均已通過build #54030:分片故障build #54033:所有分片都已通過build #54030:分片故障build #54033:所有分片已通過build #540540740000 #54049:分片故障build #54055:分片故障build #54057:分片故障build #54064:所有分片passbuild #54074:所有分片都已通過build #54083:沒有失敗(部分運行)build #54093:所有已分片#54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:全部分片已通過

macOS arm64 · 4 個分片

build #52897:碎片故障build #52929:碎片故障build #52932:碎片故障build #52944:碎片故障build #52975:碎片故障build #52996:碎片故障build #52998:碎片故障3d #53014:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53032:碎片故障build #53035:碎片故障build#53041:碎片故障build#53047:碎片故障build#53056:碎片故障build#53059:碎片故障build #53077:碎片故障build #53095:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53133:碎片故障build #53131:碎片故障build #5313 #53135:碎片故障build #53143:碎片故障build #53149:碎片故障build #53159:碎片故障build #53164:碎片故障build #53167:碎片故障build #53172:碎片故障build #53176:碎片故障3 #53194:碎片故障build #53208:碎片故障build#53212:碎片故障build#53213:碎片故障build#53214:碎片故障build#53216:碎片故障build #53222:碎片故障build#53229:沒有失敗(部分運行)build#53236:沒有失敗(部分運行)build#53241:所有分片都通過了build #53265:所有分片都通過build#53271:沒有失敗(部分運行)build#53280:沒有失敗(部分運行)build#53304:分片失敗build# 53327:沒有失敗(部分運行)build#53340:沒有失敗(部分運行)build#53360:沒有失敗(部分運行)build#53368:分片失敗build #53379:碎片故障build#53383:碎片故障build#53401:碎片故障build#53431:碎片故障build#53458:碎片故障build#5 3491:無故障(部分運行)build#53503:碎片故障build#53570:碎片故障build#53583:碎片故障build#53599:分片故障build #53601:分片故障build #53748:分片故障build #53753:所有分片故障build #53748:分片故障build #53753:所有分片均已通過build #53757:無故障(部分運行)build #53759:無故障(部分運行)build #53787:無故障100 #53952:無故障(部分運行)build #53992: 分片故障build #53999: 分片故障build #54007: 無故障(部分運行)build #54012: 所有分片均已通過build #54015: 分片故障#54026: 無故障(部分運行)build #54030: 分片故障build #54033:所有分片通過build#54040:分片故障build#54047:分片故障build#54049:分片故障build#54055:分片故障buil d#54057:分片故障build#54064:分片故障build#54074:分片故障build#54093:分片故障build#54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:所有分片均通過

Windows x64 · 8 個分片

build #53090:碎片故障build #53094:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障 build #53123:碎片故障build #53127:碎片故障1d #53133:碎片故障build#53134:碎片故障build#53135:碎片故障build#53143:碎片故障build#53149:碎片故障build #53159:碎片故障build#53164:碎片故障build#53167:碎片故障build#53172:碎片故障build#53176:碎片故障build #53188:碎片故障build #53194:碎片故障build #53208:碎片故障build #53212:碎片故障build #53213:碎片故障build #53214:碎片故障build #53216:碎片故障build #5322:碎片故障、碎片故障#53236:分片故障build #53241:分片故障build #53260:分片故障build #53265:分片故障build #53271:分片故障build #53280:分片故障build #53298:分片故障build #53280:分片故障build #53298:分片故障。 #53327:所有分片均已通過build #53340:所有分片均通過build #53360:沒有失敗(部分運行)build#53419:碎片故障build#53431:碎片故障build#53458:碎片故障build#53470:沒有失敗(部分運行)build#53485:碎片故障build#53491:沒有失敗(部分運行)build#53503:碎片故障build#53514:沒有失敗(部分運行)運行)build#53565:沒有失敗(部分運行)build#53570:碎片故障build#53599:碎片故障build#53745:碎片故障buil d#53748:碎片故障build#53753:碎片故障build#53757:碎片故障build#53759:碎片故障build#53762:碎片故障build #53769:碎片故障build#53781:碎片故障build#53787:碎片故障build#53808:碎片故障build#53811:碎片故障build# 53852:碎片故障build#53863:碎片故障build#53883:碎片故障build#53893:碎片故障build#53914:全部分片已通過build #53933:所有分片已通過build #53952:所有分片已通過build #53973:無故障(部分運行)build #53983:分片故障build #53992:分片故障build #53999:分片故障build #54002:分片故障build #53999:分片故障build #54002:無故障#54007: 分片故障build #54008: 無故障(部分運行)build #54012: 所有分片均已通過build #54015: 所有分片均已通過build #54017: 所有分片均已通過build #54022: 故障所有分片都通過build #54033: all分片通過build #54040:所有分片已通過build #54047:分片故障build #54049:分片故障build #54055:分片故障build #54057:54000057:54000057:54000057:5400 #54074:所有分片已通過build #54083:所有分片已通過build #54093:全部分片已通過build #54144:分片故障build #54161:分片故障build #54186:分片故障build #54161:分片故障build #54186:分片故障build #54189:分片故障build #54186:分片故障4build #54189:分片故障分數#54202:所有分片均已通過

Windowsarm64·8個分片

build #53090:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障 build #53127:碎片故障build #53130:碎片故障故障:dbuild #53135:碎片故障build#53149:碎片故障build#53159:碎片故障build#53164:碎片故障build#53167:碎片故障build#5 3172:碎片故障build#53176:碎片故障build#53188:碎片故障build#53194:碎片故障build#53208:碎片failurebuild #53212:分片故障build #53213:分片故障build #53214:分片故障build #53216:分片故障build #53222:分片故障build #53229:分片故障build #53236:無故障(分機故障#53260:分片故障build #53265: 分片故障build #53271: 分片故障build #53304: 分片故障build #53327: 所有分片均已通過build #53340: 所有分片運行無故障#53431: 分片故障build #53458:無故障(部分運行)建置#53485:碎片故障建置#53491:無故障(部分運行)構建#53503:無故障(部分運行)構建#53599:碎片故障構建#53748:碎片故障構建#53753:碎片故障構建53757:碎片故障#751375#375#37572:碎片故障#37752#3752#375#37757:碎片故障。 :碎片故障build#53808:碎片故障build#53811:碎片故障build#53852:碎片故障build#53863:碎片故障build#53883:碎片故障build#53893:碎片故障build#3914:沒有失敗(3914163部分) #53952:所有分片通過build #53983:分片失敗build #53992:分片失敗build #53999:分片失敗build #54007:沒有失敗(部分運行)build #54012:所有分片通過4build #54015:所有分片通過4012:所有分片通過40bu #54022:分片failurebuild #54026:所有分片均已通過build #54030:無故障(部分運行)build #54033:所有分片均已通過build #54040:所有分片均已通過build #540047:54099 #54055:無故障(部分運行)build #54057:分片故障build #54064:全部分片已透過build #54074:所有分片已通過build #54083:無故障(部分運行)build #54093:所有分片已通過build #54083:無故障(部分運行)build #54093:所有分片已通過build #54083:無故障(部分運行)build #54093:所有分片都已通過141614141 #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:全部分片已通過

每個 CI 建構的測試分片(按平台)跨 135 個運行測試的建置(420 個從 BuildKite 中挖掘)。亮綠色:每個碎片都通過了。暗綠色:沒有失敗,但運行被縮短(被取代)。紅色:至少有一個分片發生故障。當其完整套件首次通過時,每個通道都會被標記——Linux 的 60 個分片比 Windows 早幾乎一整天都是綠色的。平台一直呈現紅色搖晃,直到最後一次失敗的測試落下;最終的全綠色版本是#54202。

合併之前的其餘時間很簡單。循環修復每個平台的 CI 測試失敗的工作流程,直到不再出現測試失敗。用於 Windows 相關清理的多個工作流程,用於刪除重複程式碼、減少不安全使用以及一般清理一些程式碼。

合併 Rust 重寫

一旦 Bun 的測試套件 100% 在所有平台上通過 CI(並且我手動驗證了測試實際上正在運行並且沒有被跳過),我在本地運行了一堆命令來測試東西 - 然後我按下了合併按鈕。

合併到 main 不是版本控制的版本。此時,我有足夠的信心繼續前進並致力於重寫,但還沒有足夠的信心來發布它。

統計資料

在高峰期,我們同時運行 4 個工作流程,每個工作流程都在一個單獨的工作樹中,每個工作流程有 16 個 Claudes。一次大約 64 個Claude。

git log · claude/phase-a-portpeak:一分鐘內 58 次提交

52

提交

+796,601

已寫入的行,包括重寫

太平洋夏令時間 5 月 5 日星期二中午 12:39

第一個 100 個檔案草稿批次PR #30412 opensmerged

·a階段:草稿批次head-1(100個檔案)+12,379−0

· b1 階段:路徑 + 字串編譯(閘草稿;最小編碼/字串/詞法分析器表面)+2,312−2,149

所有 6,502 次提交(不包括合併)均已重播。粉紅色條大多是新程式碼;青色條大部分是刪除。行計數器對沿途的每次重寫進行計數-到達的差異是+1,009,272。日誌是真實的提交訊息。

0 個測試已跳過或刪除

11 天(5 月 3 日 → 5 月 14 日合併)· 6,778 次提交

平台expect() 呼叫測試文件
Debian 13 x641,386,82660,6244,174
macOS 14 arm641,259,95358,8504,175
Windows 2019 x641,007,54457,3374,173

合併前,這需要 59 億個未快取的輸入代幣、6.9 億個輸出代幣和 720 億個快取的輸入代幣讀取 — 按 API 定價約 16.5 萬美元。從手工角度來看,我認為這需要 3 名具有程式碼庫完整背景的工程師大約一年的時間,在此期間我們將無法提高 Node.js 相容性、修復錯誤、修復安全問題或實現新功能。我們永遠不會那樣做。現實的選擇是什麼也不做,並永遠修復本文頂部的錯誤。

這是當今可能實現的最前線。我使用了 Claude Fable 5 的預發行版本,這是 Mythos 級模型。 Claude Code 的動態工作流程使 64 個 Claudes 運行了 11 天(否則我必須編寫自己的工具才能實現這一點)。

工作繼續

自合併 Rust 連接埠以來,我們已完成 Claude Code 安全性 的 11 輪安全審查並解決了調查結果。

我們也為 Bun 中的每個解析器添加了 24/7 覆蓋引導模糊測試 - JavaScript、TypeScript、JSX、CSS、JSON5、JSONC、TOML、YAML、Markdown、INI、Bun Shell 腳本、semver 範圍、.patch 檔案和 CSS 顏色。模糊器會自動將其發現的錯誤傳送給 Claude,以提交 PR 重現和修復,然後由人工審核 PR。到目前為止,它已經執行了我們的解析器 1000 億次,從而產生了大約 15 個 PR。

在撰寫本文時,大約 4% 的 Bun 的 Rust 程式碼位於 unsafe 區塊內(~13,000 個 unsafe 關鍵字跨 ~27,000 行/~780,000 行),而這些區塊中的 7% 則是來自於 C++ 的單行。我預計這個數字會隨著時間的推移而下降,因為我們從忠實的 Zig 端口(沒有 grepable unsafe 關鍵字)重構為慣用的 Rust,但我們將繼續使用像 JavaScriptCore 這樣的 C 和 C++ 庫,因此它總是比純 Rust 項目擁有更多的 unsafe更多。

移植錯誤

Rust 重寫的重點是穩定性,但不可能進行像這樣的大規模更改並引入零回歸。

此重寫引入了 19 個已知的迴歸,每一個都已修復。

大多數迴歸都來自兩種語言中語法相同但語意不同的程式碼。

debug_assert! 內的副作用

這兩個片段看起來相似,但行為不同。 Zig 的 assert 是一個函數,因此它的參數在每個建置中運行。 Rust 的 debug_assert! 是一個宏,因此在發布版本中,整個 expression 都被刪除,包括 insert_stale 呼叫。

// Zig:
if (dev.framework.react_fast_refresh) |rfr| {
    assert(try dev.client_graph.insertStale(rfr.import_source, false) == IncrementalGraph(.client).react_refresh_index);
}

// Rust:
if let Some(rfr) = &dev.framework.react_fast_refresh {
    debug_assert!(dev.client_graph.insert_stale(&rfr.import_source, false)? == react_refresh_index);
}

insert_stale 將檔案新增至前端開發伺服器的熱重載圖表。在發布版本中,它停止運行,並且在某些情況下,對於具有使用 React 的 HTML 路由的項目,HMR 會中斷,而熱重新載入的檔案會失效:Cannot destructure property 'isLikelyComponentType' of 'k'。調試建置有效。 #30678

Slices of odd length

Bun 的 Zig 幫助器 reinterpretSlice(u16, bytes)(支援切片的內建強制轉換)使用了 @divTrunc 並忽略了尾隨奇數字節。 bytemuck::cast_slice 反而對此感到恐慌。 Blob.text() 在 UTF-16 位元組順序標記上,後面跟著奇數個位元組,停止返回字串並使進程陷入恐慌。我們又繼續忽略奇數字節:&buf[..buf.len() & !1]#31188

邊界檢查

在 macOS 和 Linux 上,我們使用 ReleaseFast 編譯了 Bun 的 Zig 程式碼,這消除了邊界檢查。 Rust 的發布版本保留了它們。

Bun 的模組解析器將長檔名實習到溢位區塊的全域清單中。原始的 Zig 程式碼將每個區塊的大小設定為 count / 4 或 2048。連接埠留下了一個佔位符:

/// ... so use a nonzero stand-in until Phase B threads the
/// per-instantiation value through.
pub const BSS_OVERFLOW_BLOCK_SIZE: usize = 64;

這將上限從 840 萬個實習文件名降低到了實際項目所達到的 270,272 個,並使我們從 Zig 移植的 ptrs[4095] 可以訪問。 Rust 驚慌失措而不是寫到最後。如果我們使用 ReleaseSafe (我們只在 Windows 上這樣做),在這種情況下 Zig 也會出現恐慌。 #31503

comptime 格式字串

Output.pretty<r><d> 顏色標記改寫為 ANSI 轉義。在 Zig 中,fmtcomptime,因此在替換參數之前標記就消失了。 Rust 函數沒有 comptime 參數,因此 Output::pretty 只能看到完成的字串,並重寫參數上的標記。

// Zig:
pub inline fn pretty(comptime fmt: string, args: anytype) void;
Output.pretty("<r>{f}<r>", .{hyperlink});

// Rust:
pub fn pretty(payload: impl PrettyFmtInput);
Output::pretty(format_args!("<r>{}<r>", hyperlink));

bun update -i 將套件名稱列印為 OSC 8 超鏈接,以 ESC \ 終止。此反斜線位於尾隨 <r>< 之前,標記解析器會吃掉它,並且 r 將列印為文字。

在Rust中它必須是一個巨集:bun_core::pretty!("<r>{}<r>", hyperlink)#30693

Bun 在 Rust 中更好

到目前為止,Bun v1.4.0 修正了 v1.3.14 中重現的 128 個錯誤。這些問題包括記憶體洩漏、崩潰、幫助文字顏色錯誤等。

減少記憶體使用

Rust有一個強大的語言層清理記憶體的工具:Drop。實作Drop後,每次值超出範圍時都會自動呼叫drop函數。

impl Drop for Bytes {
    fn drop(&mut self) {
        if !self.pinned.is_empty() {
            JSC__JSValue__unpinArrayBuffer(self.pinned);
        }
    }
}

在 Zig 中,defer 可用於在作用域末端執行程式碼:

const bytes: ArrayBuffer = try .fromPinned(global, value);
defer bytes.unpin();

在 Zig 中,需要將 defer 新增至可能需要清理的每個單獨的呼叫站點。很容易最終忘記清理(記憶體洩漏),或者在很少到達的錯誤處理程式碼中運行清理程式碼兩次(雙重釋放)。在 Rust 中,當該值不再可存取時,Drop 會自動運作 - 交換「無隱藏控制流」來防止常見的 footgun。

Drop 修正了 Bun 中與錯誤處理程式碼中的檔案路徑相關的多個記憶體洩漏。

我們修復了所有可偵測的記憶體洩漏

我們改進了 Bun 的 LeakSanitizer 集成,以追蹤所有本機程式碼記憶體分配

以下是一個範例:每個進程內 Bun.build() 呼叫都會洩漏數兆位元組的記憶體 - 已解析的來源文字和 AST 符號表比它們所屬的建置壽命更長。

// Bundle the same 60-module project 2,000 times in one process
for (let i = 0; i < 2_000; i++) {
  await Bun.build({
    entrypoints: ["./index.js"],
    minify: true,
    sourcemap: "external",
  });
}

在 Bun v1.3.14 中,每個建置都會永遠洩漏約 3 MB - 捆綁每個請求的開發伺服器等工具最終會耗盡記憶體。在 Bun v1.4.0 中,記憶體逐漸減少:

建置Bun v1.3.14Bun v1.4.0
5001,914 MB526 MB
1,0003,506 MB586 MB
1,5005,097 MB608 MB
2,0006,745 MB609 MB

在 Zig 中執行此操作的先前嘗試 未合併,因為缺少 Drop 的等效項使得更難以自信地進行合併。

較小的二進位大小

Rust 重寫中的初始變更將 Windows 上的二進位檔案大小減少了 3.8 MB,在 macOS 上減少了 5.5 MB,在 Linux 上減少了 6.8 MB。這主要是因為我們在 Zig 程式碼中使用了太多 comptime

在最初的縮小之後,團隊探索了更多減少二進位大小的機會,使用連結器優化(例如相同程式碼折疊)、從 ICU 中刪除未使用的資料以及按需使用 zstd 字典延遲解壓縮 libicu 的小部分。

結合 Rust 重寫、ICU 變更和相同的程式碼折疊,Bun 的二進位大小在 Linux 和 Windows 上縮小了 ~20%

版本平台尺寸
Bun v1.4.0(金絲雀)Windows76 MB
Bun v1.3.14Windows94 MB
Bun v1.4.0(金絲雀)Linux70 MB
Bun v1.3.14Linux88 MB

減少堆疊空間使用

TOML 解析器以及 Bun 中的所有其他遞歸下降解析器(JSON、YAML、JavaScript、TypeScript 等)現在使用較少的堆疊空間。

這在合併 Rust 重寫之前導致了一些測試失敗:

bun test v1.3.14-canary.1 (e99311e58)
.......

105 | });
106 |
107 | it("Bun.TOML.parse throws on deeply nested inline tables instead of crashing", () => {
108 |   const depth = 25_000;
109 |   const deepToml = "a = " + "{ b = ".repeat(depth) + "1" + " }".repeat(depth);
110 |   expect(() => Bun.TOML.parse(deepToml)).toThrow(RangeError);
                                               ^
error: expect(received).toThrow(expected)

Expected constructor: RangeError

Received function did not throw
Received value: {
  a: {
    b: {
      b: {
        b: {
          b: {
            b: {
              b: {
                b: {
                  b: [Object ...],
                },
              },
            },
          },
        },
      },
    },
  },
}

      at <anonymous> (/var/lib/buildkite-agent/build/test/js/bun/resolve/toml/toml.test.js:110:42)

✗ Bun.TOML.parse throws on deeply nested inline tables instead of crashing [2907.64ms]

Rust 的 LLVM IR 程式碼產生器會發出 LLVM 的 llvm.lifetime.startllvm.lifetime.end 內在函數,這讓 LLVM 可以重複使用堆疊空間槽。這使得具有巢狀作用域的大型函數使用顯著較少的堆疊空間。

之前,我們透過將特別大的函數重構 為許多較小的函數來手動解決一個未解決的問題

速度提高 2% - 5%

Rust 支援 C/C++ 和 Rust 之間的跨語言連結時最佳化,從而實現跨程式語言的內聯(這太酷了!!)。

我們在 Linux x64(EC2、Xeon Platinum 8488C)上對 Bun v1.3.14 與 Bun v1.4.0 進行了基準測試。 HTTP 吞吐量使用 oha 針對 hello-world 伺服器測量,應用工作負載使用 hyperfine 測量。

HTTP 吞吐量(req/s,3 輪的平均值)

伺服器Bun v1.3.14Bun v1.4.0Δ
Bun.serve169.6k177.7k+4.8%
node:http103.8k108.5k+4.5%
Elysia158.9k163.3k+2.8%
express64.5k66.6k+3.2%
fastify91.5k95.9k+4.8%

應用程式/CLI (hyperfine)

工作負載Bun v1.3.14Bun v1.4.0Δ
next build13.62 s13.03 s+4.5%
vite build(tsc + vite)1.69 s1.65 s+2.2%
tsc -b --force0.94 s0.89 s+4.7%

生產

Prisma 在 Bun 的 Rust 重寫上推出了 Prisma Compute 公共測試版。

「我們遇到了記憶體洩漏和連接池在虛擬機暫停和恢復後無法恢復的問題。當 Rust 重寫出現時,我們針對相同的故障模式對其進行了測試。它完美地處理了這些問題。」 - 阿列克謝·奧連科

Claude Code v2.1.181(6 月 17 日發布)及更高版本使用 Bun 的 Rust 連接埠。 Linux 上的啟動速度提高了 10%,但除此之外幾乎沒有人注意到。無聊是好事。

運費

Bun v1.3.14 是用 Zig 寫的 Bun 的最後一個版本。 Bun v1.4.0 將會是用 Rust 寫的 Bun 的第一個版本。它現已在 Canary 中提供 - 請報告您發現的任何問題:

bun upgrade --canary

可維護性

對我自己和團隊來說,我們的新 Rust 程式碼庫感覺與舊的 Zig 程式碼庫非常相似。例如,以下是原始 Zig 程式碼和新 Rust 程式碼的片段:

pub fn canMergeSymbols(
    scope: *Scope,
    existing: Symbol.Kind,
    new: Symbol.Kind,
    comptime is_typescript_enabled: bool,
) SymbolMergeResult {
    if (existing == .unbound) {
        return .replace_with_new;
    }

    if (comptime is_typescript_enabled) {
        // In TypeScript, imports are allowed to silently collide with symbols within
        // the module. Presumably this is because the imports may be type-only:
        //
        //   import {Foo} from 'bar'
        //   class Foo {}
        //
        if (existing == .import) {
            return .replace_with_new;
        }

        // ...
    }

    // ...
}
pub fn can_merge_symbol_kinds<const IS_TYPESCRIPT_ENABLED: bool>(
    scope_kind: Kind,
    existing: symbol::Kind,
    new: symbol::Kind,
) -> SymbolMergeResult {

    if existing == symbol::Kind::Unbound {
        return SymbolMergeResult::ReplaceWithNew;
    }

    if IS_TYPESCRIPT_ENABLED {
        // In TypeScript, imports are allowed to silently collide with symbols within
        // the module. Presumably this is because the imports may be type-only:
        //
        //   import {Foo} from 'bar'
        //   class Foo {}
        //
        if existing == symbol::Kind::Import {
            return SymbolMergeResult::ReplaceWithNew;
        }

        // ...
    }

    // ...
}

任何理解原始 Zig 程式碼的人都可以理解機械翻譯的 Rust 程式碼。我透過檢查對抗性代碼審查代理是否正確捕獲了 Zig 代碼和 Rust 代碼之間的差異來審查了原始的 Rust 重寫 PR,確保他們確保遵循移植指南和生命週期指南,並且自己手動閱讀大量代碼與 Zig 與 Rust 並排。

下一步

Bun v1.4 使 Bun 更快、更小、使用更少的內存,並為團隊提供了極其強大的工具來系統地提高未來的穩定性:Rust 的借用檢查器、Miri(在 CI 中運行不斷增長的程式碼區塊)、LeakSanitizer 和用於解析器的 24/7 引導 24/7 引導測試。雖然還有更多需要重構,但一切已經有了一個好的開始。

這個 Rust 重寫需要一個具有程式碼庫完整背景的工程師團隊花費一年的時間。 1 名工程師使用 Fable 並密切監控 Claude Code,我們在 11 天內從開始到測試套件在所有平台上 100% 通過。

今天,工程師可以比一年前做更多的事情。

著作權歸原作者及相關權利人所有。如需更正或刪除, 請聯絡參識AI