ソースコードから実際に動作するものへ

あなたはラップトップでコードを書き終えました。完璧に動作します。すべての機能が動き、エラーもありません。気分は良いです。さて、それをチームやユーザーに見せたいと思い、プロジェクトフォルダを別のコンピュータにコピーして実行しようとします。すると突然、何も動かなくなります。見たことのないエラーが発生する。あるいはアプリは起動しても、見た目がまったく違う。

同じコードなのに、結果が異なる。

これこそが、多くの開発者が重要なことに気づく瞬間です。ソースコードはどこでも実行できるものではありません。ソースコードは原材料です。プログラムの命令が書かれたテキストファイルの集まりです。サーバー、ユーザーのコンピュータ、本番環境のいずれかで使えるようにするには、そのソースコードを実行可能な形に変換する必要があります。

この変換プロセスをビルドと呼びます。

なぜソースコードをそのまま本番環境に送れないのか

生のソースコードがラップトップの外でそのまま実行できない理由はいくつかあります。

第一に、ほとんどのプログラミング言語は翻訳を必要とします。Java、Kotlin、Go、Rust、C# はバイナリファイルやバイトコードにコンパイルする必要があります。JavaScript、Python、Ruby のような言語は同じようにコンパイルする必要はありませんが、異なる環境で動作するように依存関係やパッケージを正しく配置する必要があります。

第二に、現代のアプリケーションはほぼ常に外部ライブラリやフレームワークに依存しています。これらのライブラリはダウンロードされ、適切な場所に配置され、環境ごとに設定される必要があります。コードだけでは役に立ちません。

第三に、アプリケーションは設定ファイル、静的アセット、テンプレート、画像などのリソースを必要とすることがよくあります。これらはコードと一緒に一貫した方法でパッケージ化する必要があります。

コードの翻訳、依存関係のダウンロード、ファイルの整理、実行可能な出力の生成 — これらすべての作業をビルドと呼びます。ビルドの結果をアーティファクトと呼びます。

アーティファクトの具体例

アーティファクトは、使用するテクノロジースタックによってさまざまな形式をとります。

Java アプリケーションの場合、アーティファクトは通常 JAR または WAR ファイルです。Go アプリケーションの場合は単一のバイナリファイルです。Web フロントエンドの場合、アーティファクトは minify された HTML、CSS、JavaScript ファイルのフォルダかもしれません。Python アプリケーションの場合は、すべての依存関係をバンドルしたパッケージになります。

特定の形式よりも、アーティファクトが何を表すかが重要です。それは、実行可能な単一の自己完結型パッケージです。このアーティファクトがサーバーに送られ、ステージング環境に配置され、本番環境にデプロイされます。ソースコード自体が直接サーバーに送られることは決してありません。常にアーティファクトが送られます。

例えば、Dockerfile はソースコードをコンテナイメージ(一般的なアーティファクト形式)に変換するビルド手順を定義します。

FROM node:18-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

料理に例えてみましょう。ソースコードは材料(小麦粉、卵、砂糖、バター)です。ビルドプロセスは、混ぜて、焼いて、盛り付ける作業です。アーティファクトは完成したケーキです。レストランのダイニングテーブルに生の材料を送ることはありません。ケーキを送ります。

複数ビルドの問題

ビルドがアーティファクトを生成することを理解すると、新たな疑問が生まれます。アプリケーションは何度もビルドすることになります。コード変更、バグ修正、新機能のたびに新しいビルドが発生します。あるアーティファクトと別のアーティファクトをどうやって区別するのでしょうか?

区別する方法がなければ、どのアーティファクトがテスト済みで、どれが本番環境で動作していて、どれが新しいものかを知ることはできません。これは問題が発生したときに重要になります。本番環境が壊れた場合、そこにデプロイされているアーティファクトが正確にどれで、どのバージョンのコードから生成されたかを知る必要があります。

ここでアーティファクトの識別が不可欠になります。すべてのビルド出力には一意の ID が必要です。この ID により、任意のアーティファクトを、それを生成した正確なソースコード、ビルド設定、環境にトレースバックできます。

ビルド中に何が起こるか

典型的なビルドプロセスはいくつかのステップを含みますが、正確な順序は使用するテクノロジーによって異なります。

次のフローチャートは、ソースコードをデプロイ可能なアーティファクトに変換する典型的なステップの流れを示しています。

flowchart TD A[ソースコード] --> B[バージョン管理から取得] B --> C[依存関係の解決] C --> D[コンパイル/トランスパイル] D --> E[リソースのアセンブル] E --> F[アーティファクトのパッケージ化] F --> G[アーティファクト: JAR, ZIP, バイナリなど] G --> H[デプロイ準備完了] I[一貫したビルド環境] -.-> B I -.-> C I -.-> D I -.-> E I -.-> F

最初のステップは通常、バージョン管理システムから最新のソースコードを取得することです。これにより、リポジトリにあるコードを正確にビルドしていることが保証され、コミットし忘れたローカルの変更が混入するのを防ぎます。

次に依存関係の解決です。ビルドツールが必要なライブラリとフレームワークをすべてダウンロードします。このステップは初回は遅くなる可能性がありますが、以降のビルドでは依存関係をキャッシュして時間を節約することがよくあります。

その後、実際のコンパイルまたは変換が行われます。コードがコンパイルされ、アセットが minify され、テンプレートが処理されます。ビルドツールはビルド設定で定義された変換を適用します。

その次にリソースがアセンブルされます。設定ファイル、静的ファイル、その他のアセットが収集され、出力構造内の正しい場所に配置されます。

最後に、ビルドツールはすべてをアプリケーションに適したアーティファクト形式にパッケージ化します。これは圧縮アーカイブ、バイナリファイル、またはコンテナイメージの場合があります。

ビルド環境の重要性

多くのチームが陥る重要なポイントがあります。ビルドは一貫した環境で行うべきです。ラップトップでビルドすると、同僚のマシンやビルドサーバーでのビルドと結果が異なる可能性があります。異なる OS、異なるツールバージョン、異なるインストール済みライブラリ — これらすべてが同じソースコードから異なるアーティファクトを生成する原因になります。

このため、プロフェッショナルなチームは専用のビルドサーバーやビルドコンテナを使用します。ビルド環境は標準化され、制御されています。すべてのビルドが同じ条件で実行され、再現可能なアーティファクトを生成します。どんなに注意深くセットアップしても、あなたのラップトップはビルドサーバーではありません。

デプロイされるのはアーティファクト

明白に聞こえるけれども頻繁に破られる原則があります。ステージングでテストしたアーティファクトは、本番環境にデプロイするアーティファクトとまったく同じであるべきです。同じコードから再ビルドしたものではありません。少し異なる設定でもありません。まったく同じファイルです。

本番環境用に再ビルドすると、リスクが生じます。ビルドサーバーの状態が異なっていたかもしれません。ビルド間で依存関係のバージョンが変わったかもしれません。ビルドツールの動作が異なっていたかもしれません。テストしたものが本番環境で実行されることを確実にする唯一の方法は、同一のアーティファクトを使用することです。

つまり、ビルドプロセスはコードのバージョンごとに1つのアーティファクトを生成する必要があります。そのアーティファクトは、再ビルドされることなく、開発、テスト、ステージング、本番と環境をプロモーションされます。アーティファクトはその ID を保持し、どこから来たのかを常にトレースできます。

ビルドプロセスの実践的チェックリスト

  • アプリケーションが生成するアーティファクト形式(JAR、バイナリ、コンテナイメージなど)を定義する
  • すべてのビルドで一貫した専用のビルド環境をセットアップする
  • ビルドツールを設定して、ビルドごとに一意のバージョン識別子を生成する
  • すべてのアーティファクトを後で取得できる中央リポジトリに保存する
  • 異なる環境のために同じコードを再ビルドしない — 同じアーティファクトをプロモーションする
  • ビルドプロセスの手順を文書化し、チームの誰もが理解できるようにする

日々の業務への影響

次にラップトップでアプリケーションを実行して動いたときは、これが始まりに過ぎないことを思い出してください。そのコードは、他のどこかで実行される前にビルドプロセスを経る必要があります。ビルドは生のソースコードをアーティファクト(適切に設定された環境ならどこでも実行可能なパッケージ)に変換します。

ソースコードとアーティファクトのこの区別を理解すると、デリバリーに対する考え方が変わります。ソースコードはあなたが書くものです。アーティファクトはあなたがデプロイするものです。これらは同じものではなく、互換性があるかのように扱うと、「自分のマシンでは動くのに」という問題が発生し、デバッグに何時間も費やすことになります。

アーティファクトを一貫してビルドし、明確に識別し、再ビルドせずに環境間でプロモーションしてください。このシンプルな規律により、デプロイメントの問題のカテゴリ全体を本番環境に到達する前に排除できます。