1 điểm bởi GN⁺ 2 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Việc Bun được viết lại bằng Rust gần giống một quyết định chuyển sang stack công nghệ phổ biến hơn, trong khi vẫn giữ nguyên kiến trúc và cấu trúc dữ liệu hiện có vốn được xây dựng bằng Zig
  • PR viết lại có quy mô 6.755 commit, được mở vào ngày 8/5 và hợp nhất ngày 14/5, để lại nghi vấn về độ sâu của quá trình review đối với một thay đổi runtime cấp production
  • Việc vượt qua test chỉ xác minh các đường đi đã biết, và khó có thể đảm bảo cả những bất biến toàn cục như đường lỗi, tính đồng thời, hay điều kiện bộ nhớ cực hạn
  • Cốt lõi không nằm ở việc Zig thất bại, mà ở sự không tương thích mang tính cấu trúc giữa văn hóa đề cao phát hành nhanh của đội Bun và chi phí của quản lý bộ nhớ thủ công
  • Canh bạc thực sự không phải Zig hay Rust, mà là liệu mã do AI tạo ra và không được review đầy đủ có thể được bảo trì lâu dài hay không

Nền tảng của Bun do Zig tạo ra

  • Trước khi xem PR viết lại Bun sang Rust, cần nói rằng vai trò của Zig là rất lớn trong việc đưa Bun đến vị trí hiện tại
  • Lý do Jarred chọn Zig không phải vì “trông ngầu”, mà vì nó cho phép một đội nhỏ nhanh chóng tạo prototype cho một JS runtime hiệu năng cao không có garbage collection
  • Ma sát thấp của Zig, khả năng thao tác bộ nhớ trực tiếp, và tính tương thích C đơn giản đã giúp tạo nên hiệu năng ban đầu của Bun cũng như quy mô đội ngũ nhỏ
  • Như Jarred đã nói rằng “kiến trúc không thay đổi, cấu trúc dữ liệu cũng không thay đổi”, việc viết lại bằng Rust gần hơn với mô hình kế thừa bộ khung được tạo ra bằng Zig
  • Xây nền tảng bằng Zig, ra mắt sản phẩm, gọi vốn, rồi sau khi công ty được mua lại và mở rộng thì chuyển sang stack công nghệ phổ biến hơn có thể được xem là một quyết định kinh doanh bình thường
  • Tuy vậy, rất khó để đóng khung điều này như là do bản thân Zig không phù hợp

Rủi ro của một bản viết lại khổng lồ được hợp nhất chỉ sau 6 ngày

  • PR viết lại ghi nhận 6.755 commit, tên nhánh claude/phase-a-port, được mở ngày 8/5 và hợp nhất ngày 14/5
  • Vấn đề cốt lõi là toàn bộ một JS runtime cấp production lại được hợp nhất chỉ sau 6 ngày
  • Nguyên tắc cơ bản của software engineering là: “mã mà bạn không hiểu thì không nên chạy trong production”
  • Nguyên tắc này không phải vì mã chắc chắn có bug, mà vì khi bug xảy ra, bạn sẽ không biết phải bắt đầu xem từ đâu; đó là mức nền cho khả năng bảo trì
  • Danh sách reviewer của PR có coderabbitai[bot], claude[bot], còn reviewer là con người duy nhất alii ở trạng thái “Awaiting requested review”
  • Vòng lặp khép kín khi Claude viết và Claude review không phải là điều bất khả thi về mặt logic, nhưng kết quả là không có con người nào thực sự đọc hết toàn bộ codebase

Những gì việc vượt qua test không thể đảm bảo

  • Dù test suite có pass trên mọi platform, điều đó cũng chỉ xác minh tính đúng đắn của hành vi đã biết và các đường đi đã biết
  • Test suite khó có thể đảm bảo đầy đủ các vùng sau
    • Đường lỗi có được xử lý đúng hay không
    • Hệ thống hành xử ra sao ở các điều kiện biên dưới tải stress
    • Tính nhất quán trạng thái có được giữ trong các tình huống đồng thời hay không
    • Mô hình bộ nhớ trong điều kiện cực hạn có đúng với chủ đích hay không
  • Cũng có thể tóm lược rằng Jarred thừa nhận các vấn đề bộ nhớ khi tái nhập ranh giới JS là thứ mà Rust compiler không thể xử lý và vẫn phải phụ thuộc vào con người
  • Vấn đề là phần phải phụ thuộc vào con người đó lại không được con người review
  • Việc AI dịch mã gần hơn với tính tương đương ngữ nghĩa cục bộ, tức làm cho từng hàm hoạt động giống bản gốc trong trạng thái tách biệt
  • Những bất biến toàn cục giữa các hàm, hay các ràng buộc thiết kế không được viết trong test mà chỉ nằm trong đầu tác giả gốc, rất khó được đảm bảo chỉ bằng dịch mã bằng AI
  • Những ràng buộc như vậy có thể không lộ ra trong test hiện tại, nhưng 6 tháng sau lại xuất hiện thành các lỗi crash khó giải thích dưới một tải production cụ thể
  • Đây không chỉ là vấn đề của riêng Claude, mà cũng áp dụng cho mọi công cụ và cả lập trình viên con người khi dịch mã mà không review đầy đủ
  • Ở quy mô 6.755 commit, rủi ro này bị khuếch đại mạnh

Sau khi bị mua lại, ai là người gánh rủi ro

  • Bun thời kỳ đầu là dự án mà Jarred tự đặt cược vào chính mình; việc dùng Zig, lặp nhanh, chấp nhận technical debt có thể được xem là logic startup khi tự mình gánh rủi ro
  • Giờ đây Bun đã được một công ty lớn mua lại và có một tập người dùng thực sự đang dùng trong các hệ thống production, người gánh rủi ro của bản viết lại không còn là Jarred mà là các kỹ sư đang chạy Bun trong production và những người dùng phía sau họ
  • Jarred nói rằng phiên bản đó vẫn còn là canary, và vẫn còn công việc tối ưu hóa cùng dọn dẹp trước khi phát hành chính thức
  • Canary là một tuyến phòng thủ, nhưng không phải vật thay thế cho review của con người
  • Tối ưu hóa và dọn dẹp là vấn đề về chất lượng mã, chứ không giải quyết được câu hỏi liệu người bảo trì có thực sự hiểu mã hay không
  • Một codebase mà không ai trong đội đọc trọn vẹn thì dù test có bao quát hay giai đoạn canary có dài đến đâu, trạng thái bên trong của nó vẫn là một hộp đen với người bảo trì
  • Vấn đề này về sau có thể hiện ra thành nỗi đau thực sự khi phải debug những bug nghiêm trọng

Sai lầm trong chẩn đoán vấn đề của Zig

  • Lý do trước đây Jarred đưa ra là codebase Zig có nhiều use-after-free, double-free, và rò rỉ bộ nhớ ở các đường lỗi
  • Chẩn đoán đó tự nó là đúng, nhưng từ đây đi đến kết luận rằng “Zig không hoạt động” thì khá khó
  • Chẩn đoán chính xác hơn là trong một dự án thương mại ưu tiên lặp nhanh, chi phí nhận thức của quản lý bộ nhớ thủ công đã vượt quá ngân sách của đội
  • Đây không phải bug của Zig, mà có thể xem là sự không tương thích mang tính cấu trúc giữa mục tiêu thiết kế của Zig và mô hình kinh doanh của Bun
  • Đối tượng người dùng của Zig là các system programmer biết rõ mình đang làm gì và sẵn sàng trả chi phí để đổi lấy quyền kiểm soát cuối cùng
  • TigerBeetle đã viết một cơ sở dữ liệu bằng Zig gần như không có bug bộ nhớ, vì văn hóa đội ngũ và bản chất dự án phù hợp với triết lý của Zig
  • Văn hóa của đội Bun gần với lặp nhanh, phát hành nhanh, sửa bug nhanh, và điều đó về bản chất luôn ở trong trạng thái căng thẳng với kỷ luật bộ nhớ nghiêm ngặt mà Zig đòi hỏi
  • Diễn giải “đội của chúng tôi hay mắc lỗi với công cụ này” thành “công cụ này không phù hợp” gần như là một lỗi quy kết
  • Bài viết dùng phép so sánh rằng cây búa không hợp không có nghĩa lỗi nằm ở chính cây búa

Triển vọng ngắn hạn và rủi ro dài hạn

  • Trong ngắn hạn, khả năng cao phiên bản viết lại sẽ nhìn chung hoạt động ổn
  • Các đường đi chính được test bao phủ, các vấn đề rõ ràng sẽ lộ ra ở giai đoạn canary, và các đảm bảo của Rust compiler giúp loại bỏ một loại lỗi bộ nhớ
  • Nhìn bề ngoài, mọi thứ có thể trông hoàn toàn bình thường
  • Về dài hạn, 6.755 commit mà con người chưa từng đọc trọn vẹn sẽ còn lại như một rủi ro mang tính cấu trúc
  • Nếu 6 tháng sau xuất hiện một bug đồng thời kỳ quặc, hoặc một điều kiện biên dưới tải cụ thể gây ra hành vi bất thường, kỹ sư debug sẽ phải đối mặt với một hệ thống mà chưa từng có ai thực sự hiểu nó
  • Nói rằng đó là một hệ thống chưa từng được hiểu không có nghĩa là nó không có bug, mà là khi bug xuất hiện thì không ai biết tại sao
  • Canh bạc kỹ thuật thực sự của lần viết lại này không phải Zig hay Rust, mà là liệu mã do AI tạo ra và không được review có thể được bảo trì lâu dài trong môi trường production hay không
  • Câu hỏi này phức tạp hơn “mọi test đều pass”, và sâu hơn cả “tính an toàn bộ nhớ của Rust”
  • Kết luận được nén lại trong phép so sánh: “Zig đã dựng phần móng, Claude đã xây tòa nhà, còn reviewer là con người thì vẫn đang trên đường tới”
  • Tòa nhà này sẽ còn ở được bao lâu phụ thuộc vào việc khi lần rò rỉ đầu tiên xuất hiện, liệu có ai còn đọc được bản thiết kế hay không

1 bình luận

 
Ý kiến trên Lobste.rs
  • Trước khi bàn chuyện “viết lại Bun bằng Rust”, cần nói rõ rằng Bun có được vị trí hiện tại là nhờ Zig
    Nhà sáng lập Bun, Jarred cũng nói như vậy. Anh ấy cho rằng Zig đã làm cho Bun trở nên khả thi, và Zig đóng góp rất lớn để một dự án do một người âm thầm code suốt một năm trong căn phòng hôi hám ở Oakland phát triển thành một trong những công cụ được dùng rộng rãi nhất trong hệ sinh thái JavaScript
    Đồng thời cũng nói rằng Zig là một ngôn ngữ tuyệt vời và Bun mang ơn thành công đó rất nhiều, nhưng các dự án khác như Ghostty hay Tigerbeetle lại không gặp những vấn đề ổn định mà Bun từng gặp

    • Nếu đúng là “các dự án khác như Ghostty hay Tigerbeetle không gặp những vấn đề ổn định mà Bun từng gặp”, thì đáng để hỏi tại sao
      Vì thông thường cơ sở dữ liệu có thách thức về độ ổn định lớn hơn nhiều so với runtime ngôn ngữ
  • Bài này có mùi bài do LLM viết rất rõ

  • Nếu người dùng mục tiêu của Zig là “lập trình viên hệ thống biết rõ mình đang làm gì và sẵn sàng trả giá để có quyền kiểm soát tối thượng”, thì nó trông giống kiểu quá tự tin theo phong cách C/C++ quay trở lại trong bối cảnh các ngôn ngữ ra đời sau Rust và Swift
    Nếu lý do Jarred đưa ra khi chuyển sang là “codebase Zig có quá nhiều lỗi use-after-free, double free và rò rỉ bộ nhớ trên các nhánh lỗi”, thì đây là một dữ kiện về việc liệu chỉ cần cho LLM tích cực tìm lỗi an toàn bộ nhớ có thể khiến một ngôn ngữ không an toàn bộ nhớ trở nên an toàn trên thực tế hay không
    Ở đây ngay cả các công ty LLM cũng không chọn con đường đó

  • Câu “mã do Claude viết, mã do Claude review, nên vòng khép kín này không hẳn bất khả thi về mặt logic, nhưng điều đó cũng có nghĩa là chưa từng có con người nào thực sự đọc toàn bộ codebase này” có vẻ rõ ràng là không đúng
    Nếu một codebase Zig do con người viết được dịch cơ học sang Rust, thì người hiểu mã gốc cũng có thể hiểu mã mới. Đây đâu phải chuyển sang APL; cả hai đều là ngôn ngữ thủ tục trong truyền thống cú pháp C
    Luận điểm rằng nếu cứ để LLM chạy mà không giám sát thì mã có thể ngày càng xa rời trực giác của con người trong tương lai thì vẫn còn bỏ ngỏ, nhưng codebase ở thời điểm hiện tại vẫn có vẻ hiểu được ở mức mà một runtime JavaScript một triệu dòng kiêm công cụ vạn năng dạng single binary có thể có
    Câu “AI chỉ khớp được tính tương đương ngữ nghĩa cục bộ ở cấp hàm, chứ không hiểu các bất biến toàn cục giữa các hàm” nghe kỳ theo cả hướng ủng hộ lẫn chỉ trích LLM
    LLM không đủ tính quyết định để đảm bảo mỗi hàm hoạt động giống hệt bản gốc. Muốn có loại bảo đảm đó thì cần công cụ như c2rust. LLM có thể dịch ra đoạn mã trông giống bản gốc, nhưng thứ ngăn ((abc & 45) << 3) == 360 bị biến thành ((abc & 45) << 30) == 360 chỉ có compiler, bộ test, và có lẽ là khả năng phát hiện mang tính xác suất của code review dựa trên LLM
    Ngược lại, nếu có một trình dịch vừa đảm bảo tính đồng nhất của từng hàm như c2rust, vừa giữ được chú thích và cấu trúc như LLM, thì đó là trình dịch hoàn hảo và có thể tự động port cả codebase triệu dòng. Compiler cũng có thể xem là một trường hợp đặc biệt như vậy, và Clang dù không phải không có bug nhưng vẫn đủ gần để mọi người tin tưởng. Nếu LLM có thể dịch Zig hay C++ sang Rust với độ tin cậy ngang Clang, thì Chrome chắc đã thành Rust thuần ngay vào cuối tháng này
    Ngoài ra, việc mã hóa các bất biến giữa các hàm chính là cốt lõi của hệ thống kiểu. Một trong những lý do viết lại Bun bằng Rust cũng là vì hệ thống kiểu của Rust có thể biểu đạt các bất biến phức tạp tốt hơn. Đâu phải Anthropic đã biên dịch toàn bộ sang assembly rồi đốt luôn mã nguồn

    • Câu “nếu một codebase Zig do con người viết được dịch cơ học sang Rust thì người hiểu mã gốc cũng có thể hiểu mã mới” có lẽ sẽ bị tối ưu hóa biến mất hoàn toàn ở bản release fast :^)
      Bun đã được vibe coding từ rất lâu trước khi có đợt port sang Rust. Họ tiếp nhận AI khá sớm, và sau khi được Anthropic thâu tóm thì gần như mọi commit đều do bot viết. Jarred cũng từng tweet rằng cuối tuần để AI viết tính năng và đã thêm được nhiều tính năng theo cách đó
    • Bỏ qua nhận xét của Kristoff rằng Bun từ lâu đã không còn là một codebase do con người viết, có vẻ người ta đang giả định rằng đợt port bằng Claude là một bản dịch cơ học, nhưng thực tế có vẻ không phải vậy
      HTML Parsers in Portland phân tích nhiều bản port của một parser HTML Python và cho thấy một trong các thuật toán cốt lõi được hiện thực rất khác nhau ở mỗi bản port. Trong mọi trường hợp đều có thể port cơ học, nhưng thực tế lại không diễn ra như vậy
      Dĩ nhiên đó là ví dụ từ đầu năm nay và quy trình cũng khác, nhưng vài mảnh trong codebase Bun sau khi port mà tôi đã xem cũng có vẻ gặp vấn đề tương tự
    • Bản thân câu “nếu một codebase Zig do con người viết được dịch cơ học sang Rust thì người hiểu mã gốc cũng có thể hiểu mã mới” là đúng
      Nhưng điểm “LLM không đủ tính quyết định để đảm bảo mỗi hàm hoạt động giống bản gốc” cuối cùng lại dẫn tới kết luận rằng con người thực ra chưa hề đọc toàn bộ codebase này
  • Một ví dụ của kiểu “mọi bài test đều pass”: https://github.com/oven-sh/bun/…

    • Thay đổi trong commit đó không có trong diff cuối cùng, và tự kiểm tra cũng có thể thấy rất dễ
      Thấy nhiều người ở các diễn đàn khác cũng dẫn cùng commit đó, có vẻ họ nhìn thấy link này ở đâu đó rồi cảm thấy nó khớp với điều mình kỳ vọng, sau đó không kiểm tra xem nó có thực sự liên quan hay không. Tôi nghĩ chúng ta nên tự đặt cho mình tiêu chuẩn cao hơn
    • Dù tốt hay xấu, commit đó là một commit do con người viết nhằm hoàn tác một phần commit trước đó cũng do con người viết
      Commit đầu tiên: “await process exit / JSON-parse-retry instead of fixed sleeps
      Hoàn tác: “test: revert proc.exited change in spawn.test.ts, keep isDebug iteration count
  • Gần đây tôi nghĩ nhiều về nguyên tắc “không nên chạy trong production những đoạn mã mà mình không hiểu”
    Sự nghiệp của tôi cũng đã đi khá xa, nhưng tôi cố tình không chuyển sang quản lý, và con đường lập trình của tôi ngày càng đi xuống tầng thấp hơn của stack. Lý do là vì tôi thích hiểu sâu chính bản thân chương trình thực tế. Việc phát hành tính năng và cải thiện cuộc sống của người dùng cũng rất quan trọng, nhưng một trong những phần thưởng nội tại lớn nhất của nghề lập trình là cảm giác học được những sự thật đúng về các hệ thống thực
    Với người như tôi, việc con người phải hiểu được mọi dòng trong chương trình gần như là một tiên đề. Thế nhưng trên sơ đồ tổ chức, quản lý của tôi lại chịu trách nhiệm với chương trình mà tôi bảo trì. Ông ấy là một quản lý tuyệt vời và không micromanage, nên dĩ nhiên không thể hiểu mọi dòng của phần mềm mà nhóm phát hành. Thực ra tôi còn không chắc ông ấy có đọc code nhiều không. Vậy thì có nghĩa là ông ấy đang vi phạm nguyên tắc này
    Tôi đang tự hỏi liệu có khác biệt nào có ý nghĩa giữa “đoạn mã do cấp dưới viết mà tôi không hiểu” và “đoạn mã do AI của tôi viết mà tôi không hiểu” hay không
    Trường hợp 1 thì có vẻ chấp nhận được, còn trường hợp 2 thì khiến tôi thấy lấn cấn. Phải chăng khác biệt chỉ là có một con người đứng ra chịu trách nhiệm cho đoạn mã đó hay không? Hay là có một ai đó để quy trách nhiệm? Tôi vẫn chưa biết điều đó có đủ để biện minh cho cảm giác đạo đức rất mạnh mà tôi đang có hay không
    Tôi lo rằng cảm xúc mạnh này không phải là yếu tố thiết yếu của kỹ nghệ phần mềm tốt, mà gần với gu thẩm mỹ cá nhân hơn. Ai cũng có thể có sở thích riêng, nhưng nếu chỉ có vậy thì có lẽ tôi phải chấp nhận rằng khía cạnh này của công việc trong tương lai sẽ bớt vui hơn. Cuối cùng thì vẫn là bị đẩy sang làm quản lý, chỉ là cấp dưới của tôi lại là bot

  • Người ta nói “Zig cho phép các nhóm nhỏ nhanh chóng tạo prototype một runtime JS hiệu năng cao mà không cần garbage collector và runtime nặng”, nhưng các runtime lớn khác là Node và Deno cũng làm điều tương tự
    Cả hai đều bọc JS engine bằng các ngôn ngữ không có garbage collector là C++ và Rust. Tôi không rõ bản thân V8 hay JSC có thực sự được phân phối kèm garbage collector hay không, nhưng điều đó không phải trọng tâm

  • Toàn bộ kho lưu trữ Bun tạo cảm giác như một thế giới phản địa đàng. Bot đang nói chuyện với nhau và tạo ra những PR vô nghĩa
    Ví dụ: https://github.com/oven-sh/bun/issues/30766

  • Tốc độ mà việc này được coi là “hoàn thành” quả thực khá gây sốc, nhưng tôi không nghĩ nó là một vụ tai nạn tàu hỏa đang lao đi như bài viết mô tả khi không có bằng chứng vững chắc
    Câu “6 tháng sau sẽ xuất hiện các bug đồng thời kỳ quặc, và khi một điều kiện biên nào đó dưới tải nhất định gây ra hành vi bất thường, kỹ sư đi debug sẽ phải đối mặt với một hệ thống mà chưa từng ai thật sự hiểu” hoàn toàn là suy đoán, không có sự kiện nào được đưa ra
    Port ngôn ngữ là một dạng công việc rất hợp với LLM, và mã nền tảng cũng vốn đã có nhiều người hiểu. Tôi không hiểu vì sao chỉ một lần port ngôn ngữ đơn thuần lại đột nhiên làm khả năng chẩn đoán hỏng đến mức không thể cứu vãn