😺

Nx と Turborepo の比較

2022/01/08に公開約10,800字

Nx と Turborepo の比較

※この記事はNx and Turborepoの日本語訳です。

Turborepo は、2021年12月にリリースされたばかりのビルドツールです。Nxから多くのアイデアを拝借しているため、当然ながら Nx と Turborepo を比較する人もいます。このドキュメントでは、Turborepo がどのようなものであるかを説明します。

Nx と Turborepo の関係は、Nx と Yarn ワークスペースや Lerna の関係と同じではありません。Nx と Yarn ワークスペースは補完的な関係にあり、状況によっては両方を使用することは理にかなっています。(現時点では)Turborepo は Nx のサブセットであり、両方を使う意味はないと考えています。

私たちは公平になるように最善を尽くしますが、もちろん、お客様ご自身で調査していただく必要があります。ドキュメントを読んだり、いろいろ試してみたりしてください。

この文書は2021年12月14日に書かれたものです。この時点では、Turborepoはリリースされたばかりで、5k行程度のコードです。小さなプロジェクトです。以下に記載されていることの多くは、Turborepoが今できないことであり、Nx ができることでもあります。いずれは Turborepo が対応するものも出てくると思います。ですから、これを読んだ方は、後にドキュメントを参照してください。

Nx (オープンソースツール) と Nx Cloud (SaaS製品) は明確に分けて考えています。Turborepoには、そのような分離はありません。したがって、このガイドでは、Turborepo と Nx + Nx Cloud を比較しています (つまり、リンゴとリンゴの比較です)。とはいえ、分散キャッシュや分散タスク実行などの機能を得るために、Nx Cloud を使う必要はありません。Nx Cloud を使いたくない場合は、パブリック API を提供しているので、自分で構築することも可能です。

私たちは、3つの方法でツールを比較するつもりです。機能技術コミュニティの3つの観点からツールを比較します。

機能

1. ワークスペースの理解

小さくないモノレポ管理ツールに最低限必要なものは、モノレポのワークスペース構造、どのようなプロジェクトがあり、それらが互いにどのように関連しているかを理解できることです。

  • Turborepo は、プロジェクト間の関係を理解するために、package.json ファイルのみを解析します。Nx の内蔵プラグインも package.json ファイルを解析しますが、それに加えて JS/TS ファイルも解析するため、偽の (パッケージのインストールや公開のために使用しない) package.json ファイルがリポジトリに存在することはありません。Nx には、他の言語 (Golang や .Net など) に対してそれを行うプラグインがあります。
  • プロジェクトグラフの計算は、複雑なワークスペースでは多くの時間がかかるため、Nx はバックグラウンドで分析を行います。Turborepo は、リクエスト時に実行します
  • Nx は、複数のチームが参加するモノレポには欠かせない可視化ルールを持っています。モノレポの中には、自分のチームのプライベートなものだから、他のチームには依存させたくないというものもありますよね。Turborepo には、可視化ルールがありません。可視化ルールは、モノレポが「大きな泥の塊(“big ball of mud”)」になるのを防ぐことができます

2. 依存関係グラフの可視化

モノレポワークスペースを視覚的に探索できることは、大規模なモノレポワークスペースのデバッグやトラブルシューティングを行う際に、大きな意味を持つことがあります。

  • Nx はリッチでインタラクティブなビジュアライザーを持っています(ビデオを見る)。
  • Turborepo では、基本的な graphviz のイメージエクスポートが可能です。

3. 影響のあるプロジェクト/パッケージの検出

スピードを最適化することは、スケーラビリティを確保するために非常に重要です。一つの戦略は、モノレポのワークスペース構造に関する知識を Git と組み合わせて活用し、与えられたプルリクエストがどのプロジェクトやパッケージに影響を与えるかを判断することです。

  • Nx と Turborepo の両方がこれをサポートしています。

詳しくはこちらのEgghead lesson on scaling CI runs with Nx Affected Commandsをご覧ください。

4. ローカルタスクの実行

モノレポでは、1台のマシンで複数のタスクを正しい順序で並列に実行することが重要です。

  • Nx も Turborepo もこれをサポートしています。また、どちらも、異なる種類のターゲット (例:テストやビルド) を、同じコマンドの一部として実行することができます。
  • Nxでは、タスクの実行を含め、すべてがプラガブルです。独自の戦略を提供することができます (例えば、可能な限り jest モノレポモードを使って複数の jest タスクを実行するなど)。Nxのプラグインはカスタムストラテジーを提供します。Turborepo の仕組みは、現時点ではプラガブルではありません。

2. ローカルコンピュテーションキャッシュ

ローカルコンピュテーションキャッシュ (しばしば「ビルドキャッシング」とも呼ばれる) は、同じコマンドを2回実行する必要がなく、ローカルキャッシュからファイル artifacts やターミナル出力を復元する処理です。Nx では、(ビルドだけでなく) あらゆるタイプのタスクに実際に適用できるため、私たちはこれを「コンピュテーションキャッシュ」と呼んでいます。

  • Nx と Turborepo の両方がこれをサポートしています。
  • Turborepo は、キャッシュヒット時に常にファイルの削除と再作成を行いますが、Windowsでは遅く、また、他のツールがこれらのファイルを監視している場合、悪影響があります。Nx はどのファイルがどこに復元されたかを知っており、正しいファイルを正しい場所に残すことができます。これは、大規模なアプリケーションを段階的に構築する場合や、マイクロフロントエンドからシステムを構築する場合に便利です。このような場合、ビルドコマンドはしばしば数百のタスクを引き起こし、その大部分はキャッシュヒットです。常にファイルを削除してリストアすることは、このシナリオの実装をより困難なものにしています。
  • Turborepo では、端末の出力をキャプチャするために、パイプを使用するだけです。パイプは、"面白い"出力をするタスク (Cypress, webpackなど) に対してうまく機能しません。その結果、Turborepo を使用した場合と使用しない場合の端末出力は同じには見えません。Nx はパイピングを使用することができますが、他の手法もサポートしています。その結果、Nxは出力を "そのまま"キャプチャすることができます。また、再生された出力は、元の出力と完全に一致します
  • 繰り返しますが、Nx はプラガブルなので、与えられた計算に何が影響を与えるかを決定するプラグインを自分で書くことができますし、いくつかの Nx プラグインはそうなっています。

6. 分散コンピュテーションキャッシュ

ローカルでのコンピュテーションキャッシュはローカルでのスピードアップに役立ちますが、本当のメリットは、そのキャッシュを CI やチームメイトとリモートで配布・共有したときから始まります。

  • Nx と Turborepo の両方がこれをサポートしています。
  • Nx はパブリックAPIを公開しており、独自のリモートキャッシュの実装を提供することが可能です (実際に提供している企業もあります)。Turborepo の実装はカスタマイズできないので、Turborepo の分散キャッシュを使う必要があります。
  • 独自の分散キャッシュを実装しなくても、Nx Cloudを利用することができます。Nx Cloud にはオンプレミス版があり、独自のキャッシュ artifacts をホストすることができます。Turborepo は、オンプレミスのソリューションを提供していません

7. タスクの分散実行

Nx の重要な機能のひとつは、1台のマシンでタスクを並列化するだけでなく、複数のマシンに完全に自動的に分散させることができることです。Nx は、すべてのタスクが 1台のマシンで実行されているかのようなデベロッパーの人間工学を維持したまま、正しい順序で並列に実行することができます。

  • Nx はタスクの分散実行をサポートします。Nx は任意のコマンドを複数のマシンで実行することができますが、1台のマシンで実行したときの開発環境を維持します。これは、Bazel (Googleで使われているビルドツール) が行っていることに似ています。私たちはそこからインスピレーションを得ました。
  • Turborepo はタスクの分散実行をサポートしていません。 Turborepo を使うときに一番良いのは binning/sharding で、これは規模の大きいワークスペースではうまくいきません。
  • 分散タスク実行は、コンピュテーションキャッシュよりモノレポのスケーリングに大きな影響を与えます。キャッシュなしでは、あるいは分散なしではスケールしません。
  • これは、Turborepo に欠けている、パフォーマンスとスケーリングに関する最大の特徴です。また、構築するのが最も困難な機能です。
  • Nx の他の部分と同様に、提供されている公開 API を使って、独自の分散タスク実行機能を構築することができます。独自のバージョンの分散キャッシュを実装しないことを選択した場合、Nx Cloud を使用することができます。Nx Cloud のオンプレミス版 があるので、成果物がどこに保存されるかを完全にコントロールすることができます。

もっと詳しく知りたい方は、Distributing CI - Binning and Distributed Task Executionの記事を参照してください。

8. エディタ対応

Nxのコマンドはすべてコマンドラインから実行することができます。しかし、モノレポが大きくなり、複数のチームや何百ものプロジェクトがある場合、コマンドを実行するプロジェクトを見つけるだけでも、時には困難な場合があります。そこで高品質なIDEとの連携があると、時間の節約になります。

  • Nx には VSCodeと WebStorm/Intellij のプラグインがあります。
  • Turborepo にはプラグインがなく、メンテナもエディタサポートを提供するつもりはないと言っています。

詳しくは、Eggheadのレッスン(https://egghead.io/lessons/javascript-generate-new-projects-for-nx-with-nx-console)をご覧ください。

9. Configuration

Nx は過去5年間で成長し、一般的なセットアップのための精選されたプリセットを提供してきましたが、同時に柔軟性と拡張性を維持することにも重点を置いています。

  • Nx のコア部分に関しては、Nx と Turborepo で必要になる Configuration の量は同じです。Nx は、ワークスペースのルートに小さな json ファイルを 1 つ生成します。Turborepoは、その設定を package.json に追加します。

すぐに使い始めることはとても簡単です。以下、いくつかの例をご覧ください。

10. 透明性

Nx Core は開発を速くしますが、コマンドの実行方法やターミナル出力の見栄えは変えません。Nxのターミナル出力とTurboのターミナル出力を比較してみましょう。

nx and turbo terminal output

Nx ではターミナル出力は変わりません。スピナー、アニメーション、色は Nx を使っても使わなくても同じです (この結果を得るために Node.js を導入しています)。また、重要なのは、キャッシュから復元するとき、Nx はコマンドを実行したときと同じターミナル出力を再生することです。Turbo の出力を見てください。スピナーもアニメーションも色もありません。Turbo を使用して実行するほとんどのコマンドは、Turbo なしで同じコマンドを実行するのとは異なって見えます(そして、私たちの意見では、より悪く見えます)。

多くの Nx ユーザーは、Nx を使用していること、あるいは Nx が何であるかさえ知らなくて良いのです。実行するものは同じに見えますが、ただ速くなっただけなのです。

プラグインと、サポートされている機能

以下の機能は比較するのが難しいです。Nxの範囲はより広いです。モノレポで開発するということは、単に速く開発すること (技術的なスケーリング) だけではなく、チームが効率的に作業できるようにすること (組織的なスケーリング) も意味します。 モノレポが10パッケージで単一のチームによって管理されているなら、組織的なスケーリングは関係ありませんが、数千のプロジェクトと数百または数千の貢献者 (エンタープライズなシステム) での巨大なモノレポでは組織スケールは技術のスケールよりも重要 (あるいはもっと重要かもしれません) です。

エンタープライズ規模でのスケーリングに必要なことをいくつか紹介します。

  • 開発者は、自分が一度も作業したことのないプロジェクトのテストを実行することができます。
  • 開発者は、artifacts を一貫して作成することができます。それは一貫性を持った変更を加えることができます (例えば、新しいAPIに移行するときなど)。
  • 開発者は、サードパーティの依存パッケージ (React/Cypress/Storybookなど) を新しいバージョンに移行することができます。
  • 開発者は、モノレポ全体の大規模なリファクタリングを自動化できます。
  • 開発者は、数百台のマシンでビルドされた数千のプロジェクトのテスト出力を見ることができます (出力されるものが多すぎて何が起こっているのかがわからないため、単純に標準出力に出力させることはできないようなもの)。
  • 開発者は、キャッシュミスを分析するツールがあります。
  • 開発者は、モジュール同士の依存制約を可視化することができる
  • ...

Nx はプラグインと Nx Cloud を使用して、これらの作業をサポートします。例えば、50台のマシンで実行された分散タスクの出力をNx Cloudで一度に見ることができます (オプションでGitHub PRに統合することも可能です)。キャッシュミスやタスクの分散を分析できるので、CI実行のデバッグが必要なときに役立ちます。

現時点では、Turborepo はそのようなことを一切行いません。そのため、モノレポの規模が小さい場合は、他のツールでこれらの機能を実装する必要があります。

Nx は、ビルドツールの VSCode のようなものです。VSCode では、VSCode のコアとなるプレーンなセットアップで始めることができますし、それは問題ないでしょう。しかし、もしあなたの経験を強化したいのであれば、Git、Docker、Mongo などを管理するための拡張機能を追加するオプションが用意されています。同様に、すべての Nx プラグインや Nx Cloud の GitHub インテグレーションを使用する必要はありません
Nxは、あなたが使っているどのツールにも取って代わるものではありませんし、「全部入り」でもないのです。Turborepo のように Nx プラグインや Nx Cloud なしでスタートすることもできます。また、Nx がネイティブでサポートするプラグインや、多くのコミュニティプラグインを追加していくこともできます。Turborepo はプラグイン化できないので、同じようなことをするならば、別のツール (GitTower, DataGrip, Mongo Compass) を使う必要があります

Nx Core だけを使う方法については、こちらのガイドをご覧ください。

技術とパフォーマンス

Turborepo は、ほとんどが Golang で書かれています。Nx はほとんど TypeScript で書かれていますが、Nx の重い計算のほとんどは Node.js のコア機能と C++ で書かれた node module によって行われているので、性能には影響がありません。

ベンチマークは、何を実行しようとしているか、どのような環境で実行するかなどに大きく依存するため、難しいものです。これは、Nx のパフォーマンスを測定するときに使うベンチマークの1つです: Nx and Turbo benchmark。これは5つの Next.js アプリが入ったレポです。Nx と Turbo が、キャッシュから何を復元する必要があるのかを、どれだけ早く把握し、どれだけ早く復元できるかを測定しています。

これがその結果です。

nx and turbo benchmark

最新の MBP では、Nx は9.4倍高速化されています。Windows のノートパソコンでは Nx が19.5倍速いです。

なぜ速いか?Nx はキャッシュからファイルを復元する際に tree diffing を行っているという点で、多くの点で React と似ています。正しいファイルが正しい場所にあれば、Nx はそれに触れません。Turbo は毎回すべてを吹き飛ばします。Nx では単に速いだけでなく、より便利です (これも React の tree diffing と同様です)。復元するたびにすべてを吹き飛ばすということは、何らかのツールがフォルダを監視している場合 (大規模なアプリを構築したり、マイクロフロントエンドを構築する場合によくあります)、それらが予期しない混乱をもたらしたり、意味もなくトリガーされたりすることを意味します。これは、DOM をゼロから再作成すると、単に遅いだけでなく、より悪い UX になるのと似ています。さらに、tree diffing を無効にして、Nx に Turbo と同じことをさせたとしても、1.7倍は速いです。

Turborepo が提供するキャッシュ復元は、多くのレポにとって十分な速度かもしれません (3秒でも十分速いです)。大規模なモノレポにとって重要なのは、1台のマシンで実行したときの開発者の人間工学を維持したまま、50台のマシンにコマンドを分散させることができる能力です。Nx はそれが可能です。Bazel はそれが可能です (Nx はいくつかのアイデアを拝借しています)。Turbo にはそれが不可能です。

Turbo の Go 実装の優位性は、Nx コマンドを実行するたびに、Node.js を起動するために〜70msのペナルティを支払うということです。1つのプロジェクトをテストする場合、70msのペナルティを支払うことになりますが、1000のプロジェクトをテストする場合でも、70msのペナルティとなります。他のほとんどのCLI(例えば、yarn)でも同じペナルティがあるので、実際には重要ではないと考えています。Nx と Turborepo の本当の性能向上は、タスクの削減、キャッシュ、そして Nx の場合は分散コンピュテーションをいかに賢く行うかにあります。

Nx と Turborepo は、ワークスペースの構築方法について、しばしば異なる哲学を有しています。Turborepo は、"パッケージ"という観点で考える傾向がありますが、Nx は、多くの軽量なプロジェクトに焦点を当てます。大規模な Nx Workspaces は、数百から数千のプロジェクトで構成される傾向があり、これは、3つの方法で平均ビルドパフォーマンスを向上させます。

  • 平均的に、グラフの小さい部分のみが影響を受ける
  • 多くの部分的なキャッシュヒットが存在する
  • 異なるエージェント間でタスクを分散実行する方法において、より柔軟性がある

私達が TypeScript にこだわっているのは、私達が拡張性に対して常にフォーカスしているためです。VSCode の台頭が私たちに教えてくれたことは、 JavaScript/TypeScript で書かれたものは拡張しやすいということです。また、5年間 Fortune 500 の企業と仕事をしたことで、拡張性が重要であるということをはっきりと認識しました

Nx Cloud のバックエンドは Kotlin で書かれていることも特筆すべき点です。これは、私たちの API の唯一の貢献者が Nrwl で働いており、私たちがこの仕事に最適なテクノロジーだと判断したためです。

コミュニティ

Nx は5年前にリリースされました。Turborepo は2021年12月にオープンソース化されたばかりです。Turborepo はまだ大きなコミュニティを持っていませんが、おそらくいつかはそうなるでしょう。

Nxは初日から常にMITライセンスのオープンソースプロジェクトであり、Nxを使う企業がベンダーロックインに陥らないよう、あらゆる手段を講じています。私たちは Nx をオープンソース プロジェクトとして、Nx Cloud を SaaS 製品として明確に分離しました。例えば、Nx Cloud は Nx が提供するパブリック API を使って構築されています (自分で構築することもできますし、そうしている企業もあります)。また、Nx Cloud のドキュメントは別ドメインにあります。

Discussion

ログインするとコメントできます