2月1日、Tracebitが「Why Tracebit is written in C#」と題した記事を公開した。この記事では、TracebitというスタートアップがC#を採用するに至った理由やその背景について詳しく紹介されている。以下に、その内容を紹介する。
プロジェクトを立ち上げる際、最初に直面した決定事項の一つが技術スタックの選択だった。AWSやPostgresといった技術の選択は比較的容易だった一方、開発言語については多くの選択肢があり、選定は難航した。
そこで予想外にもC#が選ばれた経緯について、本記事では詳しく説明している。多くのエンジニアがPython、TypeScript、Golang、Rust、JVM系の言語などで作るのを選択するかもしれないが、いろいろと考慮した結果、TracebitではC#が採用されたという。
C#を選んだ理由1: 生産性
記事によると、スタートアップが技術スタックを選ぶときには「 よく知っていて、かつ信頼できるものを使うべき 」というアドバイスがしばしば挙げられるという。
その点筆者はPythonやTypeScriptでの大規模かつクリティカルなコードベースでの経験が深く、当然それらの言語の採用も検討したが、 一方でそうした言語の動的型付けや依存関係の管理などに難しさを感じていた。 そのうえで、過去に使ったことのない言語を含めて検討し、C#を選んだという。
Tracebitでは開発言語を選定するうえで 生産性(Productivity)を最重要視 していたそうだ。特にスタートアップの初期フェーズでは、アイデアを迅速に実装・検証し、間違った方向性を修正するサイクルが極めて重要になる。この段階ではコードを書き始める前の段階から素早い検証が必要で、コードを書き始めた後もアイデアを具体的に形にしやすい言語が求められる。
さらに、すべてのソフトウェアはメンテナンスを要するため、その手間を最小化しながらリファクタリングを安全かつ効果的に実行できるかどうか、という点も生産性に大きく関わる。最終的にコードベースが大きくなり、エンジニアチームが拡大したときでも、生産性とソフトウェアの品質を維持できる言語かどうかが重要である。 .NETはこの点で非常に優れており、今後の拡張にも十分耐えられる基盤だと評価している。
C#を選んだ理由2: フリー&オープンソースであること
次のポイントはオープンソースであり、かつ無料で利用できるプラットフォームかどうかだった。特にスタートアップの初期段階では、ソースコードに直接触れられることや、不要なコストを避けられることが大きな利点になる。
PythonやTypeScriptを含む多くの技術スタックがこの条件を満たすなか、 .NETも現在はMITライセンスでオープンソース化されている。
(Microsoftに対する以前の印象からは意外に思う人もいるかもしれない)
Tracebitは現在AWSを使っており、Microsoft Azure以外のプラットフォームへのデプロイも全く問題ない。
C#を選んだ理由3: クロスプラットフォーム
これもあまり知られていないことだが、 C#/.NETはクロスプラットフォームである。
Windowsに依存せず、LinuxやmacOS上で問題なく動作する点は、特にクラウドやコンテナを多用する現代の開発環境では重要だと記事では述べられている。実際にTracebitのコードはArch Linux、macOS、Linux(ARMコア)上で開発・実行されており、MicrosoftやUbuntuが共同で提供するセキュリティリスクを抑えたChiseledコンテナイメージも活用しているという。
C#を選んだ理由4: 人気
言語の人気度自体は品質の指標とは限らないが、採用時にプールとなるエンジニア人口や、豊富なライブラリ・ドキュメント・SDKの存在は無視できない利点だと記事では指摘している。スタートアップでは拡大時に迅速なエンジニア採用が必要になるため、特に重要な要素だ。また人気が高いということはドキュメントやサンプルコード、既存のバグ情報などが豊富なだけでなく、LLMの学習データにも含まれやすい。この点が開発者の生産性向上につながる。
その点C#は、 Hacker Newsなどではあまり話題にならないものの、堅実に利用されている言語の一つだ。 C#はTIOBE IndexやStack Overflowの開発者調査などで常に上位に位置しており、世界的なユーザーベースを持つ。
Stack Overflowの開発者アンケートでも常に10位以内をキープしている
C#を選んだ理由5: メモリ管理
メモリ安全性は記事内でも「絶対事項」と表現されるほど重要視されている。特にセキュリティ関連のスタートアップがC/C++のようなメモリ管理を必要とする言語を使うリスクは高いと考えられており、CISA(アメリカ国土安全保障省サイバーセキュリティ・インフラセキュリティ庁)の勧告も一因であるようだ。C#は自動的なメモリ管理を備えており、この要件を満たす。
ガベージコレクション(GC)を導入している言語を選んだ理由としては、リアルタイムシステムや組込み向けソフトウェアのようにGC停止が問題となるケースではないこと、また Rustの所有権システムに時間を取られるよりは、まず製品の試行錯誤にリソースを割きたかった ためと記事では述べられている。
C#を選んだ理由6: 静的型付け
静的型付けであることも決め手の一つだったようだ。動的型付け言語で生産したコードのメンテナンス性と比較して、型情報がもたらす安全性や可読性、IDEでの「定義へジャンプ」や「参照の検索」などが大きな利点だと書かれている。C#の型システムは強力だが難解すぎるわけではなく、ジェネリクスやレコード型などが備わっている点も評価されている。今後追加が検討されているDiscriminated Unionの導入にも期待があるという。
C#を選んだ理由7: 安定性
.NETのリリースサイクルは比較的安定している点も評価材料になっている。毎年決まった時期にメジャーバージョンがリリースされ、LTS(Long Term Support)とSTS(Short Term Support)が交互に登場する。記事によると、Tracebitの開発でもすでに3回のメジャーバージョンアップを経験しているが、いずれも短期間で対応が可能だったとのことだ。
.NETのリリーススケジュールは明快なルールに基づいている
アクセス修飾子(public、private、internalなど)の存在もメジャーアップデートの互換性を保つうえで有効だという。公開API以外を触らないコーディングが自然と推奨されるため、アップデートでの互換性問題が起きにくいとの見方が示されている。
C#を選んだ理由8: 組み込みの機能、ライブラリ、ツールが強力
.NETプラットフォームには多種多様な高品質ライブラリやAPIが標準で含まれており、Microsoftが公式にメンテナンスしているものが非常に多い。汎用的なコレクションや日時処理から、JSONのストリーミング・デシリアライズ、暗号化、構造化ログ出力、テストフレームワーク、高性能なHTTPサーバー、OpenIdConnect実装、ORM、WASMコンパイラ、OpenAPI生成、分散アクターフレームワークなどに至るまでカバー範囲が広い。
LINQやEntity Framework、TPL Dataflow、ASP.NET、F#など.NETプラットフォーム独自の機能についても、これらがもたらす生産性と技術的な優位性を高く評価しており、他の言語スタックでは代替が難しい要素も多々ある。
.NETの豊富なツール群も大きな利点である。
JetBrains社のRiderは非商用利用で無料提供されており、デバッグやテスト、リファクタリング機能はもちろん、Dynamic Program Analysisを通じて、クエリの非効率やメモリ割り当ての問題などを自動的に検出できる。メモリプロファイリングなどの機能も充実しており、大規模なアプリケーション開発に非常に適しているという。
また、実行中のコンテナ上で詳細な診断データを収集できるツールも整備されており、ステージング環境特有の問題をリアルタイムでトレースして原因を特定することが容易になっている。
C#を選んだ理由9: パフォーマンス
.NETのパフォーマンス面も記事では高く評価されている。特にTechEmpowerのベンチマークにおいて、ORMとフルフレームワークを用いたケース(Fortunesなど)で上位に位置している点が取り上げられている。Microsoftの.NETチームはパフォーマンス改善に積極的で、毎年のアップデートごとに詳細な改善点を公開しており、アプリケーションをアップグレードするだけで大きく性能が向上するケースもあるようだ。
TechEmpower 'Fortunes'ベンチマークのトップ15 (ORM & Full Framework)のトップでも常連
まとめ
数千のコミットと多数のリリースを重ねてきた現在でも非常に満足度が高く、もし大きな障害があれば別言語に移行していただろうが、その必要性は感じなかったとのことだ。過去にC#を触ったことのないエンジニアでも、比較的早く開発に慣れることができる点も評価に値する。
もちろん、言語選択に絶対的な正解はなく、プロダクトの要件やチーム構成によって最適解は変わるだろう。ただし、初期段階では検討の余地にすら挙がらなかったC#を実際に使ってみた結果、予想外にメリットが大きかったという振り返りが印象的である。
詳細はWhy Tracebit is written in C#を参照していただきたい。