在初創公司使用Rust的警示故事 - ndw

banq發表於2022-11-24

我在Rust方面的主要經驗來自於在以前的一家創業公司工作了2年多一點的時間。這個專案是一個基於雲的SaaS產品,或多或少是一個傳統的CRUD應用:它是一組微服務,在資料庫前提供一個REST和gRPC API終端,以及其他一些後端微服務(本身是用Rust和Python組合實現的)。使用Rust主要是因為公司的幾個創始人是Rust專家。隨著時間的推移,我們的團隊得到了很大的發展(工程人員增加了近10倍),程式碼庫的規模和複雜性也隨之大大增加。

隨著團隊和程式碼庫的增長,我感覺到,隨著時間的推移,我們為繼續使用Rust付出了越來越沉重的代價。開發有時很緩慢,推出新功能所需的時間比我預期的要長,而且團隊從早期使用Rust的決定中感受到了真正的生產力下降。從長遠來看,用另一種語言重寫程式碼會使開發更加靈活,並加快交付時間,但要找到時間進行重大的重寫工作是非常困難的。所以我們只能用Rust,除非我們決定咬緊牙關重寫大量的程式碼。

Rust有一個巨大的學習曲線
在我的職業生涯中,我使用過幾十種語言,除了少數例外,大多數現代程式性語言(C++、Go、Python、Java等)在其基本概念方面都非常相似。每種語言都有其不同之處,但通常只需學習一些不同語言的關鍵模式,然後就能很快提高工作效率。然而,對於Rust,人們需要學習全新的概念--像生命期、所有權和借用檢查器。對於大多數使用其他普通語言的人來說,這些概念並不熟悉,即使對於有經驗的程式設計師來說,也有一個相當陡峭的學習曲線。

當然,其中一些 "新 "想法在其他語言中也有--尤其是函式式語言--但Rust將它們帶入了 "主流 "語言環境,因此對許多Rust新手來說是新的。

儘管他們是我工作過的最聰明和最有經驗的開發者,但團隊中的許多人(包括我自己)都在努力理解在Rust中做某些事情的典型方法,如何摸索來自編譯器的經常是神秘的錯誤資訊,或者如何理解關鍵庫的工作原理(下面有更多關於這個的內容)。我們開始為團隊舉辦每週一次的 "學習Rust "會議,以幫助分享知識和專長。這對團隊的生產力和士氣都是一個很大的消耗,因為每個人都感覺到了開發速度的緩慢。

作為一個軟體團隊採用新語言的比較點,我在谷歌的一個團隊是第一批完全從C++轉向Go的團隊之一,在整個15人左右的團隊第一次相當舒適地用Go編碼之前,只花了兩個星期。對於Rust,即使每天用該語言工作了幾個月,團隊中的大多數人也從未感到完全勝任。許多開發人員告訴我,他們經常感到尷尬,因為他們的功能落地所需的時間比他們預期的要長,而且他們花了這麼長的時間來思考Rust的問題。

還有其他的方法來解決Rust所要解決的問題
如上所述,我們正在構建的服務是一個相當簡單的CRUD應用。在這個特定系統的生命週期中,這個服務的預期負載將不超過每秒幾個查詢。該服務是一個相當複雜的資料處理管道的前端,可能需要許多小時才能執行,所以該服務本身預計不會成為效能瓶頸。我們並不特別擔心像Python這樣的傳統語言會在提供良好的效能方面遇到任何問題。除了任何面向網路的服務需要處理的問題之外,沒有特別的安全或併發需求。我們使用Rust的唯一原因是該系統的原始作者是Rust專家,而不是因為它特別適合於建立這種服務。

使用Rust是已經做出決定,安全比開發者的生產力更重要。
這在很多情況下是正確的權衡--比如在作業系統核心中構建程式碼,或者用於記憶體受限的嵌入式系統--但我不認為這在所有情況下都是正確的權衡,尤其是在速度至關重要的初創公司。
我是一個實用主義者。我寧願讓我的團隊花時間去除錯偶爾出現的記憶體洩漏或用Python或Go編寫的程式碼的型別錯誤,也不願讓團隊中的每個人因為使用一種旨在完全避免這些問題的語言而遭受4倍的生產力打擊。

正如我在上面提到的,我在谷歌的團隊建立了一個完全用Go編寫的服務,隨著時間的推移,它已經支援了超過8億的使用者,並且在高峰期的QPS大約是谷歌搜尋的4倍。在建立和執行這項服務的幾年裡,我可以用一隻手數出我們遇到由Go的型別系統或垃圾收集器引起的問題的次數。
基本上,Rust旨在避免的問題可以透過其他方式解決--透過良好的測試、良好的提示、良好的程式碼審查和良好的監控。當然,並不是所有的軟體專案都有這樣的奢侈,所以我可以想象,在那些其他情況下,Rust可能是一個不錯的選擇。

你將很難聘請到Rust開發人員
在我在這家公司工作期間,我們僱傭了一大批人,但在加入工程團隊的60多人中,只有兩三個人以前有Rust的經驗。這並不是因為我們想找到Rust開發人員--他們只是不在那裡。(同樣,我們也不願意僱傭那些只想用Rust編碼的人,因為我認為在一個需要以敏捷的方式選擇語言和其他技術的創業公司裡,這是一個不好的期望)。隨著Rust成為主流,這種Rust開發人才的匱乏將隨著時間的推移而改變,但圍繞Rust建立的假設是你能夠僱用已經知道它的人,這似乎是有風險的。

另一個次要因素是,使用Rust幾乎肯定會導致團隊中懂Rust的人和不懂Rust的人之間產生分裂。因為我們為這項服務選擇了一種 "深奧 "的程式語言,公司裡的其他工程師本來可以在構建功能、除錯生產問題等方面提供幫助,但由於他們對Rust程式碼庫摸不著頭腦,所以基本上無法提供幫助。當你試圖快速發展並利用團隊中每個人的綜合實力時,工程團隊中缺乏可替代性可能是一個真正的責任。根據我的經驗,人們在C++和Python等語言之間的轉換一般沒有什麼困難,但Rust足夠新,也足夠複雜,這給人們的合作帶來了障礙。

庫和檔案都不成熟
這個問題(我希望!)會隨著時間的推移而得到解決,但與Go相比,Rust的庫和檔案生態系統是非常不成熟的。現在,Go的好處是在向世界釋出之前,它是由Google的整個專業團隊開發和支援的,所以檔案和庫都相當成熟。相比之下,Rust長期以來一直被認為是一個正在進行的工作。很多流行的庫的檔案是相當稀少的,人們經常需要閱讀某個庫的原始碼來瞭解如何使用它。這很糟糕。

團隊中的Rust辯護士經常會說 "async/await還很新 "和 "是的,那個庫的檔案很缺乏 "之類的話,但這些缺點對團隊的影響相當大。我們在早期犯了一個巨大的錯誤,那就是採用Actix作為我們服務的網路框架,這個決定導致了巨大的痛苦和折磨,因為我們遇到了埋藏在庫中的錯誤和問題,沒有人能夠想出如何解決。(公平地說,這是幾年前的事了,也許現在情況已經有所改善了)。

當然,這種不成熟並不是Rust所特有的,但它確實是你的團隊必須支付的一種稅。不管核心語言的檔案和教程有多好,如果你不知道如何使用這些庫,那就沒有什麼意義了(當然,除非你打算從頭開始寫所有東西)。


Rust使得新功能的粗略設計變得非常困難
我不知道其他人怎麼樣,但至少對我來說,當我構建一個新的功能時,我通常不會事先考慮所有的資料型別、API和其他的細節。我經常只是在放屁,試圖讓一些基本的想法發揮作用,並檢查我對事情應該如何工作的假設是否大致正確。在Python中這樣做是非常容易的,因為你可以快速和寬鬆地處理諸如型別的事情,並且在你粗略地考慮你的想法時,不用擔心某些程式碼路徑被破壞。你可以稍後回去,讓它變得整潔,修正所有的型別錯誤,並編寫所有的測試。

在Rust中,這種 "編碼草案 "是非常困難的,因為編譯器可以而且會抱怨每一個沒有透過型別和壽命檢查的該死的東西--因為它是明確設計的。當你需要建立最終的、可用於生產的實現時,這是很有意義的,但當你試圖把一些東西拼湊起來以測試一個想法或獲得一個基本的基礎時,這絕對是糟糕的。未實現的!宏在一定程度上是有幫助的,但仍然需要在編譯前對堆疊上下的所有東西進行型別檢查。

真正讓人頭疼的是,當你需要改變一個承重介面的型別簽名時,你會發現自己花了好幾個小時來改變每一個使用該型別的地方,只是為了看看你最初的嘗試是否可行。當你意識到你需要再次改變它的時候,又要重新做所有的工作。

Rust的優點是什麼?
我喜歡Rust的一些東西,也喜歡Rust的一些特性,我希望在其他語言中也能有這些特性。匹配語法很好。選項、結果和錯誤屬性非常強大,而"?"運運算元是一種處理錯誤的優雅方式。許多這些想法在其他語言中都有對應的功能,但Rust對它們的處理方式特別優雅。

對於那些需要高水平的效能和安全的專案,我絕對會使用Rust,對於這些專案,我並不十分擔心需要和整個快速成長的團隊一起快速進化程式碼的主要部分。對於單個專案,或者非常小的(比如2-3人)團隊,Rust很可能就很好。對於像核心模組、韌體、遊戲引擎等對效能和安全性要求很高的專案來說,Rust是一個很好的選擇,在這種情況下,可能很難在交付前做真正徹底的測試。


Reddit網友
1、像 Spring Boot 這樣的行業標準和經過實戰檢驗的框架將讓您在幾分鐘內啟動並執行……更不用說“Java 生態系統”中近 30 年的工具、庫和社群了。太多的“工程師”很難處理他們的工作不是玩有趣的技術,而是交付產品

2、我只是用一個 200 行的 Rust 服務替換了一個 1200 行的 Spring Boot 服務,它做的更多、更快,並且其部署容器的大小不到 Java 容器大小的 1/30。它解決了原本需要在經過驗證的臃腫 Java 實現中實現的問題。

3、問題不在於把Rust用在它本不應該用的地方,而在於把Rust用在生態系統還不成熟的地方。當你不關心低階別的細節時,Rust語言實際上做了一個了不起的工作,讓你感覺自己是一個高階語言。一旦你適應了Rust,它的編寫就會變得簡單而快速,而且比我知道的任何其他語言都更容易讓人相信你的程式碼會工作。
也就是說,在Rust的生態系統中,沒有任何東西可以讓你像NextJS那樣,把一個功能齊全、可以投入生產的網路應用放在一起。NextJS有一個非常豐富的生態系統,你可以透過安裝和配置正確的軟體包在一天內解決大多數常見問題。要想讓Rust取代這一點,還需要很長一段時間。

4、他們選錯了工具。作為技術長,我對基礎技術的決策是:

  • 它有滿足我需要的可靠、穩定的庫嗎?
  • 它有一個龐大的社群(包括在 SO 上)嗎?
  • 我和我的幾個開發人員有經驗嗎?
  • 我可以租嗎?在我所在的地區或時區,經驗豐富的開發人員是否稀有且價格昂貴?如果我的專家離開了而我需要更換他們,我有多糟?人們對使用它感到興奮嗎?
  • 它是否滿足我的產品的效能和速度要求?

理想情況下,我可以對所有這些回答是。與替代方案相比,Rust 不適用於網路Web應用程式。

5、我不認為 Rust 對於 CRUD API 來說很棒,但是沒有人真的在那裡說“絕對不要將 Rust 用於 CRUD API”。事實上,有用於 Rust web 開發的箱子、書籍和網站(有警告):https ://www.arewewebyet.org/

6、我個人喜歡 Rust webapp 框架,比如 Rocket,並且喜歡編譯器提醒你並迫使你處理常見的陷阱,比如可為 null 的資料和驗證。
本文中提到的所有內容都是完全有效的批評,可能減去檔案部分(Rust 檔案非常好)。我沒有在初創公司工作過,所以我認為對“速度”的需求使 Rust 在該領域成為一個弱者。

7、在使用 Go 很多次之後,我認為在這方面它並不比 Typescript 好多少,我稱之為真正的中間立場。
Go 將程式碼表現力發揮到極致,這經常導致大量程式碼重複——例如檢視錯誤處理的樣子。此外,無處不在的 goroutine 將併發性帶到了極致,這使得它在某些網路繁重的場景中實際上比 python 慢,在這些場景中每個網路幀都開始透過 goroutines 排程程式。

8、抱歉,我認為具有足夠經驗的 Rust 可以成為一種非常高效的語言。
我必須承認,我花了很長時間才達到目前的 Rust 熟練程度,但現在我在 Rust 方面的工作效率遠高於我在工作中使用的語言(例如 Go)。
話雖這麼說,除非其他人都對 Rust 感到非常滿意,否則我不會在工作中用 Rust 實現新服務,這主要是因為“Rust 有一個巨大的學習曲線”。
問題不在於 Rust 的生產力不好,問題在於它需要花費大量的時間和精力才能達到比其他語言更高的生產力。

 

相關文章