- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
前回は、レビューが読んでいるのが差分であることを見ました。今回は同じ手順を「手元」と「きれいな機械」で走らせ、その結果を比べます。
まず結論
「壊れていない」は、何と比べたかでしか言えません。この回で比べるのは、自分の手元の機械と、リポジトリの中身だけから組み立てた「きれいな機械」です。
手元で通ったビルドは、リポジトリに入っていないファイルや設定に支えられていることがあります。同じ1コマンドを別の機械で走らせて初めて、それが分かります。
手元では `npm run build` が通ります。同じコミットを新しいコンテナに取り出し、同じコマンドを走らせるとどうなるでしょうか。ビルドは `.gitignore` に書いた `.env` を読みます。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
「手元で通った」は、何と比べた結果か
このコースの芯は「『壊れていない』は、何と比べたかでしか言えない」です。では「手元でビルドが通った」は、何を確かめたことになるでしょうか。
手元の機械には、リポジトリに入っているファイルの他に、入っていないものも置いてあります。無視したファイル、前に手で作った設定、入れたままの道具。手元の成功は、それら全部が揃った状態で通ったという結果です。そのうちどれが効いていたのかは、手元だけでは分かりません。
Fowler の CI 記事は、ビルドの自動化を扱う節でこう書きます。
I'll elaborate my earlier rule of thumb: anyone should be able to bring in a clean machine, check the sources out of the repository, issue a single command, and have a running system on their own environment.
きれいな機械を持ってきて、リポジトリから取り出し、1コマンドで動く状態になる。これは手元にしか無いものを取り除いた比較です。同じ手順が通れば、成功を支えていたのはリポジトリの中身だと言えます。
- ・手元の成功は、上の箱の全部が揃ったときの結果
- ・きれいな機械の成功は、真ん中の箱だけで足りることを示す
無視した .env が、手元でだけビルドを支えていた
小さなリポジトリで試しました。ビルドは node --env-file=.env build.js で、.env から API_URL を読みます。.gitignore には .env を書いてあります。
きれいな機械は、git clone した作業ツリーを Docker コンテナ(node:24-alpine、ネットワーク遮断、実行後に自動削除)に渡して作りました。環境は macOS 26.5.2、git 2.50.1、Node v24.21.0 です。出力は加工していません。
== git ls-files (リポジトリに入っているもの).gitignorebuild.jspackage.json== git status --short --ignored!! .env== 手元: npm run build> build> node --env-file=.env build.jsbuilt dist/config.js with API_URL = https://api.example.testexit=0== きれいな機械: git clone → docker run --rm --network none -v clean:/w node:24-alpine npm run build-- container: node v24.21.0, files:.git.gitignorebuild.jspackage.json> build> node --env-file=.env build.jsnode: .env: not foundexit=9
!! .env は、無視されているが手元にあるファイルの印です。手元では exit=0、きれいな機械では node: .env: not found と exit=9。同じコミットの同じ1コマンドが、手元でだけ通っていました。
gitignore の説明文書は目的をこう書きます。
The purpose of gitignore files is to ensure that certain files not tracked by Git remain untracked.
追跡していないファイルを、追跡しないままにする。狙いどおりに働いた結果、.env はリポジトリに入らず、取り出した先にも無かったのです。
実演で、手元では通ったビルドがきれいな機械で落ちた理由として正しいのはどれですか。
CI の定義と、きれいな機械の関係
CI は一般にこう定義されます。Fowler の2024年改訂版の冒頭です。
Continuous Integration is a software development practice where each member of a team merges their changes into a codebase together with their colleagues changes at least daily. Each of these integrations is verified by an automated build (including test) to detect integration errors as quickly as possible.
SWE at Google 23章も冒頭で定義を引いていますが、これは脚注で Fowler の記事を示す引用の引用です。文言は members of a team integrate their work frequently で、2024年版と違います。改訂前の版を引いたと見られますが、その版は確かめていません。
定義そのものに「きれいな機械」は出てきません。先ほどの引用は、ビルドの自動化を扱う節にある要件です。「Fowler は CI をきれいな機械での再実行と定義した」と書くと、出典に無いことを言わせます。この教材がつなぐのは、「統合のたびに自動ビルドで検証する」なら、その自動ビルドは誰の手元でもない機械で走る、という点です。
Fowler は同じ節でこうも書きます。
A common mistake is not to include everything in the automated build.
きれいな機械で落ちたとき、その落ち方は「自動ビルドに入っていないもの」の一覧になります。
開発で動いても、本番では undefined
別コース「ビルドとバンドル — 書いたコードと届くコードが違う理由」の FieldNote に、process.env.API_URL が本番で undefined になる例がありました。開発中は動き、本番に出すと undefined/items を叩きに行く、という例です。
仕組みは今回と違います。あちらの論点は、その行がブラウザ側で動くなら接頭辞の無い環境変数は見えないことと、ビルド時の静的置換でした。.gitignore も CI も関係しません。
それでも、比べている相手を取り違えた点は同じです。あちらの本文は「厄介なのは開発時に正しく動いてしまうところです」と書いていました。開発サーバで通ったことは、本番の出力で通ることの証拠になりません。今回も、手元で通ったことは、リポジトリだけで通ることの証拠になりませんでした。
`.gitignore` の事例と、別コースで見た「本番で undefined になる process.env.API_URL」に共通する点はどれですか。
無視した .env に頼るビルド
手元では .env が作業ツリーにあるので通ります。リポジトリには入っていないので、取り出しただけの機械では見つからずに落ちました。
場所 .env 結果手元 ある exit=0きれいな機械 ない exit=9
本番で undefined になる API_URL
開発中は動きました。その行がブラウザ側で動くなら、接頭辞の無い環境変数は見えず、本番では undefined のまま fetch します。
場所 API_URL 結果開発中 見える 動く本番の出力 undefined undefined/items
原因の仕組みがまったく違う2つの失敗に、共通する構造は何でしょうか。
「どこで確かめたか」と「どこで動いてほしかったか」を、両方で並べてみてください。
この章のまとめ
- 手元の成功は、リポジトリに無いものも含めて全部が揃った状態の結果。どれが効いたかは手元だけでは分からない。
- 実演では同じ1コマンドが手元で
exit=0、きれいな機械でnode: .env: not found/exit=9。 .gitignoreの目的は追跡していないファイルを追跡しないままにすること。すでに追跡しているファイルには効かない。- Fowler の
a clean machineはビルドの要件として書かれたもので、CI の定義そのものではない。 - SWE at Google 23章の定義はFowler からの引用で、文言は2024年改訂版と違う。
git diffの出力に出るのは追跡されたファイルの変更だけ。原因がリポジトリの外にあると差分には出ない。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
すでにコミットしてしまった `.env` を、あとから `.gitignore` に書き足しました。どうなりますか。
用語集(3語)
- CI(継続的インテグレーション)
- 変更をこまめに統合し、そのたびに自動のビルドとテストで検証する習慣。Fowler の2024年改訂版は統合の頻度を `at least daily` と書く。
- きれいな機械
- この教材での呼び方。リポジトリから取り出したものだけを持ち、手元の追跡外ファイルや設定を持たない機械。Fowler はビルドの要件として `a clean machine` と書く。
- 無視されたファイル
- `.gitignore` に一致し、追跡されていないファイル。`git status --short --ignored` では `!!` で表示される。すでに追跡されているファイルには `.gitignore` は効かない。