Tin ngành
Bun được Anthropic mua lại và tái cấu trúc bằng Rust, đạt hơn 22 triệu lượt tải mỗi tháng
(giờ Việt Nam)
Tóm tắt AI
Sau khi về tay Anthropic, Bun đã được viết lại bằng Rust để tăng cường độ ổn định. Với hơn 22 triệu lượt tải hàng tháng, dự án hiện tập trung khắc phục các lỗi bộ nhớ nghiêm trọng nhờ sự hỗ trợ từ Rust và các công cụ kiểm thử hiện đại.
Bản dịch AI

Tiết lộ: Bun đã được Anthropic mua lại vào tháng 12 năm 2025. Tôi và những người khác trong đội ngũ Bun hiện đang làm việc tại Anthropic. Tôi đã sử dụng phiên bản tiền phát hành của Claude Fable 5 cho phần lớn quá trình viết lại bằng Rust.
Bun khởi đầu là một bản chuyển đổi từng dòng mã của trình biên dịch JavaScript & TypeScript từ Go sang Zig. Tôi đã viết dòng mã Zig đầu tiên của mình vào ngày 16 tháng 4 năm 2021. Tôi đã đặt cược vào Zig sau khi nhìn thấy tài liệu tham khảo ngôn ngữ Zig (Zig Language Reference) chỉ trên một trang trên Hacker News và thực sự hào hứng với khả năng kiểm soát cấp thấp cũng như sự chú trọng vào hiệu năng của nó.
Ngay từ đầu, phạm vi của Bun đã rất lớn:
Phiên bản đầu tiên của Bun được tôi viết trong 1 năm, tại một căn hộ chật chội ở Oakland, trước thời đại LLM, bằng ngôn ngữ Zig. Kết cục mặc định cho các dự án có phạm vi đầy tham vọng như Bun thường là gia nhập "nghĩa địa" của những dự án phụ bị bỏ dở trên trang cá nhân GitHub. Zig đã biến Bun thành hiện thực. Tôi sẽ không bao giờ có thể xây dựng được nhiều thứ như vậy trong 1 năm nếu không có Zig.
Ngày nay, CLI của Bun đạt hơn 22 triệu lượt tải xuống mỗi tháng. Các công cụ phổ biến như Claude Code và OpenCode đã đặt cược vào Bun làm runtime của họ. Vercel, Railway, DigitalOcean và nhiều đơn vị khác đã hỗ trợ chính thức cho Bun.
Phạm vi của Bun cũng là một thách thức đối với sự ổn định. Dưới đây là một ví dụ nhỏ về các lỗi chúng tôi đã sửa trong Bun v1.3.14:
Chúng tôi có thể đã tiếp tục sửa những loại lỗi này theo kiểu lẻ tẻ mãi mãi, nhưng chúng tôi nợ người dùng – những người đang tin tưởng chúng tôi – việc phải làm tốt hơn thế và ngăn chặn một cách hệ thống các lỗi này tái diễn.
Những gì chúng tôi đã và đang thực hiện
Điều này đã là nhiều hơn so với những gì nhiều dự án khác làm.
Chỉ cần thực sự thông minh và đừng mắc sai lầm?
Danh sách sửa lỗi của chúng tôi trông thật tệ và tôi đã mệt mỏi với việc đi ngủ mà vẫn lo lắng về các sự cố treo máy trong Bun. Tôi không đổ lỗi cho Zig vì điều đó – những người dùng Zig khác không gặp phải các lỗi như chúng tôi, và việc kết hợp GC (thu gom rác) với bộ nhớ được quản lý thủ công là một điều hiếm gặp đến mức không ngôn ngữ nào thực sự thiết kế cho nó. Chúng tôi sẽ không thể đi xa đến thế này nếu không có Zig, và tôi sẽ luôn biết ơn điều đó. Cho đến tận gần đây, việc chọn ngôn ngữ lập trình vẫn là một quyết định một chiều đối với một dự án như Bun.
JavaScript là một ngôn ngữ có thu gom rác và các engine JavaScript hiện đại như JavaScriptCore (và V8) có các quy tắc nghiêm ngặt về xử lý ngoại lệ và bộ thu gom rác. Zig, giống như C, không quản lý bộ nhớ thay cho bạn và đây là một sự đánh đổi mà đối với nhiều dự án, đó là lý do tuyệt vời để sử dụng Zig. Zig không có hàm khởi tạo/hủy (constructors/destructors), và hầu hết việc dọn dẹp được mong đợi sẽ được viết rõ ràng tại mỗi vị trí gọi bằng từ khóa defer.
Đối với Bun, việc xử lý chính xác vòng đời của các giá trị được thu gom rác và các giá trị được quản lý thủ công là nguồn gốc chính của các vấn đề ổn định – thường là rò rỉ bộ nhớ nhỏ và đôi khi là treo máy. Mỗi lần cấp phát bộ nhớ đều phải được xem xét tỉ mỉ. Những byte này được giải phóng ở đâu? Làm thế nào để đảm bảo nó chỉ được giải phóng một lần? Chúng ta đã kiểm tra các ngoại lệ JavaScript đúng cách chưa? Con trỏ được thu gom rác này có hiển thị với trình quét ngăn xếp bảo thủ (conservative stack scanner) không? Đây là bộ nhớ được thu gom rác hay bộ nhớ được quản lý thủ công?
Đối với các vấn đề ổn định, biết càng sớm càng tốt là tốt nhất. Fuzzing diễn ra sau khi mã được hợp nhất (merge). CI diễn ra khi mã được đẩy lên (push). Kiểm tra an toàn runtime & address sanitizer diễn ra khi mã được chạy (hy vọng là trong quá trình phát triển, trước khi CI).
Một cách phổ biến để giảm thiểu loại vấn đề này là đảm bảo mã dọn dẹp luôn được chạy chính xác một lần cho những đoạn mã cần nó. Zig được thiết kế là một ngôn ngữ đơn giản không có luồng điều khiển ẩn, vì vậy nó ưu tiên từ khóa defer rõ ràng để chạy mã ở cuối phạm vi thay vì hàm hủy ẩn ~Destructor của C++ hay Drop ẩn của Rust.
Đối với mã Zig, chính xác thì khi nào chúng ta nên chạy mã dọn dẹp? Nếu chúng ta truyền cùng một *T cho nhiều hàm khác nhau, làm thế nào để biết khi nào nó không còn truy cập được nữa và có thể dọn dẹp? Nó hoạt động như thế nào khi một số hàm cần tiếp tục tham chiếu đến bộ nhớ sau khi hàm được gọi? Cách tiếp cận hiện tại của chúng tôi là sự kết hợp của:
Nhiều dự án chọn trả lời những câu hỏi kiểu này thông qua một hướng dẫn phong cách (style guide). TigerStyle của TigerBeetle là một ví dụ trong Zig và hướng dẫn phong cách C++ dài 31.000 từ của Google là một ví dụ khác. Thách thức với các hướng dẫn phong cách là việc thực thi. Làm thế nào để bạn đảm bảo hướng dẫn phong cách được tuân thủ? Trong lịch sử, code review là câu trả lời với sự thực thi nỗ lực tốt nhất thông qua các trình kiểm tra lỗi (linters) & trình phân tích tĩnh (static analyzers).
Việc có một hướng dẫn phong cách cứng nhắc với các kỳ vọng về quyền sở hữu được nêu rõ ràng trong hệ thống kiểu (type system) là một lựa chọn thực tế cho Bun. Vì Zig không có tính năng nạp chồng toán tử (operator overloading), chúng ta có khả năng sẽ kết thúc với rất nhiều mã trông giống như thế này:
Điều này kém công thái học hơn so với Zig mà chúng ta mong đợi:
Còn C/C++ thì sao?
Khoảng 20% mã của Bun được viết bằng C++ và Bun nhúng một số thư viện C/C++:
C++ thay vì Zig sẽ là một lựa chọn hợp lý cho Bun. Chúng ta sẽ có các hàm khởi tạo & hủy. Chúng ta có thể xóa rất nhiều mã bao bọc extern "C".
Nhưng, chúng ta vẫn sẽ phụ thuộc vào các hướng dẫn phong cách được thực thi thông qua code review, và ngay cả với ASAN, tình trạng hỏng bộ nhớ và rò rỉ bộ nhớ vẫn sẽ xảy ra.
Tại sao lại là Rust?
Một tỷ lệ lớn các lỗi trong danh sách đó là use-after-free, double-free và "quên giải phóng" trong đường dẫn lỗi. Trong Rust an toàn, đây là các lỗi trình biên dịch và việc dọn dẹp tự động kiểu RAII với Drop. Lỗi trình biên dịch là một vòng phản hồi tốt hơn so với hướng dẫn phong cách.
Trong lịch sử, việc viết lại là một ý tưởng tồi tệ. Không tính các dòng chú thích, Bun có 535.496 dòng mã Zig. Việc viết lại bằng một ngôn ngữ khác sẽ mất một nhóm kỹ sư nhỏ trong cả một năm. Điều đó có nghĩa là phải đóng băng việc sửa lỗi, sửa bảo mật hoặc phát triển tính năng trong thời gian đó. Cách tiếp cận ít rủi ro nhất để có được thứ gì đó có thể phát hành là chuyển đổi cơ học từ Zig sang Rust, với số lượng thay đổi hành vi tối thiểu, sử dụng chính xác bộ kiểm thử (test suite) mà chúng tôi đã sử dụng để kiểm thử Bun.
May mắn thay, bộ kiểm thử của Bun được viết bằng TypeScript, nghĩa là nó không phụ thuộc vào ngôn ngữ lập trình của runtime.
Một năm không có tác động nào đến người dùng không phải là một lựa chọn thực tế mà chúng tôi có thể cân nhắc. Vì vậy, việc thực thi thông qua phong cách mã để khắc phục các vấn đề ổn định là lựa chọn tốt nhất của chúng tôi, và đó là kế hoạch của chúng tôi khi chúng tôi thêm các con trỏ thông minh (smart pointers) lấy cảm hứng từ Rust vào cơ sở mã của Bun.
Nhưng thành thật mà nói, tôi không muốn làm điều đó. Các con trỏ thông minh tự chế mang lại khả năng công thái học kém hơn Rust mà không có bất kỳ sự đảm bảo nào.
Nếu thay vào đó, tôi dành một tuần để kiểm tra xem mô hình mới của Anthropic có thể viết lại Bun bằng Rust hay không thì sao?
Lúc đầu, tôi không mong đợi nó sẽ hiệu quả. Vài ngày sau, một tỷ lệ cao các bài kiểm thử bắt đầu vượt qua và tôi thấy mã Rust mới khớp với cơ sở mã Zig gốc nhiều đến mức nào. Ý kiến của tôi đã chuyển từ "điều này đáng để thử" sang "tôi sẽ hợp nhất nó".
Claude, hãy viết lại Bun bằng Rust.
Có rất nhiều cách để làm việc này một cách tồi tệ. Ví dụ, việc nhắc Claude "Viết lại Bun bằng Rust. Đừng mắc bất kỳ sai lầm nào." và sau đó cầu nguyện rằng nó sẽ hiệu quả không phải là cách tôi đã làm.
Hãy suy nghĩ về cách một người sẽ thực hiện việc này. Câu hỏi lớn đầu tiên là:
Viết lại từng phần? Hay tất cả cùng một lúc?
Theo kinh nghiệm của tôi khi chuyển đổi trình biên dịch của esbuild từ Go sang Zig cho phiên bản đầu tiên của Bun (không có LLM), làm tất cả cùng một lúc sẽ tốt hơn. Việc viết lại từng phần sẽ thêm vào các đoạn mã tạm thời mà bạn hy vọng cuối cùng sẽ bị xóa bỏ, và sẽ rất đau đớn trong ngắn hạn và trung hạn.
Câu hỏi lớn thứ hai: làm thế nào?
Làm thế nào để giữ cho Bun trong Rust vẫn là Bun như trước, với cùng kiến trúc, hiệu năng và bộ tính năng, đồng thời có được các tính năng ngôn ngữ của Rust như borrow checker? Làm thế nào để đảm bảo đội ngũ vẫn có thể duy trì nó sau khi viết lại?
Hãy thực hiện việc viết lại sao cho trông giống như chúng ta đã chuyển đổi mã Zig sang Rust. Chúng ta có thể dần dần tái cấu trúc nó để giảm việc sử dụng các đoạn mã không an toàn (unsafe) và trông giống Rust thành ngữ hơn sau khi Bun v1.4 được phát hành.
Bài viết được AI dịch và tổng hợp tự động từ Hacker News Nổi bật (buzzing.cc bản dịch tiếng Trung). Liên kết bài gốc ở phía trên. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.