1 điểm bởi GN⁺ 21 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Đây là một thử nghiệm dự án cá nhân nhằm chuyển một crate web app Rust sang Ruby on Rails; đối tượng là 14.943 dòng mã dựa trên Tera và Axum
  • Cấu hình Rust hiện tại cần Playwright E2E, namespace cơ sở dữ liệu cô lập, dịch vụ mocking và cả crate API nội bộ, nên chi phí kiểm thử cao
  • Trong so sánh LLM, Rails đạt tổng điểm 710, cao hơn 480 của Rust/Axum/Diesel; tốc độ phát triển và sự thuận tiện của unit test được đánh giá là thế mạnh
  • Bản chuyển đổi one-shot bằng Local Qwen3.6 mất khoảng 30 phút và mã Ruby giảm còn 3.322 dòng, nhưng vẫn chưa được xác minh bằng cách chạy thực tế
  • Rails có ưu điểm ở các tính năng mặc định và kiểm thử ngắn gọn; việc Ruby thiếu an toàn kiểu có thể được bù đắp bằng Sorbet hoặc bổ sung kiểu dựa trên agent

Bối cảnh của thử nghiệm chuyển đổi

  • Một crate web app Rust thuộc dự án cá nhân được chọn làm đối tượng chuyển sang Ruby on Rails
    • Toàn bộ dự án có quy mô khoảng 30.000 dòng, và crate được chọn để chuyển đổi gần với một web app viết bằng TeraAxum
    • Phần mã Rust được chuyển đổi có tổng cộng 14.943 dòng, thời gian biên dịch khoảng 10 giây
    • Bản thân mã không lớn, nhưng có cấu trúc kéo theo nhiều phụ thuộc
  • Cấu hình Rust hiện tại có chi phí kiểm thử cao
    • Bài kiểm thử E2E cần cấu hình Playwright
    • Do khó mocking, cần namespace cơ sở dữ liệu cô lập và dịch vụ mocking
    • Cũng cần một crate API nội bộ riêng để Playwright tương tác với ứng dụng ở chế độ headless
  • Ruby và Ruby on Rails được xem xét như một phương án thay thế gọn hơn
    • Ruby không có kiểu nên mức độ ổn định có thể thấp hơn Rust
    • Dùng Sorbet có thể bù đắp phần nào tính an toàn kiểu trong Ruby
  • Kết quả so sánh độ phức tạp, độ ổn định, mức độ dễ kiểm thử... bằng nhiều instance LLM cho thấy Rails có điểm cao hơn
    • Tổng điểm của Rust/Axum/Diesel là 480, Rails là 710, và Rails + Sorbet là 695
    • Rails được đánh giá cao ở mức độ phù hợp cho lập trình viên cá nhân 90, tốc độ phát triển 90, và độ dễ của unit test 90
    • Rust/Axum/Diesel đạt điểm cao ở an toàn 95 và hiệu năng 95, nhưng thấp ở độ dễ của unit test 20 và integration test 30
    • Theo tổng điểm đơn giản, tác giả cho rằng app Rails có thể cho kết quả tốt hơn 1,47 lần

Kết quả chuyển đổi và các điểm cần xem xét

  • Dùng Local Qwen3.6 để chuyển đổi one-shot một dự án tương đối nhỏ
    • Quá trình chuyển đổi mất khoảng 30 phút
    • Do chưa chạy thử nên vẫn chưa xác nhận được có hoạt động thực sự hay không
  • Thay đổi lớn nhất là số dòng mã giảm xuống
    • Tổng số dòng file Rust: 14.943 dòng
    • Tổng số dòng file Ruby: 3.322 dòng
    • Số dòng giảm 77%, và 1 dòng Ruby tương đương khoảng 4,49 dòng Rust
  • Phần mã Ruby đã chuyển đổi trông sạch và khá đúng phong cách trong phạm vi tác giả đã rà qua
    • Vẫn còn khả năng có lỗi
    • Sau này sẽ được xem xét kỹ hơn
  • Các điểm cần xem thêm là bổ sung kiểu, tính năng mặc định của Rails, và việc đơn giản hóa kiểm thử
    • Nếu thêm kiểu bằng agent, có thể giảm bớt vấn đề về an toàn kiểu
    • Ruby/Rails được đánh giá là gần với kiểu “batteries + kitchen sink included”, và tốt hơn so với 3GiB phụ thuộc đã biên dịch
    • Kỳ vọng việc kiểm thử sẽ trở nên dễ hơn nhiều
  • Ví dụ kiểm thử trong Ruby có dạng ngắn, dùng VCR.use_cassette("llm_call") để bọc lời gọi LLM và kiểm tra kích thước kết quả
      VCR.use_cassette("llm_call") do
        result = LlmClient.match(entry, data_list)
        expect(result.results.size).to eq(data_list.size)
      end
    
  • Ví dụ kiểm thử trong Rust dài hơn vì phải tự triển khai provider giả lập
    • Sử dụng Arc<RwLock<Vec<Response>>>, AtomicUsize, async_trait, tokio::test...
    • Tạo MockProvider để quản lý danh sách phản hồi và số lần gọi, triển khai match của trait Provider, rồi trong test xác minh kết quả và số lần gọi
  • Vì đây là dự án cá nhân nên có thể đưa ra lựa chọn táo bạo, và việc chuyển từ Rust sang Ruby sẽ còn được xem xét kỹ trong thời gian tới

1 bình luận

 
Ý kiến trên Hacker News
  • Khó mà tin được. Nghe giống kiểu: “Có một chỗ ngứa về mặt kỹ thuật mà tôi muốn gãi, và AI cục bộ đã làm xong trong 30 phút. Tôi còn chưa bấm Start để xem nó có chạy không, nhưng đã viết bài blog rồi…”

    • Việc nó đang đứng số 1 trang đầu lúc này có nghĩa là dự án có chạy thật hay không cũng chẳng quan trọng. Chắc độc giả cũng chưa đọc kỹ bài đâu
      Tất nhiên là nếu không phải bot đẩy lên
    • 2016: Xem thư viện JavaScript mới của tôi đi!
      2026: Xem thành quả mà tôi không tự làm đi!
      2036: Cách tôi viết 200 dòng bằng ngôn ngữ Latin cổ tên là C
    • Không hẳn là “khó tin”, mà theo tiêu chuẩn năm 2026 thì khá bình thường
    • Dù sao thì họ cũng đã rà soát trong 5 tiếng. Nên ¯_(ツ)_/¯
  • Ban đầu tôi tưởng đây sẽ là một bài thú vị, nhưng đến đoạn nói dùng LLM để chuyển đổi thì tôi mất hứng ngay. Nó giống kiểu: “Tôi muốn làm việc này nên giao cho cấp dưới, giờ để tôi kể cho bạn nghe”
    Bản thân tác giả không trực tiếp làm việc chuyển đổi, cũng không suy nghĩ sâu về nó, nên tôi chẳng thấy có lý do gì để đọc

    • Vấn đề lớn nhất là tác giả thậm chí còn không xác minh. Tôi từng thấy những công cụ lớn ghép nhiều mô hình chạy chuyển đổi suốt 6 tiếng, rồi khi kiểm tra thủ công xem chúng xử lý các phép tính hoặc logic hóc búa thế nào thì hóa ra có stub hoặc trả về true được hard-code
      Vì vậy chỉ cần chạy smoke test là có thể trông như đã thành công
    • Vấn đề lớn hơn là phát triển phần mềm trong tương lai sẽ đi theo hướng này
      Trừ kiểu lập trình thủ công mang tính nghệ nhân, bản thân ngôn ngữ lập trình sẽ ngày càng kém quan trọng
      Khi LLM tiếp tục cải thiện, rốt cuộc vấn đề sẽ chỉ còn là quyết định sinh ra đặc tả bằng loại ngôn ngữ nào
      Phe UML và RUP coi như đã trả được thù
    • Điểm mấu chốt không phải là có tự tay viết code hay không, mà là đã nhìn vào sự khác biệt và đưa ra những lựa chọn có lẽ chưa tối ưu
      Như tôi đã nói ở bình luận khác, tôi đã xem xét khá rộng. Đây không phải dự án lớn, nên ngoài vài chỗ khá cay thì phần lớn chỉ là web app
      Đánh giá rằng tôi “không suy nghĩ gì” là không công bằng. Không phải kiểu bấm nút rồi YOLO
      Tôi đã nghiên cứu các trade-off và kết quả, sự khác biệt giữa các đoạn code Rust và Rails thực sự rất lớn, còn khả năng kiểm thử của ứng dụng Rust là chủ đề tôi đã cân nhắc suốt 2 tháng
      Như những người mê LLM vẫn nói, ngữ cảnh là quan trọng ;)
  • Tôi không chắc có ngôn ngữ hay framework nào ưu tiên hạnh phúc của lập trình viên hơn Ruby on Rails

    • Hiếm khi nào tôi khổ sở như lúc làm trong một dự án Rails. Thấy bug trên site, tôi chạy grep để tìm view render sai. Tìm được lệnh gọi method render phần đó, rồi grep lại theo tên method thì ra 0 kết quả
      Nó là thứ gì đó được tổng hợp ở đâu đó nên không biết nằm ở đâu, cuối cùng phải dừng việc đang làm và ngồi đọc tài liệu cả tiếng. Với người dùng Rails cả ngày thì có thể ổn, nhưng convention over configuration với tôi là một anti-pattern khổng lồ
    • Tôi cũng có cảm giác tương tự với Elixir và Phoenix, nhưng ít ra không có cái bẫy tự bắn vào chân mang tên method_missing
    • Dù trong các cộng đồng chuộng phong cách Silicon Valley thì có thể không hấp dẫn, tôi vẫn làm việc khá hài lòng với các framework Java và .NET
      Hạnh phúc không phải lúc nào cũng chuyển thành hiệu năng. Tôi nhớ đến ví dụ nổi tiếng về logo của Twitter trước khi họ chuyển sang JVM và Scala
      Ruby on Rails đã nổi tiếng, nhưng tôi cũng từng có trải nghiệm tương tự với AOLServer và Vignette dựa trên Tcl
      Ở một startup Bồ Đào Nha người ta còn tự làm biến thể riêng, và sau đó các nhà sáng lập tạo ra OutSystems. Đây là một trong những công cụ RAD đồ họa đời đầu cho phát triển website và hệ thống phân tán, theo hướng low-code/no-code, nhắm vào hạ tầng JVM hoặc CLR
      Dù vậy, giờ thấy CRuby có JIT mặc định vẫn là điều đáng mừng
    • Nhiều nhất thì đó chỉ là hạnh phúc ngắn hạn, đổi lại bằng mọi thuộc tính kiến trúc khác như khả năng bảo trì, hiệu năng, độ tin cậy, khả năng mở rộng, v.v.
  • Tôi đã tạo một bộ gem tên là propel_rails để đẩy sự ngắn gọn vốn đã có của Ruby on Rails đi xa hơn nữa. Nó sinh ra các lớp cấp cao như API controller và concern, rồi từ đó tạo cả một RESTful resource hoàn chỉnh (model, controller, serializer, unit test và E2E test) mà không cần chút boilerplate nào
    Cuối cùng controller chỉ còn chứa danh sách các thuộc tính mà API cho phép, vì các RESTful action được sinh tự động. Hơi khó giải thích trọn vẹn, nhưng sức mạnh metaprogramming của Ruby thực sự khiến những thứ đáng kinh ngạc trở nên dễ dàng

    • Nghe như một phiên bản tinh luyện của CRUD
      Có thể hiểu là nó hoạt động dựa trên domain model không?
    • Tôi tìm được nó trên rubygems, nhưng link GitHub gắn ở đó lại trả về 404
  • Tôi ở tình cảnh tương tự
    Tôi thích Go và Rust, cả hai đều là ngôn ngữ tuyệt vời với ưu nhược điểm riêng. Nhưng tiếc là tôi chưa thể xây ứng dụng SaaS bằng cả hai. Cảm giác như nhét chốt vuông vào lỗ tròn
    Có thể tôi sai rất nhiều, nhưng các công cụ SaaS đi kèm quá nhiều thứ, và tôi không muốn phát minh lại chúng
    RoR là “đủ tốt”. Bạn có thể đưa kiểu dữ liệu vào khi muốn, làm nhanh, và bộ công cụ cũng khá ổn
    Công việc chuyên nghiệp đầu tiên của tôi là PHP, mà nó có quá nhiều cạm bẫy. Ruby có vẻ nghiêng về mảng thương mại điện tử hơn nên khá thú vị, và nếu nó đủ tốt cho Shopify thì tôi thấy ổn

  • Nếu chuyển từ Rust sang Ruby mà vẫn hợp lý, thì việc chọn Rust ngay từ đầu đã là một sai lầm

    • Đây là câu tóm tắt bài viết quá chuẩn, và cũng là lý do tôi quyết định chia sẻ nó
  • Những ai nghĩ Ruby chậm hơn Rust có lẽ sẽ ngạc nhiên khi biết Ruby giờ thực ra nhanh hơn Python, chỉ là vẫn chậm hơn Go hay Rust

    • Thường thì mức dùng bộ nhớ còn quan trọng hơn tốc độ. Phần lớn ứng dụng Ruby không bị giới hạn bởi Ruby mà bởi cơ sở dữ liệu
      Nhưng khi có nhiều worker nền và mỗi cái bắt đầu ăn hơn 2GB RAM thì cộng dồn rất nhanh
      Tôi từng viết một service Rust chạy production, và điều gây ấn tượng hơn cả tốc độ là nó chạy với 30MB bộ nhớ
    • Tôi không hiểu vì sao người nghĩ Ruby chậm hơn Rust lại phải ngạc nhiên trước việc nó nhanh hơn Python. Hai chuyện đó liên quan gì nhau?
  • Đây là kiểu phát biểu gây chú ý, có thể hữu ích nếu muốn khoe IQ 900+ với dân thường xung quanh, nhưng nhiều lập trình viên thông minh và tài năng thực ra không thích Rust lắm. Có người thà tự làm ngôn ngữ lập trình và trình biên dịch của riêng mình còn hơn viết một dòng Rust
    Tôi nghĩ đến Jai của Jonathan Blow và Odin của Ginger Bill
    Còn có nhiều người khác nữa với sức sáng tạo và chiều sâu đã được chứng minh qua việc tạo ra các thư viện và framework đẹp, được dùng rộng rãi, nhưng tôi không muốn tốn chỗ kể ra ở đây
    Chỉ là Rust có một câu lạc bộ macho rất ngầu và một tình anh em rất gắn bó

    • Cộng đồng Rust thật ra là một trong những nơi xa với kiểu “câu lạc bộ macho” nhất có thể
  • Claude hoạt động cực tốt với ứng dụng Rails. Như tác giả bài viết đã chỉ ra, Ruby cho phép làm được nhiều việc với ít code, còn Rails dùng convention over configuration, nên ứng dụng Rails còn cô đọng hơn nữa
    Một giả thuyết về lý do Claude viết Rails app tốt là hiệu quả token
    Trước đây tôi từng thấy dự án này cố đo và so sánh hiệu quả token theo từng dự án, và Rails cho kết quả khá tốt
    https://felipemrvieira.github.io/SyntaxTax/dashboard/

  • Đôi khi tôi ngạc nhiên khi nhìn kích thước dự án. Codebase 30 nghìn dòng mà gọi là nhỏ à? Tôi biết trần trên có thể rất cao, nhưng 30 nghìn dòng đã có thể chứa lượng thông tin và sự tinh vi về hành vi khổng lồ
    Có lẽ cũng do nền tảng của tôi chủ yếu là backend/network dùng Go. Khi vượt quá khoảng 10 nghìn đến 15 nghìn dòng, việc giữ cả mô hình codebase trong đầu thường đã khá khó và mệt

    • Còn tùy bạn đang xây cái gì. Một ứng dụng SaaS có nhiều frontend rất dễ chạm mốc 30 nghìn dòng