needhelp
← Back to blog

Bun เขียน Rust ใหม่: 11 วัน, 6,778 Commits, และอะไรที่เปลี่ยนไปจริงๆ

by needhelp
Bun
Rust
Zig
JavaScript Runtime
LLM
วิศวกรรมซอฟต์แวร์

ทีม Bun ส่งของที่ไม่ธรรมดาในเดือนพฤษภาคม 2026: พวกเขาแทนที่ core ดั้งเดิมของ runtime ทั้งหมดด้วย Rust port, และทำเสร็จใน 11 วัน

การเปิดเผยที่ด้านบนของประกาศนั้นบอกโทนได้ดี Bun ถูก Anthropic ซื้อกิจการในเดือนธันวาคม 2025 Jarred Sumner ผู้สร้าง Bun ใช้เวอร์ชันก่อนเปิดตัวของ Claude — “Mythos-class model” ที่พวกเขาเรียก Fable 5 — สำหรับงานส่วนใหญ่ นี่ไม่ใช่การทดลองทางความคิด Bun v1.4.0 รุ่น Rust แรก อยู่ใน canary แล้วตอนนี้

ตัวเลขดิบ:

  • Zig 535,496 บรรทัด (ไม่รวม comments) กลายเป็น Rust codebase
  • 6,778 commits ระหว่างวันที่ 3-14 พฤษภาคม
  • 1,448 ไฟล์ .zig ที่ถูกพอร์ตด้วยเครื่องไปเป็น .rs
  • ค่าใช้จ่าย API ~$165,000 (5.9B uncached input tokens, 690M output tokens)
  • พีค: 64 Claudes ทำงานพร้อมกันใน 4 worktrees

การเริ่มต้น Bun หลังจาก Rust rewrite, ทำงานภายใต้ Claude Code

Bun คือการเดิมพันด้วย Zig เสมอ

Bun เริ่มต้นเป็น line-for-line port ของ JS/TS transpiler ของ esbuild จาก Go เป็น Zig Sumner เขียน Zig บรรทัดแรกเมื่อวันที่ 16 เมษายน 2021 หลังจากเห็นเอกสารอ้างอิง Zig หน้าเดียวบน Hacker News เวอร์ชันแรก — transpiler, minifier, bundler, package manager ที่เข้ากับ npm, test runner แบบ Jest, Node.js API surface — เขียนโดยคนคนเดียวในหนึ่งปี ในอพาร์ทเมนต์แคบๆ ใน Oakland ก่อนที่ LLMs จะมีประโยชน์สำหรับงานแบบนี้

การเดิมพันนั้นให้ผลตอบแทน CLI ของ Bun มียอดดาวน์โหลดกว่า 22 ล้านครั้งต่อเดือนแล้วตอนนี้ Claude Code และ OpenCode ใช้มันเป็น runtime Vercel, Railway, และ DigitalOcean มี first-party support

ขอบเขตก็คือปัญหาด้วย

หนี้: GC memory อยู่ข้างๆ manual memory

JavaScript มี garbage collection Bun ฝัง JavaScriptCore (engine ใน Safari) พร้อมกับกอง C/C++ libraries: uWebSockets, BoringSSL, SQLite, lsquic Zig ก็เหมือน C ตรงที่ไม่จัดการ memory ให้คุณ งานของ Bun คือการนั่งอยู่ระหว่างภาษาแบบ GC’d กับ native code ที่จัดการ manual และขอบเขตนั้นคือที่มาของ bugs ที่ร้ายแรงที่สุดส่วนใหญ่

ประกาศดังกล่าวระบุตัวอย่างสิ่งที่พวกเขาแก้ไขใน v1.3.14 เพียงเวอร์ชันเดียว: heap-use-after-free ใน node:zlib, use-after-free ใน node:http2 จาก re-entrant callbacks, out-of-bounds writes ใน UDPSocket.sendMany, memory leak ต่อ tls.connect, double-free ใน CSS parser รายการยังมีต่อ และมัน technical

Sumner บอกชัดเจนว่านี้ไม่ใช่ความผิดของ Zig “เราคงไปไม่ถึงขนาดนี้ถ้าไม่มี Zig และฉันจะขอบคุณตลอดไป” ปัญหาคือเชิงโครงสร้าง: การจัดการ lifetimes ข้าม GC boundary เป็นเรื่องยากในทุกภาษาที่ไม่ออกแบบมาเพื่อมัน และคำตอบของ Zig — defer แบบชัดแจ้งทุก call site — พลาดได้ง่ายใน error paths ที่ไม่ค่อยถึง

เขาลองพิจารณา C++ ประมาณ 20% ของ Bun เป็น C++ อยู่แล้ว และการย้ายเพิ่มจะได้ destructors กับ constructors แต่มันก็ยังต้องพึ่ง style guides ที่ enforce ผ่าน code review และ memory corruption ก็ยังเกิดขึ้น

Rust เสนออะไรที่ต่าง: ใน safe Rust, use-after-free, double-free, และ “ลืม free” เป็น compiler errors borrow checker และ Drop เปลี่ยนปัญหาแบบ style-guide ให้เป็นปัญหาแบบ type-system

กลยุทธ์: transpile อย่าออกแบบใหม่

Rewrites มีชื่อเสียงไม่ดี และด้วยเหตุผลที่ดี การเขียนใหม่ 535K บรรทัดจากศูนย์จะทำให้ features และ fixes หยุดนิ่งเป็นปี รูปแบบที่ Bun เลือกคือ mechanical port: สถาปัตยกรรมเดียวกัน, performance targets เดียวกัน, features เดียวกัน, test suite เดียวกัน — แค่เป็น Rust

สองการตัดสินใจขับเคลื่อนความพยายามทั้งหมด:

  1. ทำทั้งหมดทีเดียว ไม่ใช่ incremental ประสบการณ์ของ Sumner ในการพอร์ต esbuild ไป Zig บอกเขาว่า incremental rewrites ทิ้ง scaffolding ชั่วคราวที่สร้างปัญหาเกินกว่าช่วย
  2. ทำให้ดูเหมือน transpiled เป้าหมายคือ Rust ที่อ่านเหมือน Zig ที่มันมา เพื่อจะ refactor เป็น Rust ที่ idiom หลังจาก v1.4 ships

จุดสำคัญ: test suite ของ Bun เขียนด้วย TypeScript ดังนั้นมันไม่สนใจว่ารันไทม์ถูก implement ด้วยภาษาอะไร นั่นทำให้พวกเขาถือว่า “tests ทั้งหมดผ่าน” เป็นคำจำกัดความของเสร็จ

การเตรียมตัวเล็กน้อยแต่ตั้งใจ ก่อนเขียนโค้ด Sumner ใช้เวลา ~3 ชั่วโมงกับ Claude เพื่อแมป Zig patterns ไป Rust; การสนทนานั้นกลายเป็น PORTING.md (ภายหลังไปขึ้น Hacker News) รอบที่สองวิเคราะห์ Rust lifetimes ที่เหมาะสมของทุก struct field ทั่ว codebase และ serialize เป็น LIFETIMES.tsv เอกสารทั้งสองผ่าน adversarial review ก่อนที่โค้ดแม้แต่บรรทัดเดียวจะถูกพอร์ต

สิ่งที่อยู่ที่เดิม: JavaScriptCore และ C/C++ libraries ที่ฝังอยู่ การ rewrite คือ native glue และ Bun-specific code ไม่ใช่ JS engine

การดำเนินการ: 64 Claudes, 11 วัน

การพอร์ตทำงานเป็น “dynamic workflows” ประมาณ 50 รายการใน Claude Code อย่างต่อเนื่องเป็นเวลา 11 วัน แต่ละ workflow คือ loop: รับ task, สร้างโค้ด, รับการ review, ใช้ feedback

โมเดล review คือส่วนที่น่าสนใจ สำหรับ implementer ทุกคน จะมี adversarial reviewers สองคนขึ้นไป — แยก Claude sessions ที่ถูกบอกให้ถือว่าโค้ดผิด และหาเหตุผลทุกอย่างที่มัน fail implementer ไม่เคย review; reviewer ไม่เคย implement ประกาศดังกล่าว frame ว่าสิ่งนี้สะท้อน human review: ผู้เขียนอยาก merge ดังนั้นอีกฝ่ายตรวจสอบ

กลไกบางอย่างที่น่าสนใจ:

  • ทดลองก่อน. พวกเขาพอร์ต 3 ไฟล์ก่อน commit ทั้ง 1,448 ไฟล์
  • Worktree sharding. รอบแรกๆ Claudes ชนกันด้วย git stash และ git reset วิธีแก้: 4 worktrees, 16 Claudes ต่ออัน, มีกติกาห้ามรัน git หรือ cargo กลาง task
  • Compiler errors เป็น work queue. cargo check dump ~16,000 errors ไปยังไฟล์, จัดกลุ่มตาม crate, และ 64 Claudes ค่อยๆ ไล่ — 16 fix/review/apply loops ใน 4 worktrees
  • Cyclical dependencies. Zig เป็นหนึ่ง compilation unit; Rust ต้องการ ~100 crates การแก้วงจร dependencies ทำให้เกิด 16,000 errors เหล่านั้น
  • Isolation. Stress tests ที่ทำให้ TCP sockets หมด หรือ spawn ~10k processes รันภายใต้ systemd-run cgroups เครื่องยังคงเต็ม disk และ crash หลายครั้ง
timeline
    title Bun's 11-day Zig → Rust rewrite (May 2026)
    2026-05-03 : Port branch opened (PR #30412)
    2026-05-04 : First 100-file draft batch
    2026-05-06 : ~16,000 compiler errors, 64 Claudes
    2026-05-08 : First CI run (972 failing files)
    2026-05-09 : Linux x64 turns green
    2026-05-11 : Windows green (last platform)
    2026-05-14 : All 6 platforms green, merged

ตามตัวเลข: peak 1,300 บรรทัดโค้ดต่อนาที, 695 commits ในชั่วโมงที่คับคั่งที่สุด (6 พฤษภาคม), 58 commits ในนาทีที่คับคั่งที่สุด ทุก commit ถูก review โดย adversarial reviewers สองคนก่อนลงดิน final diff คือ +1,009,272 บรรทัด ไม่มีการทดสอบใดถูกข้ามหรือลบ

CI คือจุดที่มันกลายเป็นจริง สองวันหลังรันครั้งแรก จำนวนไฟล์ทดสอบที่ fail ลดลงจาก 972 เหลือ 23 อีกวันครึ่ง Linux กลายเป็นเขียวเต็มตัว Windows มาทีหลังสุดในวันที่ 11 พฤษภาคม; ทั้งหก platforms เขียวใน build #54202, วันที่ 14 พฤษภาคม

Rust ซื้ออะไรมาให้จริงๆ

เป้าหมายที่ประกาศคือ stability และโพสต์สนับสนุนด้วยตัวเลขจริง

Bugs. Bun v1.4.0 แก้ 128 bugs ที่ reproduce ได้ใน v1.3.14

Memory. Drop แทนที่ per-call-site defer ตัวอย่างจากโพสต์: bundle โปรเจกต์ 60 โมดูลเดียวกัน 2,000 ครั้งใน process เดียว ใน v1.3.14 ทุก build รั่ว ~3 MB ตลอดไป; ใน v1.4.0 memory คงที่

Builds v1.3.14 v1.4.0
500 1,914 MB 526 MB
1,000 3,506 MB 586 MB
1,500 5,097 MB 608 MB
2,000 6,745 MB 609 MB

ความพยายามก่อนหน้านี้ในการแก้ไขนี้ใน Zig ไม่เคยถูก merge Sumner เขียน เพราะการไม่มีสิ่งที่เทียบเท่า Drop ทำให้รู้สึกไม่มั่นใจ

Binary size. Rust rewrite เพียงอย่างเดียวลด 3.8 MB (Windows), 5.5 MB (macOS), 6.8 MB (Linux) — ส่วนใหญ่จากการตัด Zig comptime ที่มากเกินไป งาน linker เพิ่มเติม (identical code folding, trimming ICU data) ทำให้ total shrinkage ~20% บน Linux และ Windows

Version Platform Size
v1.4.0 Windows 76 MB
v1.3.14 Windows 94 MB
v1.4.0 Linux 70 MB
v1.3.14 Linux 88 MB

Speed. Cross-language LTO ระหว่าง C/C++ กับ Rust ให้ compiler inline ข้าม language boundaries Bun วัดได้ 2–5% gains:

  • Bun.serve: 169.6k → 177.7k req/s (+4.8%)
  • express: 64.5k → 66.6k (+3.2%)
  • next build: 13.62s → 13.03s (+4.5%)
  • tsc -b --force: 0.94s → 0.89s (+4.7%)

Prisma ย้าย Compute public beta ของมันไปที่ Rust rewrite Alexey Orlenko: “เราเจอ memory leaks และ connection pool ที่กู้คืนไม่ได้หลังจาก VM ถูก pause และ resume เมื่อ Rust rewrite ปรากฏขึ้น เราทดสอบมันกับ failure modes เดียวกัน มันจัดการได้สมบูรณ์แบบ”

Claude Code v2.1.181 (17 มิถุนายน) และรุ่นหลังใช้ Rust port การเริ่มต้นเร็วกว่า 10% บน Linux คำตัดสินของ Sumner ต่อการเปลี่ยนแปลงที่ผู้ใช้เห็น: “น่าเบื่อคือดี”

จุดที่พอร์ตลื่น

การ transpile 535K บรรทัดอย่างซื่อสัตย์ไม่ใช่ของฟรี โพสต์บันทึก 19 regressions ที่รู้จัก ทั้งหมดถูกแก้แล้ว สิ่งที่บอกเล่าคือช่องว่าง semantic เล็กๆ ระหว่างภาษา:

  • debug_assert! side effects. assert ของ Zig คือ function ดังนั้น argument ของมันรันในทุก build debug_assert! ของ Rust คือ macro ที่ถูกลบใน release — การเรียก insert_stale ที่ mutate HMR state หยุดทำงานเงียบๆ (#30678)
  • Odd-length slices. helper ของ Zig ไม่สนใจ trailing odd byte; bytemuck::cast_slice panic กับมัน Blob.text() บน UTF-16 BOM บวก odd byte count เริ่ม crash (#31188)
  • Bounds checks. ReleaseFast ของ Zig ลบมัน; Rust เก็บไว้ constant placeholder ตัวหนึ่งลด ceiling ของ filename-interning จาก 8.4M เหลือ 270K, และโปรเจกต์จริงก็เจอ (#31503)
  • comptime format strings. Zig ประเมิน format strings ที่ compile time ดังนั้น color markers หายไปก่อนที่ arguments จะถูกแทนที่ Rust ไม่มี comptime ดังนั้น parser ของ marker กิน literal backslash ใน OSC 8 hyperlinks (#30693)

เหล่านี้คือ bugs ที่โผล่ขึ้นมาก็เพราะสองภาษาดูเหมือนกันแต่ไม่เหมือน

นี่หมายถึงอะไร

ถ้าเอาเรื่อง LLM ออก ยังมีเรื่องวิศวกรรมจริง: โปรเจกต์หนึ่งชนเพดานของสิ่งที่ manual memory management จะให้ได้ใน scale และเลือก borrow checker ของ Rust เป็นเครื่องมือที่จะทำให้ bugs ทั้งคลาสเป็นไปไม่ได้ แทนที่จะแค่ไม่สนับสนุน

มุม LLM คือส่วนที่คนจะเถียงกัน Sumner เปิดเผยว่าสิ่งนี้จะใช้วิศวกรสามคนที่มี full codebase context ประมาณหนึ่งปี และพวกเขาจะไม่มีวันทำ — ทางเลือกที่ realistic คือ “ไม่ทำอะไรเลยและซ่อม bugs ต่อไป” แต่แล้ววิศวกรคนเดียวที่ดูแล 64 Claudes ทำมันใน 11 วันด้วย ~$165K

ประมาณ 4% ของ Rust code ของ Bun อยู่ใน unsafe blocks (~13,000 unsafe keywords ใน ~27,000 บรรทัดจากทั้งหมด ~780,000) และ 78% ของ blocks เหล่านั้นคือบรรทัดเดียว ตั้งแต่ merge พวกเขาทำ 11 รอบ security review และเพิ่ม 24/7 coverage-guided fuzzing ทุก parser — 100 พันล้าน executions ถึงตอนนี้, ~15 PRs

คำถามเรื่อง maintainability คือสิ่งที่ผมจะติดตาม Sumner บอกว่า Rust อ่านเหมือน Zig และแสดง side-by-side ของ canMergeSymbols เพื่อพิสูจน์ การเดิมพันคือการที่ faithful port สามารถ review ได้ทีละ port ซึ่งคือสิ่งที่ให้คนคนเดียวเซ็นรับ diff ล้านบรรทัด

Closing line ของเขาคือสิ่งที่จะมีอายุอย่างน่าสนใจ: “วิศวกรคนหนึ่งทำได้มากกว่าในวันนี้มากกว่าเมื่อปีที่แล้ว”

อ้างอิง

Share this page