スタートアップでの Rust の使用: 教訓的な話

Nov 25 2022
さびは、特定のことについては素晴らしいです。しかし、迅速に動く必要があるスタートアップのためにそれを選ぶ前に、よく考えてください。

さびは、特定のことについては素晴らしいです。しかし、迅速に動く必要があるスタートアップのためにそれを選ぶ前に、よく考えてください。

この投稿のすべてのアートは、DALL-E を使用して生成されました。

プログラミング言語をめぐる聖戦を始めたり、それに巻き込まれたりしたくないので、この記事を書くのを躊躇しました。(邪魔にならない程度に言いますが、Visual Basic はこれまでで最高の言語です!) しかし、私の Rust の経験と、プロジェクトに Rust を採用するべきかどうかについて多くの人から尋ねられました。そこで、スタートアップ環境で Rust を使用することの長所と短所のいくつかを共有したいと思います。そこでは、迅速な動きとチームのスケーリングが非常に重要です。

私は特定の理由で Rust のファンであることを明確にしたいと思います。この投稿は、Rust が言語としてどのように悪いか、またはそのようなものについてではありません。ただし、私が話したいのは、Rust を使用すると、生産性が大幅に低下することはほぼ確実であり、迅速に行動しようとしている場合、これが大きな要因になる可能性があるということです。ベロシティの影響が、会社と製品にとって言語のメリットに見合う価値があるかどうかを慎重に検討してください。

前もって、 Rust はその設計目的に非常に優れていると言わなければなりません。また、プロジェクトが Rust の特定の利点 (高性能、超強力な型付け、ガベージ コレクションの必要がないシステム言語など) を必要とする場合は、その場合、Rust が最適です。しかし、Rust はあまり適していない状況で使用されることが多く、チームは Rust の複雑さとオーバーヘッドの代償を払ってもあまりメリットが得られないと思います。

Rust に関する私の主な経験は、以前のスタートアップで 2 年強 Rust を使用したことです。このプロジェクトは、多かれ少なかれ従来の CRUD アプリであるクラウドベースの SaaS 製品でした。これは、データベースの前に REST および gRPC API エンドポイントを提供するマイクロサービスのセットであり、その他のバックエンドも提供します。エンド マイクロサービス (Rust と Python の組み合わせで実装されています)。Rust が使用された主な理由は、会社の創設者の何人かが Rust の専門家だったからです。時間が経つにつれて、私たちはチームを大幅に拡大し (エンジニアリングの人員をほぼ 10 倍に増やしました)、コードベースのサイズと複雑さも大幅に増大しました。

チームとコードベースが成長するにつれて、Rust を使用し続けるために、時間の経過とともに支払う税金がますます重くなっていると感じました。開発は時々遅くなり、新機能のリリースには予想以上に時間がかかり、チームは Rust を使用するという初期の決定により生産性が大幅に低下したと感じていました。別の言語でコードを書き直せば、長い目で見れば、開発がはるかに機敏になり、納期が短縮されたでしょうが、大幅な書き直し作業の時間を見つけることは非常に困難でした。そのため、弾丸をかじって大量のコードを書き直すことにしない限り、私たちは Rust に行き詰まっていました。

さびはスライスされたパン以来最高のものであると考えられているのに、なぜ私たちにとってそれほどうまくいかなかったのでしょうか?

Rust には膨大な学習曲線があります。

私はキャリアの中で数十の言語に携わってきましたが、いくつかの例外を除いて、ほとんどの最新の手続き型言語 (C++、Go、Python、Java など) はすべて、基本的な概念が非常に似ています。各言語には違いがありますが、通常は、言語間で異なるいくつかの重要なパターンを学習することで、すぐに生産性を高めることができます。Rust では、ライフタイム、所有権、借用チェッカーなど、まったく新しいアイデアを学ぶ必要があります。これらは、他の一般的な言語で作業しているほとんどの人にとってなじみのない概念であり、経験豊富なプログラマーでさえ、かなり急な学習曲線があります。

もちろん、これらの「新しい」アイデアのいくつかは、他の言語 (特に関数型言語) にも存在しますが、Rust はそれらを「メインストリーム」言語設定に持ち込むため、多くの Rust 初心者にとっては新しいものになるでしょう。

私が一緒に働いた中で最も賢く経験豊富な開発者の一人であるにもかかわらず、チームの多くの人々 (私自身を含む) は、Rust で特定のことを行うための標準的な方法、コンパイラからのしばしば難解なエラー メッセージを理解する方法、または主要なライブラリがどのように機能するかを理解する方法 (これについては以下で詳しく説明します)。チームが知識と専門知識を共有できるように、毎週の「Rust の学習」セッションを開始しました。誰もが開発速度の遅さを感じていたため、これはすべてチームの生産性と士気を大幅に低下させました。

ソフトウェア チームで新しい言語を採用する場合の比較ポイントとして、Google の私のチームの 1 つは、C++ から Go に完全に切り替えた最初のチームの 1 つであり、完全に移行するまでに約 2 週間もかかりませんでした。 15 人余りのチームは、初めて Go でのコーディングを非常に快適に行うことができました。Rust では、言語で毎日何ヶ月も働いた後でも、チームのほとんどの人が完全に有能だと感じることはありませんでした。多くの開発者は、自分たちの機能が実装されるまでに予想以上に時間がかかり、Rust に頭を悩ませるのに非常に長い時間を費やしていることを恥ずかしく思っていると私に言いました。

Rust が解決しようとしている問題を解決する方法は他にもあります。

上で述べたように、私たちが構築していたサービスはかなり単純な CRUD アプリでした。このサービスで予想される負荷は、この特定のシステムの存続期間を通じて、最大で 1 秒あたり数クエリ以下になると予想されていました。このサービスは、実行に何時間もかかるかなり精巧なデータ処理パイプラインのフロントエンドであったため、サービス自体がパフォーマンスのボトルネックになるとは予想されませんでした。Python のような従来型の言語で問題なくパフォーマンスを発揮できるという懸念は特にありませんでした。Web 向けサービスが対処する必要がある以上の特別な安全性や同時実行のニーズはありませんでした。Rust を使用した唯一の理由は、システムのオリジナルの作成者が Rust の専門家だったからであり、この種のサービスの構築に特に適していたからではありません。

Rust は、開発者の生産性よりも安全性の方が重要であるという決定を下しました。これは、OS カーネルでコードをビルドする場合や、メモリに制約のある組み込みシステムなど、多くの状況で適切なトレードオフとなりますが、すべての場合で適切なトレードオフだとは思いません。特に、速度が重要なスタートアップではそうではありません。私はプラグマティストです。Python や Go などで記述されたコードの時折発生するメモリ リークや型エラーのデバッグにチームが時間を費やすほうが、これらの問題を完全に回避するように設計された言語を使用することで、チームの全員が 4 倍の生産性低下に苦しむよりもずっとましです .

前述したように、Google の私のチームは完全に Go でサービスを構築しましたが、これは時間の経過とともに 8 億人以上のユーザーをサポートするまでに成長し、ピーク時には Google 検索の 4 倍の QPS をサポートしました。Go の型システムやガベージ コレクターが原因で、このサービスの構築と運用に何年にもわたって問題が発生したことは、片手で数えることができます。基本的に、Rust が回避するように設計されている問題は、他の方法で解決できます— 良いテスト、良いリンティング、良いコード レビュー、そして良いモニタリングです。もちろん、すべてのソフトウェア プロジェクトにこの余裕があるわけではないので、Rust は他の状況では良い選択かもしれないと想像できます。

Rust 開発者を雇うのに苦労するでしょう。

私がこの会社にいた間、私たちはたくさんの人を雇いましたが、エンジニアリング チームに加わった 60 人以上のうち、Rust の経験があったのは 2 人か 3 人だけでした。これは、Rust 開発者を見つけようとするためではありません。彼らはそこにいないだけです。(同じように、言語やその他の技術の選択をアジャイルな方法で行う必要があるスタートアップ環境に設定するのは悪い期待だと思うので、Rust でコーディングしたいだけの人を雇うことを躊躇していました。) Rust 開発者の才能は、Rust がより主流になるにつれて、時間の経過とともに変化しますが、Rust を中心に構築することで、Rust がリスクを感じていることを既に知っている人を雇うことができます。

もう 1 つの二次的要因は、Rust を使用すると、ほぼ確実に Rust を知っているチーム内の人々とそうでない人々の間で分裂が生じることです。このサービスに「難解な」プログラミング言語を選択したため、機能の構築や生産上の問題のデバッグなどに役立つはずだった社内の他のエンジニアは、頭を悩ませたり、頭を悩ませたりすることができなかったため、ほとんど助けることができませんでした。 Rust コードベースのテール。このエンジニアリング チームの代替性の欠如は、迅速に行動し、チームの全員の強みを組み合わせて活用しようとする場合に、大きな障害になる可能性があります。私の経験では、一般的に C++ や Python などの言語間を移動するのはほとんど困難ではありませんが、Rust は十分に新しく、十分に複雑であるため、人々が一緒に作業する際の障壁となっています。

ライブラリとドキュメントは未熟です。

これは、時間の経過とともに修正されることを願っていますが、たとえば Go と比較すると、Rust のライブラリとドキュメントのエコシステムは信じられないほど未熟です。現在、Go には、世界にリリースされる前に Google の専任チーム全体によって開発およびサポートされているという利点があったため、ドキュメントとライブラリはかなり洗練されていました。それに比べて、Rust は長い間進行中の作業のように感じられてきました。多くの一般的なライブラリのドキュメントはかなりまばらであり、特定のライブラリのソース コードを読んで使用方法を理解する必要があることがよくあります。これは悪いです。

チームの Rust 弁護者は、「async/await はまだ本当に新しい」や「そのライブラリのドキュメントが不足している」などとよく言いますが、これらの欠点はチームにかなり大きな影響を与えました。私たちのサービスの Web フレームワークとして Actix を採用したことは、早い段階で大きな間違いを犯しました。この決定は、ライブラリの奥深くに埋もれたバグや問題に遭遇し、誰も修正方法を見つけることができず、多大な苦痛と苦痛をもたらしました。(公平を期すために、これは数年前のことであり、今では状況が改善されている可能性があります。)

もちろん、この種の未熟さは Rust に固有のものではありませんが、チームが支払わなければならない税金になります。コア言語のドキュメントとチュートリアルがどれほど優れていても、ライブラリの使用方法を理解できなければ、大した問題ではありません (もちろん、すべてをゼロから作成する予定がある場合を除きます)。

さびは、新しい機能のラフアウトを非常に困難にします。

他の人のことは知りませんが、少なくとも私にとっては、新しい機能を構築するとき、通常、すべてのデータ型、API、およびその他の詳細を前もって調整しているわけではありません。私はしばしば、いくつかの基本的なアイデアが機能するようにコードをぶち壊し、物事がどのように機能するかについての私の仮定が多かれ少なかれ正しいかどうかをチェックしています。たとえば、Python でこれを行うのは非常に簡単です。なぜなら、タイピングなどをすばやく自由に行うことができ、アイデアの大まかな作成中に特定のコード パスが壊れても心配する必要がないからです。後で戻って、すべてを整理し、すべての型エラーを修正して、すべてのテストを書くことができます。

Rust では、この種の「ドラフト コーディング」は非常に困難です。なぜなら、コンパイラは、明示的に設計されているように、型と有効期間のチェックを通過しないすべてのひどいものについて文句を言うことができ、また文句を言うからです。これは、本番環境に対応した最終的な実装を構築する必要がある場合は完全に理にかなっていますが、アイデアをテストしたり、基本的な基盤を整えたりするために何かを一緒に作り上げようとしている場合は、まったく役に立ちません。このunimplemented!マクロはある程度は役に立ちますが、コンパイルする前に、スタックを上下にすべて型チェックする必要があります。

本当に厄介なのは、耐荷重インターフェースの型シグネチャを変更する必要があり、その型が使用されているすべての場所を変更するのに何時間も費やしていることに気付いたときです。そして、もう一度変更する必要があることに気付いたときに、そのすべての作業をやり直します。

Rustは何が得意ですか?

私が Rust について気に入っている点や、Rust の機能を他の言語で使用したいと思っている点は間違いなくあります。match構文は素晴らしいです。、OptionResultおよびErrorトレイトは非常に強力で、?演算子はエラーを処理する洗練された方法です。これらのアイデアの多くは、他の言語にも相当するものがありますが、それらに対する Rust のアプローチは特にエレガントです。

高レベルのパフォーマンスと安全性を必要とし、急速に成長しているチーム全体でコードの主要部分を急速に進化させる必要性についてそれほど心配していないプロジェクトには、絶対に Rust を使用します。個々のプロジェクト、または非常に小規模な (たとえば 2 ~ 3 人の) チームの場合、Rust で十分でしょう。Rust は、カーネル モジュール、ファームウェア、ゲーム エンジンなど、パフォーマンスと安全性が最も重要であり、出荷前に徹底的なテストを行うことが難しい場合に最適です。

さて、Hacker News の読者層の半分を十分に怒らせたので、次の記事のトピックを発表する絶好の機会だと思いnanoます。またね!