ビルドシステムを選ぶ理由

このページでは、ビルドシステムとは何か、ビルドシステムが何を行うのか、ビルドシステムを使用する理由、組織が拡大し始めたときにコンパイラとビルドスクリプトが最適な選択肢ではない理由について説明します。ビルドシステムの使用経験が少ないデベロッパーを対象としています。

ビルドシステムとは

基本的に、すべてのビルドシステムには明確な目的があります。エンジニアが記述したソースコードを、マシンが読み取れる実行可能バイナリに変換することです。ビルドシステムは、人間が作成したコードだけでなく、テストや本番環境へのリリースなど、マシンがビルドを自動的に作成できるようにします。数千人のエンジニアが在籍する組織では、ほとんどのビルドはエンジニアが直接トリガーするのではなく、自動的にトリガーされます。

コンパイラだけではだめですか?

ビルドシステムの必要性は、すぐに明らかにならないかもしれません。ほとんどのエンジニアは、コーディングを学習する際にビルドシステムを使用しません。ほとんどのエンジニアは、コマンドラインから gccjavac などのツールを直接呼び出すか、統合開発環境(IDE)で同等の操作を行います。すべてのソースコードが同じディレクトリにある限り、次のようなコマンドで問題ありません。

javac *.java

これにより、Java コンパイラは現在のディレクトリにあるすべての Java ソースファイルをバイナリ クラスファイルに変換します。最も簡単なケースでは、これで十分です。

ただし、コードが拡張されるとすぐに複雑になります。javac は、現在のディレクトリのサブディレクトリを検索して、インポートするコードを見つけることができます。ただし、ファイル システムの他の部分(複数のプロジェクトで共有されるライブラリなど)に保存されているコードを見つけることはできません。 また、Java コードのビルド方法しかわかりません。大規模なシステムでは、さまざまなプログラミング言語で記述されたさまざまな部分が、それらの部分間の依存関係の網の目状に絡み合っていることが多く、単一言語のコンパイラではシステム全体をビルドできません。

複数の言語または複数のコンパイル単位のコードを扱う場合、コードのビルドは 1 ステップのプロセスではなくなります。コードが依存しているものを評価し、適切な順序でビルドする必要があります。このとき、各部分に異なるツールセットを使用する場合があります。依存関係が変更された場合は、古いバイナリに依存しないように、このプロセスを繰り返す必要があります。コードベースが中程度のサイズでも、このプロセスはすぐに面倒でエラーが発生しやすくなります。

コンパイラは、Java のサードパーティ JAR ファイルなどの外部依存関係の処理方法も認識しません。ビルドシステムがない場合は、インターネットから依存関係をダウンロードし、ハードドライブの lib フォルダに配置して、そのディレクトリからライブラリを読み取るようにコンパイラを構成することで、これを管理できます。時間の経過とともに、これらの外部依存関係の更新、バージョン、ソースを維持することが難しくなります。

シェル スクリプトはどうですか?

趣味のプロジェクトが、コンパイラだけでビルドできるほど単純なものから始まったとします。しかし、前述の問題が発生し始めました。ビルドシステムは必要ないと考えて、正しい順序でビルドを行う簡単なシェル スクリプトを使用して、面倒な部分を自動化できます。しばらくはこれで問題ありませんが、すぐにさらに多くの問題が発生します。

  • 面倒になります。システムが複雑になるにつれて、実際のコードと同じくらいの時間をビルドスクリプトの作業に費やすようになります。シェル スクリプトのデバッグは面倒で、ハックが次々と重ねられていきます。

  • 遅くなります。古いライブラリに誤って依存していないことを確認するために、ビルドスクリプトを実行するたびに、すべての依存関係を順番にビルドします。再ビルドする必要がある部分を検出するロジックを追加することを考えますが、スクリプトでは非常に複雑でエラーが発生しやすくなります。または、再ビルドする必要がある部分を毎回指定することを考えますが、それでは元に戻ってしまいます。

  • リリースするタイミングが来ました。最終ビルドを行うために jar コマンドに渡す必要がある引数をすべて把握する必要があります。また、アップロードして中央リポジトリに push する方法も覚えておく必要があります。ドキュメントの更新をビルドして push し、ユーザーに通知を送信します。別のスクリプトが必要になるかもしれません。

  • 大変です。ハードドライブがクラッシュし、システム全体を再作成する必要があります。すべてのソースファイルをバージョン管理に保存するほど賢明でしたが、ダウンロードしたライブラリはどうでしょうか?すべてを見つけて、最初にダウンロードしたときと同じバージョンであることを確認できますか?スクリプトは、特定の場所に特定のツールがインストールされていることに依存している可能性があります。スクリプトが再び動作するように、同じ環境を復元できますか?コンパイラを適切に動作させるために設定して、忘れてしまった環境変数はどうでしょうか?

  • 問題はありますが、プロジェクトは十分に成功しているため、エンジニアの採用を開始できます。以前の問題が発生するのに災害は必要ありません。新しいデベロッパーがチームに参加するたびに、同じ面倒なブートストラップ プロセスを行う必要があります。最善を尽くしても、各ユーザーのシステムには小さな違いがあります。多くの場合、あるユーザーのマシンで動作するものが別のユーザーのマシンでは動作せず、違いを見つけるためにデバッグツールのパスやライブラリのバージョンを数時間かけて確認する必要があります。

  • ビルドシステムを自動化する必要があると判断します。理論的には、新しいパソコンを入手し、cron を使用して毎晩ビルドスクリプトを実行するように設定するのと似ています。面倒な設定プロセスを行う必要がありますが、人間の脳が小さな問題を検出して解決できるというメリットはありません。毎朝出社すると、昨夜のビルドが失敗したことがわかります。これは、昨日デベロッパーが自分のシステムでは動作するが自動ビルドシステムでは動作しない変更を行ったためです。毎回簡単な修正ですが、頻繁に発生するため、毎日多くの時間を費やしてこれらの簡単な修正を見つけて適用することになります。

  • プロジェクトが拡大するにつれて、ビルドが遅くなります。ビルドの完了を待っている間に、休暇中の同僚のアイドル状態のデスクトップを悲しげに見つめ、無駄になったコンピューティング能力を活用する方法を考えます。

スケールの古典的な問題に直面しました。1 人のデベロッパーが最大 1 週間か 2 週間で最大数百行のコードを記述する場合(大学を卒業したばかりのジュニア デベロッパーのこれまでの経験かもしれません)、コンパイラだけで十分です。スクリプトを使用すると、もう少し進むことができます。しかし、複数のデベロッパーとそのマシン間で調整する必要が生じると、完璧なビルドスクリプトでも十分ではありません。マシンの小さな違いを考慮することが非常に難しくなるためです。この時点で、このシンプルなアプローチは機能しなくなり、実際のビルドシステムに投資する時期が来ています。