Part 2 開発の基本動作
第5章 手元で動かす作業場
第4章では、相手が次の判断をできるように、観察、推測、依頼を分けて書くことを学んだ。 第5章では、その観察の材料を手元で集める。
開発を始めたばかりの読者にとって、ターミナル、パス、ポート、ログ、ランタイムという言葉は、ばらばらの専門用語に見えやすい。 しかし実務で大切なのは、これらの名前を暗記することではない。 自分がどこで、何を実行し、何が起き、どう止め、何を相談材料として残したかを説明できることである。
手元で動くとは、一度だけ画面が表示されたという意味ではない。 同じ手順をもう一度たどれること、失敗したときにどこまで進んだかを説明できること、他の人が同じ状況を再現できることまで含む。
この章では、ローカルの作業場を整え、読むだけの操作から始め、開発サーバーを起動し、ログを読み、止めるところまでを扱う。 ここで作る記録は、第6章のGitとPull Requestで、変更説明や確認方法を書く材料になる。
この章でできるようになること
この章のゴールは、ターミナルのコマンドを暗記することではない。 自分の手元の環境を、あとから再現できる作業場として説明できるようになることである。
この章を読み終えたら、次のことができる状態を目指す。
pwd、ls、cdを使い、現在地とプロジェクトルートを確認できる。- README、
package.json、lockファイル、src/、test/、.env.exampleの役割をおおまかに説明できる。 - Node.js、npm、Git、エディタの場所とバージョンを確認し、environment-check.md に残せる。
- READMEの手順を根拠に、サンプルアプリを起動し、URL、ポート、ログ、停止方法を local-run-log.md に残せる。
- エラーを、場所、道具、設定、実行中の状態に分けて、次に確認することを書ける。
- AIにログを渡す前に、secret、個人情報、本番データが含まれていないか確認できる。
この章では、starter-apps/learning-log-sample を題材にする。
このサンプルは、READMEと package.json を見ると、外部npmパッケージに依存せず、Node.js 18.18 以上で動くことが分かる。
実行手順や必要なバージョンは記憶ではなく、プロジェクト内のファイルを根拠に確認する。
作業場は、現在地から決まる
最初に確認するのは、どのアプリを動かすかではなく、いま自分がどこにいるかである。
ターミナルで実行する多くのコマンドは、現在のディレクトリを基準にして動く。
同じ npm install でも、実行する場所が違えば、見る package.json も変わる。
プロジェクトルートとは、プロジェクト全体を見渡す基準のディレクトリである。 多くの場合、README、依存関係を示すファイル、ソースコードの入口、テスト、設定ファイルがここから見える。 ルートという言葉は根を意味する。 作業の根元を間違えると、その後のコマンドは正しくても、対象を間違える。
現在地を確認する代表的なコマンドは pwd である。
pwd は print working directory の略で、いま作業しているディレクトリを表示する。
ls は一覧を見るコマンドで、cd は作業する場所を移動するコマンドである。
この段階で全部を覚えなくてよい。
まず、現在地を見る、一覧を見る、移動する、という三つの動きを区別できればよい。
パスは、ファイルやディレクトリの住所である。
絶対パスは、PCのいちばん上の階層からたどる住所である。
相対パスは、いまいる場所から見た住所である。
たとえば src/server.js という相対パスは、プロジェクトルートにいるときは期待したファイルを指すかもしれない。
しかし別のディレクトリにいると、同じ文字列でも別の場所を指すか、何も見つからない。
初心者がつまずく理由は、コマンドが難しいからだけではない。
現在地と対象が曖昧なままコマンドを実行してしまうからである。
作業の最初に pwd とプロジェクトルートを確認する習慣は、地味だが強い。
まず壊さずに読む
プロジェクトに入ったら、すぐにコードを書き始めない。 まず読む。 読む順番を持つと、初めて見るプロジェクトでも慌てにくくなる。
最初に見るのはREADMEである。 READMEは、プロジェクトの入口である。 起動コマンドだけを探すのではなく、前提となるランタイム、依存関係の入れ方、環境変数、テスト方法、既知の制約を読む。 READMEに書かれている前提を飛ばすと、失敗したときに戻る場所がなくなる。
次に、依存関係を示すファイルを見る。
Node.jsのプロジェクトなら package.json が代表例である。
ランタイムは、コードを動かすための土台である。
依存関係は、アプリが使う外部のライブラリやパッケージである。
Node.jsのバージョン、npmのバージョン、依存パッケージのバージョンが違うと、同じコードでも動き方が変わることがある。
package.json では、特に次を見る。
scripts:npm run devやnpm testが何を実行するか。engines: 必要なNode.jsなどのバージョン条件。dependenciesとdevDependencies: 実行や開発に必要な外部パッケージ。private: 公開パッケージとして配布するものではないか。
この章で使う learning-log-sample では、scripts.dev は node src/server.js、scripts.test は node --test である。
engines.node は >=18.18 なので、Node.js 18.18以上ならREADMEの前提を満たす。
外部npmパッケージには依存していないため、READMEのクイックスタートでは npm install ではなく npm run dev から始められる。
ただし、実務ではプロジェクトによって依存関係があるため、必ずそのプロジェクトのREADMEと package.json を優先する。
その後で、ソースコード、テスト、設定ファイルを見る。
研修用のサンプルアプリなら、src/、test/、.env.example のような名前が手がかりになる。
ここでも、すべてを理解する必要はない。
どのファイルが入口らしいか、どのファイルがデータらしいか、どのファイルがテストらしいかを、自分の言葉で仮置きすることが重要である。
learning-log-sample なら、最初は次のように仮置きできる。
| パス | 最初に見える役割 |
|---|---|
README.md |
起動手順、API例、章ごとの使い方 |
package.json |
npm scripts、Node.jsのバージョン条件 |
src/server.js |
ローカルサーバーとAPIの入口 |
src/store.js |
サンプルデータと更新処理 |
src/public/ |
ブラウザに表示する画面側のファイル |
test/ |
Node.js組み込みテスト |
.env.example |
必要になり得る環境変数名の例 |
schema.sql |
後のDB章で使うスキーマ例 |
Dockerfile、compose.yaml |
後のコンテナ章で使う設定 |
rg --files のような検索系コマンドは、プロジェクトにあるファイルを一覧する助けになる。
rg が使えない場合は、エディタのファイルツリーや検索機能でもよい。
目的は特定のコマンドを使うことではなく、プロジェクトの形を壊さずに観察することである。
CLIは、操作を文字として残す入口である
CLIは Command Line Interface の略で、文字でコマンドを入力して操作する入口である。 画面上のボタンやメニューで操作するGUIと違い、CLIの操作は文字として残しやすい。 そのため、同じ手順を共有したり、失敗した操作を相談したりしやすい。
CLIを難しく見せているのは、短い単語と記号が多いことである。
しかし、コマンドは部品に分けて読める。
たとえば ls -la なら、ls が実行する道具の名前で、-la は表示方法を変える指定である。
node --version なら、node という道具に、バージョンを表示してほしいと依頼している。
最初に慣れるべきなのは、読み取り系のコマンドである。
pwd で現在地を見る。
ls で一覧を見る。
which node や which npm で、どこにある道具を呼び出しているかを見る。
node --version や npm --version で、いま使っている版を見る。
たとえば starter-apps/learning-log-sample を動かす前なら、次のような確認から始められる。
出力の値は環境によって違ってよい。
大切なのは、作業場所、使っている道具、バージョンを記録できることである。
$ pwd
<project-root>/starter-apps/learning-log-sample
$ ls
Dockerfile README.md compose.yaml package-lock.json package.json schema.sql src test
$ node --version
v18.18.0
$ npm --version
10.x.x
この出力から、少なくとも package.json がある場所で作業していること、Node.jsとnpmを呼び出せていることが分かる。
package.json の engines.node が >=18.18 なら、node --version の結果がその条件を満たすかを見る。
もし package.json が一覧にないなら、起動コマンドを打つ前に現在地を直す。
読み取り系の操作は、状態を壊しにくい。 分からないときほど、削除、初期化、強制終了のような操作へ急がない。 まず現在地、対象、道具の有無、バージョンを観察する。
出力は、成功と失敗の手がかりである
コマンドを実行すると、何らかの出力が返る。 標準出力は、通常の結果を表示するための流れである。 標準エラーは、エラーや警告を表示するための流れである。 終了コードは、コマンドが成功したか失敗したかを数字で表す値である。
最初からOSの仕組みを細かく理解する必要はない。 大切なのは、出力を一つの大きな文字のかたまりとして見ないことである。 普通の結果、警告、エラー、終了したかどうかを分けて見る。
エラーを読むときは、最後の一行だけを見て判断しない。 ログの最後には、結果だけが出ていることがある。 本当に見るべきなのは、最初に失敗した場所、対象ファイル、実行したコマンド、関係する設定である。 長いログを全部理解できなくてもよい。 実行した場所、実行したコマンド、期待結果、実際の結果、主要なエラー名を残すだけで、相談の質は大きく上がる。
ログを記録するときは、次を分ける。
- 自分が実行したこと: ディレクトリとコマンド。
- ツールが返したこと: URL、ポート、エラー名、警告。
- 自分が確認したこと: ブラウザで開いた、テストを実行した、止めた。
- まだ確認していないこと: 別のポート、環境変数、他のターミナルのプロセスなど。
「たぶん動いています」ではなく、「http://localhost:3000 を開き、画面が表示された」のように、見た結果を書く。
逆に、実行していない npm test を確認済みとして書かない。
ローカル開発サーバーを起動し、止める
ローカルとは、自分の手元のPC上を指す。 ローカル開発サーバーを起動するとは、自分のPCの中でアプリを確認できる状態にすることである。
開発サーバーを起動すると、ターミナルにURLが表示されることがある。
localhost は、このPC自身という意味である。
ポートは、同じPCの中で複数の通信先を区別する番号である。
たとえば http://localhost:3000 は、このPC自身の3000番ポートで待ち受けているサーバーへアクセスする、という意味になる。
起動したサーバーは、多くの場合、動き続けるプロセスである。 プロセスとは、PC上で実行中の仕事である。 起動後にターミナルが戻ってこないのは、失敗して固まったからとは限らない。 サーバーが動き続け、ブラウザからのアクセスを待っている状態かもしれない。
使い終わったら止める。
よくある停止方法は Ctrl+C である。
止め方を知らないまま次の起動を重ねると、前のサーバーが同じポートを使い続け、address already in use のようなエラーにつながる。
起動できることだけでなく、アクセスできること、ログが出ること、止められることまで確認する。 これで初めて、手元の作業場を説明できる状態に近づく。
研修用サンプルアプリなら、成功時のログは次のように見える。
$ cd starter-apps/learning-log-sample
$ npm run dev
> learning-log-sample@1.0.0 dev
> node src/server.js
learning-log-sample listening on http://localhost:3000
この場合、local-run-log.md には、実行した場所、npm run dev、表示されたURL、ブラウザで開いた結果、止め方として Ctrl+C を書く。
余裕があれば、別のターミナルで次の確認もできる。
$ npm test
$ curl http://localhost:3000/api/mentor/learners
ただし、確認コマンドはREADMEに書かれているものから選ぶ。 意味が分からないコマンドを、AIや記事に出てきたからという理由だけで実行しない。
一方で、次のようなログなら、3000番ポートを別のプロセスが使っている可能性がある。
Error: listen EADDRINUSE: address already in use :::3000
この時点で、すぐに強制終了する必要はない。
すでに開発サーバーを起動していないか、別のターミナルで同じアプリが動いていないかを確認し、分からなければ状況を記録して相談する。
macOSやLinuxでは lsof -i :3000 のような確認方法が使えることもあるが、出てきたプロセスを終了してよいかは別問題である。
自分のプロセスか、チームで使っているものか分からない場合は、終了前に相談する。
エディタは、プロジェクトの地図になる
エディタは、文字を入力するだけの道具ではない。 プロジェクトルートを開くと、ファイルを探す、文字列を検索する、エラー表示を見る、変更前後の差分を見る、といった作業がしやすくなる。
初心者がまず慣れるとよいのは、検索、診断表示、差分表示である。 検索は、関係する言葉や関数名を探すために使う。 診断表示は、構文エラーや型の問題など、エディタが見つけた問題を見るために使う。 差分表示は、変更前と変更後を比べ、何を変えたかを確認するために使う。
CLIとエディタは競合する道具ではない。 CLIは、実行した手順を文字として残すのに向いている。 エディタは、プロジェクト全体の中でどこを見ているかを把握するのに向いている。 両方を行き来できると、作業の現在地を失いにくくなる。
失敗は、種類に分けると小さくなる
ローカル実行の失敗は、怖く見える。 しかし多くの失敗は、種類に分けると次に見る場所が決まる。
場所の問題は、コマンドを実行したディレクトリやパスが違うことで起きる。
たとえば Could not read package.json なら、プロジェクトルートにいない可能性を疑う。
まず pwd と ls を見る。
道具の問題は、必要なコマンドやランタイムが見つからないことで起きる。
command not found: npm なら、npmが入っていないか、PATHから見えていない可能性がある。
which npm や npm --version で確認する。
設定の問題は、ポート、環境変数、バージョン、接続先が合っていないことで起きる。
環境変数は、コードの外から実行時の設定を渡す仕組みである。
Missing required environment variable: DATABASE_URL のようなエラーなら、.env.example を見て、必要な設定名を確認する。
このとき、実際の接続文字列やsecretをAIやチャットに貼ってはいけない。
.env.example は、必要な環境変数名と用途を共有するための見本である。
実際の値を入れた .env とは分けて扱う。
公開リポジトリでは、.env.example に本物のパスワード、APIキー、個人情報を入れない。
この教材のサンプルには PORT、MENTOR_ID、DATABASE_URL の例がある。
第5章では、まず変数名と用途を確認できればよい。
本物のDB接続やsecret管理は、後のDB、コンテナ、クラウド、セキュリティの章で扱う。
実行中の状態の問題もある。
address already in use は、同じポートを別のプロセスが使っている可能性を示す。
勝手にプロセスを終了してよいか迷う場合は、現在の状況を記録して相談する。
失敗を分類する目的は、正解をすぐ当てることではない。 次に見る場所を小さくし、相談できる形にすることである。
よくあるエラーは、次のように分けて考える。
| エラー例 | 分類 | 次に見るもの |
|---|---|---|
command not found: npm |
道具の問題 | which npm、npm --version、Node.jsの導入状況 |
Could not read package.json |
場所の問題 | pwd、ls、プロジェクトルートにいるか |
EADDRINUSE |
実行中の状態 | 同じポートのサーバー、別ターミナル、停止方法 |
Missing required environment variable |
設定の問題 | .env.example、README、環境変数名 |
| 画面は開くがデータが出ない | アプリ状態、通信 | ブラウザ、Networkタブ、サーバーログ、API |
AIにログを読ませる前に安全確認をする
AIは、長いエラーログを要約したり、原因候補を整理したり、次に見る観点を提案したりする助けになる。 ただし、ログを貼る前に確認が必要である。
貼る前に見るのは、APIキー、トークン、パスワード、個人情報、内部URL、顧客情報が含まれていないかである。 秘密情報を含む可能性がある場合は、値を伏せる。 設定名は残してよいことが多いが、値は出さない。
AIの提案を実行する前にも確認する。 そのコマンドは何を対象にしているか。 ファイルを消さないか。 初期化しないか。 実行中のプロセスを強制終了しないか。 意味が分からないまま、削除、初期化、強制終了、強制pushに見える操作をしてはいけない。
AIに聞くときは、破壊的ではない確認方法を求めるとよい。 たとえば、次のように書ける。
実行した場所:
starter-apps/learning-log-sample
実行したコマンド:
npm run dev
期待結果:
http://localhost:3000 で画面が表示されること。
実際の結果:
Error: listen EADDRINUSE: address already in use :::3000 が出た。
出してほしいこと: 何が起きている可能性が高いか、次に確認すること、破壊的ではない確認方法。
この形なら、AIはログだけでなく、現在地、操作、期待結果、実際の結果を見て回答できる。 第4章で扱った相談文の型は、AIに聞くときにも役立つ。
手元の作業場を成果物にする
この章の成果物は、手元で起きたことを再現できる形にするための文書である。 完成した画面だけを提出するのではなく、そこへ至る確認結果を残す。
environment-check.md には、実行日、作業ディレクトリ、OS、Node.js、npm、Git、エディタなどの確認結果を書く。
分からない項目は空欄にしない。
分からない、確認できなかった、と書くことも重要な情報である。
project-overview.md には、プロジェクトルート、READMEに書かれた起動手順、主要ファイルとディレクトリの役割を書く。
完全な理解を装う必要はない。
まだ役割が分からないものを分からないものとして列挙すると、次の質問が作れる。
local-run-log.md には、実行した場所、実行したコマンド、期待結果、実際の結果、表示されたURL、ポート、ブラウザで確認したこと、ログ、停止方法を書く。
この文書は、第6章でPull Request本文の確認方法を書くときに使える。
debug-note.md には、対象エラー、エラー分類、起きている可能性、次に確認すること、相談する場合に伝えることを書く。
AIを使った場合は、AIに渡した情報、AIの提案、実行前に意味を確認したコマンド、採用したこと、採用しなかったことも残す。
提出物を書くときは、次のような粒度で十分である。
## 実行した場所
starter-apps/learning-log-sample
## 実行したコマンド
npm run dev
## 期待結果
http://localhost:3000 で学習ログサンプルが表示される。
## 実際の結果
ターミナルに listening on http://localhost:3000 と表示され、ブラウザで画面を確認できた。
## 停止方法
起動したターミナルで Ctrl+C を押した。
成功ログでも失敗ログでも、事実と推測を分ける。 「表示された」は事実であり、「ポート競合だと思う」は推測である。 第4章の観察、推測、依頼の分け方を、ここでも使う。
この四つの文書は、研修用の提出物であると同時に、実務の基本動作でもある。 環境を説明できる人は、問題が起きたときに、チームを巻き込みやすい。
ローカル環境で起きやすい誤解
- コマンドを覚えること自体が目的だと思う。
pwdを確認せず、違うディレクトリで依存関係のインストールや起動をする。- READMEの起動手順だけをコピーし、前提ランタイムや環境変数を読まない。
package.jsonのscriptsやenginesを見ず、何のコマンドが何を動かすか分からないまま実行する。- エラーの最後の一行だけを見て、原因を断定する。
- 開発サーバーを止めず、前のプロセスが残ったまま次の起動を試す。
.env.exampleと.envの違いを意識せず、本物の値を公開場所へ書く。- ログに含まれるsecretや個人情報を、そのままAIやチャットへ貼る。
作業場の再現性を確認すること
environment-check.md、project-overview.md、local-run-log.md、debug-note.md を作り、手元の作業場を説明する。
最初に、現在地、OS、ランタイム、npm、Git、エディタの状態を確認する。 次に、サンプルプロジェクトのREADME、依存関係、ソースコード、テスト、設定ファイルを読み、主要ファイルの役割を自分の言葉で書く。 その後、READMEに沿ってローカル開発サーバーを起動し、URL、ポート、ブラウザ表示、ログ、停止方法を記録する。 最後に、よくあるエラーを一つ選び、場所、道具、設定、実行中の状態のどれに近いかを分類し、次に確認することを書く。
提出物は、きれいな文章である必要はない。 メンターや未来の自分が、同じ手順をたどれる程度に、事実が残っていることが大切である。
手元で動かす章で持ち帰ること
第5章で身につけるべきことは、ローカル環境を再現可能な作業場として扱うことである。 現在地を確認し、プロジェクトルートを見つけ、READMEを読み、読み取り系のコマンドで状態を観察する。 開発サーバーを起動したら、localhost、ポート、プロセス、ログ、停止方法をセットで確認する。 失敗したら、場所、道具、設定、実行中の状態に分けて、次に見る場所を小さくする。
この章を終えた時点で、すべてのエラーを一人で解決できる必要はない。 実行した場所、実行したコマンド、期待結果、実際の結果、ログ、次に確認したいことを説明できればよい。 その説明が、チーム開発の入口になる。
変更をレビューにつなげる章へ
次章では、手元で行った変更をGitの履歴に残し、GitHubのPull Requestとしてレビューへ渡す。
第5章で作った project-overview.md と local-run-log.md は、PR本文の背景、変更範囲、確認方法を書く材料になる。
手元で何を見たかを説明できると、変更をチームへ渡す準備が整う。