Sử dụng Rust khi khởi nghiệp: Một câu chuyện cảnh báo

Nov 25 2022
Rust thật tuyệt vời, đối với một số thứ. Nhưng hãy suy nghĩ kỹ trước khi chọn nó cho một công ty khởi nghiệp cần phát triển nhanh.

Rust thật tuyệt vời, đối với một số thứ. Nhưng hãy suy nghĩ kỹ trước khi chọn nó cho một công ty khởi nghiệp cần phát triển nhanh.

Tất cả các tác phẩm nghệ thuật cho bài đăng này được tạo bằng DALL-E.

Tôi đã do dự khi viết bài đăng này, bởi vì tôi không muốn bắt đầu hoặc tham gia vào cuộc thánh chiến về ngôn ngữ lập trình. (Chỉ để loại bỏ ngọn lửa mồi nhử, Visual Basic là ngôn ngữ tốt nhất từ ​​trước đến nay!) Nhưng tôi đã có một số người hỏi tôi về kinh nghiệm của tôi với Rust và liệu họ có nên chọn Rust cho các dự án của họ hay không. Vì vậy, tôi muốn chia sẻ một số ưu và nhược điểm mà tôi thấy khi sử dụng Rust trong cài đặt khởi động, nơi mà các nhóm di chuyển nhanh và mở rộng quy mô thực sự quan trọng.

Tôi muốn nói rõ rằng tôi là một fan hâm mộ của Rust vì một số thứ . Bài đăng này không nói về việc Rust tệ như thế nào với tư cách là một ngôn ngữ hay bất kỳ thứ gì tương tự. Tuy nhiên, điều tôi muốn nói đến là cách sử dụng Rust gần như chắc chắn sẽ liên quan đến một cú hích năng suất không tầm thường có thể là một yếu tố chính nếu bạn đang cố gắng tiến nhanh. Cân nhắc cẩn thận xem tác động vận tốc có xứng đáng với lợi ích của ngôn ngữ đối với công ty và sản phẩm của bạn hay không.

Ngay từ đầu, tôi phải nói rằng Rust làm rất tốt những gì nó được thiết kế để làm và nếu dự án của bạn cần những lợi ích cụ thể của Rust (một ngôn ngữ hệ thống có hiệu suất cao, gõ siêu mạnh, không cần thu gom rác, v.v.) thì Rust là một lựa chọn tuyệt vời. Nhưng tôi nghĩ rằng Rust thường được sử dụng trong những tình huống không phù hợp lắm và các đội phải trả giá cho sự phức tạp và chi phí của Rust mà không thu được nhiều lợi ích.

Kinh nghiệm chính của tôi về Rust đến từ việc làm việc với nó trong hơn 2 năm tại một công ty khởi nghiệp trước đó. Dự án này là một sản phẩm SaaS dựa trên đám mây, ít nhiều cũng là một ứng dụng CRUD thông thường: đó là một tập hợp các dịch vụ siêu nhỏ cung cấp điểm cuối API REST và gRPC trước cơ sở dữ liệu, cũng như một số điểm cuối khác- end microservices (bản thân chúng được triển khai trong sự kết hợp giữa Rust và Python). Rust được sử dụng chủ yếu vì một vài người sáng lập công ty là chuyên gia về Rust. Theo thời gian, chúng tôi đã phát triển nhóm một cách đáng kể (tăng số lượng nhân viên kỹ thuật lên gần 10 lần), đồng thời quy mô và độ phức tạp của cơ sở mã cũng tăng lên đáng kể.

Khi nhóm và cơ sở mã phát triển, theo thời gian, tôi cảm thấy rằng chúng tôi đang phải trả một khoản thuế ngày càng nặng cho việc tiếp tục sử dụng Rust. Quá trình phát triển đôi khi diễn ra chậm chạp, việc tung ra các tính năng mới mất nhiều thời gian hơn tôi dự kiến ​​và nhóm đang cảm thấy năng suất thực sự bị ảnh hưởng từ quyết định sử dụng Rust ban đầu đó. Về lâu dài, việc viết lại mã bằng ngôn ngữ khác sẽ giúp quá trình phát triển nhanh hơn nhiều và đẩy nhanh thời gian hoàn thành, nhưng việc tìm thời gian cho công việc viết lại chính sẽ cực kỳ khó khăn. Vì vậy, chúng tôi gần như bị mắc kẹt với Rust trừ khi chúng tôi quyết định cắn viên đạn và viết lại một lượng lớn mã.

Rỉ sét được cho là thứ tốt nhất kể từ khi cắt lát bánh mì, vậy tại sao nó lại không hiệu quả với chúng tôi?

Rust có một đường cong học tập rất lớn.

Tôi đã làm việc với hàng chục ngôn ngữ trong sự nghiệp của mình và với một vài ngoại lệ, hầu hết các ngôn ngữ thủ tục, hiện đại (C++, Go, Python, Java, v.v.) đều rất giống nhau về các khái niệm cơ bản của chúng. Mỗi ngôn ngữ đều có những điểm khác biệt nhưng thường thì vấn đề là học một vài mẫu chính khác nhau giữa các ngôn ngữ và sau đó một ngôn ngữ có thể làm việc hiệu quả khá nhanh. Tuy nhiên, với Rust, người ta cần học những ý tưởng hoàn toàn mới — những thứ như thời gian tồn tại, quyền sở hữu và trình kiểm tra khoản vay. Đây không phải là những khái niệm quen thuộc với hầu hết những người làm việc với các ngôn ngữ phổ biến khác và có một đường cong học tập khá dốc, ngay cả đối với các lập trình viên có kinh nghiệm.

Tất nhiên, một số ý tưởng “mới” đó có trong các ngôn ngữ khác - đặc biệt là các ngôn ngữ chức năng - nhưng Rust đưa chúng vào cài đặt ngôn ngữ “chính thống” và do đó sẽ là điều mới mẻ đối với nhiều người mới sử dụng Rust.

Mặc dù tôi là một trong những nhà phát triển thông minh và giàu kinh nghiệm nhất mà tôi từng làm việc cùng, nhưng nhiều người trong nhóm (bao gồm cả tôi) vẫn gặp khó khăn trong việc hiểu các cách chính tắc để thực hiện một số việc nhất định trong Rust, cách mò mẫm các thông báo lỗi phức tạp thường gặp từ trình biên dịch hoặc làm thế nào để hiểu cách các thư viện chính hoạt động (thêm về điều này bên dưới). Chúng tôi bắt đầu tổ chức các buổi “tìm hiểu Rust” hàng tuần để nhóm giúp chia sẻ kiến ​​thức và chuyên môn. Tất cả điều này đều làm giảm đáng kể năng suất và tinh thần của nhóm vì mọi người đều cảm thấy tốc độ phát triển chậm.

Như một điểm so sánh về việc áp dụng một ngôn ngữ mới trong nhóm phần mềm sẽ như thế nào, một trong các nhóm của tôi tại Google là một trong những người đầu tiên chuyển hoàn toàn từ C++ sang Go và chỉ mất không quá hai tuần trước khi toàn bộ Lần đầu tiên, nhóm 15 người khá thoải mái viết mã trong cờ vây. Với Rust, ngay cả sau nhiều tháng làm việc hàng ngày với ngôn ngữ này, hầu hết mọi người trong nhóm chưa bao giờ cảm thấy hoàn toàn thành thạo. Một số nhà phát triển nói với tôi rằng họ thường cảm thấy xấu hổ vì mất nhiều thời gian hơn họ dự kiến ​​để các tính năng của họ hạ cánh và họ đã dành quá nhiều thời gian để cố gắng xoay sở với Rust.

Có nhiều cách khác để khắc phục sự cố mà Rust đang cố gắng giải quyết.

Như đã đề cập ở trên, dịch vụ mà chúng tôi đang xây dựng là một ứng dụng CRUD khá đơn giản. Tải dự kiến ​​trên dịch vụ này sẽ theo thứ tự không quá một vài truy vấn mỗi giây, tối đa, trong suốt thời gian tồn tại của hệ thống cụ thể này. Dịch vụ này là tiền đề cho một quy trình xử lý dữ liệu khá phức tạp có thể mất nhiều giờ để chạy, vì vậy bản thân dịch vụ này không được coi là nút cổ chai hiệu suất. Không có mối lo ngại cụ thể nào về việc một ngôn ngữ thông thường như Python sẽ gặp khó khăn trong việc mang lại hiệu suất tốt. Không có nhu cầu đồng thời hoặc an toàn đặc biệt nào ngoài những gì mà bất kỳ dịch vụ đối mặt với web nào cần giải quyết. Lý do duy nhất chúng tôi sử dụng Rust là vì các tác giả ban đầu của hệ thống là các chuyên gia về Rust, chứ không phải vì nó đặc biệt phù hợp để xây dựng loại dịch vụ này.

Rust đã đưa ra quyết định rằng sự an toàn quan trọng hơn năng suất của nhà phát triển. Đây là sự đánh đổi phù hợp để thực hiện trong nhiều tình huống — chẳng hạn như xây dựng mã trong nhân hệ điều hành hoặc cho các hệ thống nhúng bị hạn chế về bộ nhớ — nhưng tôi không nghĩ đó là sự đánh đổi đúng đắn trong mọi trường hợp, đặc biệt không phải trong các công ty khởi nghiệp mà tốc độ là rất quan trọng. Tôi là một người thực dụng. Tôi thà để nhóm của mình dành thời gian gỡ lỗi thỉnh thoảng bị rò rỉ bộ nhớ hoặc lỗi gõ đối với mã được viết bằng Python hoặc Go, hơn là để mọi người trong nhóm phải chịu năng suất gấp 4 lần vì sử dụng ngôn ngữ được thiết kế để tránh hoàn toàn những vấn đề này .

Như tôi đã đề cập ở trên, nhóm của tôi tại Google đã xây dựng một dịch vụ, hoàn toàn bằng Go, dịch vụ này theo thời gian đã phát triển để hỗ trợ hơn 800 triệu người dùng và tương đương gấp 4 lần QPS của Google Tìm kiếm vào thời kỳ đỉnh cao. Tôi có thể đếm trên đầu ngón tay số lần chúng tôi gặp phải sự cố do hệ thống loại của Go hoặc trình thu gom rác gây ra trong những năm xây dựng và vận hành dịch vụ này. Về cơ bản, các vấn đề mà Rust được thiết kế để tránh có thể được giải quyết theo những cách khác — bằng cách thử nghiệm tốt, lọc tốt, đánh giá mã tốt và giám sát tốt. Tất nhiên, không phải tất cả các dự án phần mềm đều có sự sang trọng này, vì vậy tôi có thể tưởng tượng rằng Rust có thể là một lựa chọn tốt trong những tình huống khác.

Bạn sẽ gặp khó khăn khi thuê các nhà phát triển Rust.

Chúng tôi đã thuê rất nhiều người trong thời gian tôi làm việc tại công ty này, nhưng chỉ có khoảng hai hoặc ba trong số hơn 60 người tham gia nhóm kỹ sư có kinh nghiệm trước đây với Rust. Điều này không phải vì muốn cố gắng tìm các nhà phát triển Rust - chỉ là họ không ở ngoài đó. (Tương tự như vậy, chúng tôi đã do dự khi thuê những người chỉ muốn viết mã bằng Rust, vì tôi nghĩ rằng đó là một kỳ vọng tồi khi đặt ra trong môi trường khởi nghiệp nơi ngôn ngữ và các lựa chọn công nghệ khác cần được thực hiện một cách nhanh chóng.) Sự ít ỏi này tài năng của nhà phát triển Rust sẽ thay đổi theo thời gian, khi Rust trở nên phổ biến hơn, nhưng việc xây dựng Rust dựa trên giả định rằng bạn sẽ có thể thuê những người đã biết điều đó có vẻ rủi ro.

Một yếu tố thứ yếu khác là việc sử dụng Rust gần như chắc chắn sẽ dẫn đến sự chia rẽ giữa những người trong nhóm biết Rust và những người không biết. Bởi vì chúng tôi đã chọn một ngôn ngữ lập trình “bí truyền” cho dịch vụ này, nên các kỹ sư khác trong công ty, những người lẽ ra có thể hữu ích trong việc xây dựng các tính năng, gỡ lỗi các vấn đề sản xuất, v.v. đuôi của cơ sở mã Rust. Sự thiếu linh hoạt này trong nhóm kỹ thuật có thể là một trách nhiệm thực sự khi bạn đang cố gắng tiến nhanh và khai thác sức mạnh tổng hợp của mọi người trong nhóm. Theo kinh nghiệm của tôi, mọi người thường gặp chút khó khăn khi di chuyển giữa các ngôn ngữ như C++ và Python, nhưng Rust đủ mới và đủ phức tạp để tạo ra rào cản đối với những người làm việc cùng nhau.

Thư viện và tài liệu chưa trưởng thành.

Đây là một vấn đề mà (tôi hy vọng!) Sẽ được khắc phục theo thời gian, nhưng so với Go, hệ sinh thái tài liệu và thư viện của Rust còn quá non nớt. Giờ đây, Go có lợi thế là nó được phát triển và hỗ trợ bởi toàn bộ nhóm tận tâm tại Google trước khi nó được phát hành ra thế giới, vì vậy các tài liệu và thư viện khá bóng bẩy. Để so sánh, Rust từ lâu đã giống như một công việc đang được tiến hành. Các tài liệu cho nhiều thư viện phổ biến khá thưa thớt và người ta thường cần đọc mã nguồn của một thư viện nhất định để hiểu cách sử dụng nó. Điều này thật tệ.

Những người xin lỗi Rust trong nhóm thường nói những điều như “async/await vẫn còn rất mới” và “vâng, tài liệu cho thư viện đó còn thiếu” nhưng những thiếu sót này đã ảnh hưởng khá lớn đến nhóm. Chúng tôi đã mắc một sai lầm lớn ngay từ đầu khi sử dụng Actix làm khung web cho dịch vụ của mình, một quyết định dẫn đến vô số đau đớn và khổ sở khi chúng tôi gặp phải các lỗi và sự cố chôn sâu trong thư viện mà không ai có thể tìm ra cách khắc phục. (Công bằng mà nói, đây là một vài năm trước và có lẽ mọi thứ đã được cải thiện cho đến bây giờ.)

Tất nhiên, sự non nớt này không thực sự đặc trưng đối với Rust, nhưng nó dẫn đến một khoản thuế mà nhóm của bạn phải trả. Cho dù tài liệu hướng dẫn và tài liệu ngôn ngữ cốt lõi tuyệt vời đến đâu, nếu bạn không thể tìm ra cách sử dụng các thư viện, thì điều đó cũng không thành vấn đề (tất nhiên là trừ khi bạn dự định viết mọi thứ từ đầu).

Rỉ sét làm cho các tính năng mới trở nên rất khó khăn.

Tôi không biết về bất kỳ ai khác, nhưng ít nhất đối với tôi, khi tôi xây dựng một tính năng mới, tôi thường không có sẵn tất cả các loại dữ liệu, API và các chi tiết tốt khác. Tôi thường chỉ viết mã để cố gắng làm cho một số ý tưởng cơ bản hoạt động và kiểm tra xem các giả định của tôi về cách mọi thứ nên hoạt động có ít nhiều đúng hay không. Chẳng hạn, làm điều này trong Python cực kỳ dễ dàng, bởi vì bạn có thể chơi nhanh và thoải mái với những thứ như gõ và không phải lo lắng nếu một số đường dẫn mã bị hỏng trong khi bạn phác thảo ý tưởng của mình. Bạn có thể quay lại sau và làm cho nó gọn gàng hơn, sửa tất cả các lỗi đánh máy và viết tất cả các bài kiểm tra.

Trong Rust, loại “mã hóa nháp” này rất khó, bởi vì trình biên dịch có thể và sẽ phàn nàn về mọi thứ chết tiệt không vượt qua kiểm tra loại và thời gian tồn tại - vì nó được thiết kế rõ ràng để thực hiện. Điều này hoàn toàn hợp lý khi bạn cần xây dựng triển khai cuối cùng, sẵn sàng sản xuất của mình, nhưng hoàn toàn tệ hại khi bạn đang cố gắng chế tạo một thứ gì đó cùng nhau để thử nghiệm một ý tưởng hoặc tạo nền tảng cơ bản. Macro hữu ích ở một mức độ unimplemented!nào đó, nhưng vẫn yêu cầu mọi thứ phải kiểm tra đánh máy lên và xuống ngăn xếp trước khi bạn thậm chí có thể biên dịch.

Điều thực sự gây khó chịu là khi bạn cần thay đổi chữ ký loại của giao diện chịu lực và thấy mình mất hàng giờ để thay đổi mọi nơi mà loại được sử dụng chỉ để xem liệu nỗ lực ban đầu của bạn vào thứ gì đó có khả thi hay không. Và sau đó làm lại tất cả công việc đó khi bạn nhận ra mình cần thay đổi lại.

Rust giỏi cái gì?

Chắc chắn có những điều tôi thích về Rust và các tính năng từ Rust mà tôi muốn có trong các ngôn ngữ khác. Cú matchpháp là tuyệt vời. Các đặc điểm Option, Result, và Errorthực sự mạnh mẽ và ?toán tử là một cách xử lý lỗi tinh tế. Nhiều ý tưởng trong số này có các đối tác trong các ngôn ngữ khác, nhưng cách tiếp cận của Rust với chúng đặc biệt tao nhã.

Tôi hoàn toàn sẽ sử dụng Rust cho các dự án cần hiệu suất và mức độ an toàn cao, đồng thời tôi không quá lo lắng về nhu cầu phát triển nhanh chóng các phần chính của mã với cả một nhóm đang phát triển nhanh chóng. Đối với các dự án cá nhân hoặc các nhóm rất nhỏ (ví dụ: 2–3 người), Rust có thể sẽ ổn. Rust là một lựa chọn tuyệt vời cho những thứ như mô-đun hạt nhân, chương trình cơ sở, công cụ trò chơi, v.v. trong đó hiệu suất và sự an toàn là tối quan trọng và trong những trường hợp khó có thể thực hiện kiểm tra kỹ lưỡng trước khi vận chuyển.

Được rồi, bây giờ tôi đã chọc tức đủ một nửa số độc giả của Hacker News, tôi đoán bây giờ là thời điểm tốt nhất để công bố chủ đề của bài viết tiếp theo của tôi: Tại sao nanotrình soạn thảo văn bản ưu việt hơn. Hẹn gặp lại bạn lần sau!