アイデアからコードへ:ソフトウェアデリバリーの第一歩

すべての機能は同じように始まります。誰かがアイデアを持ち出し、チームがそれを作る価値があると合意し、開発者がエディタを開いてコードを書き始めます。この時点では、コードは一人の開発者のラップトップにしか存在しません。その開発者が完全に制御できる環境で動作しています。

開発者はタイプし、テストし、微調整し、自分の画面で出力を確認します。すべてがうまくいきます。ボタンは正しい場所に表示されます。データは正しく読み込まれます。機能は期待通りに動作します。

これは進歩のように感じられます。実際、進歩です。しかし、ラップトップで動いているコードは、まだ出荷できる状態ではありません。

ローカル環境の罠

コードが開発者のマシンにしか存在しないとき、それはバブルの中で動いています。インストールされているライブラリ、オペレーティングシステムのバージョン、データベースの設定、ポート番号さえも、すべてそのラップトップ固有のものです。同じチームの別の開発者がPython 3.11を使っているのに、あなたは3.10を使っているかもしれません。彼らがPostgreSQL 15を使っているのに、あなたは14を使っているかもしれません。Nodeのバージョンも違う可能性があります。ファイルパスは間違いなく異なります。

これらの違いはローカル開発中は見えません。あなたのマシンでコードが動くので、どこでも動くと想定してしまいます。しかし、他の誰かがそれを実行しようとした瞬間に問題が表面化します。ライブラリがインストールされていない。設定が別のマシンに存在しないローカルパスを指している。依存関係のバージョンの不一致が不可解なエラーを引き起こす。

これがソフトウェアデリバリーにおける最初の本当の問題です。あるラップトップで完璧に動くコードが、他の場所では失敗する可能性があるのです。そして、コードが開発者のマシンから遠ざかれば遠ざかるほど、なぜ失敗するのかを診断するのが難しくなります。

旅立つコードを書く

コードを一人のラップトップの外に持ち出せるようにするには、開発者は規律を持って書く必要があります。ルールやプロセスのためではなく、一つのマシンに留まるコードは他の誰にとっても役に立たないからです。

最初の規律は依存関係の管理です。コードが必要とするすべてのライブラリ、フレームワーク、ツールは明示的に記録されなければなりません。手動でインストールして忘れるのではなく、他の開発者やサーバーが読んで自動的にインストールできる設定ファイルに書き留めます。コードが特定のバージョンのライブラリを必要とするなら、そのバージョンを固定します。システムツールが必要なら、そのツールを文書化します。

例えば、Node.jsプロジェクトは依存関係を次のように package.json ファイルに宣言します。

{
  "name": "my-app",
  "version": "1.0.0",
  "dependencies": {
    "express": "4.18.2",
    "pg": "8.11.3",
    "lodash": "4.17.21"
  },
  "devDependencies": {
    "jest": "29.7.0",
    "eslint": "8.56.0"
  }
}

二つ目の規律は設定の分離です。データベースのアドレス、APIキー、ファイルパス、環境固有の設定は、ソースファイルにハードコードしてはいけません。それらは環境変数か、実行時に読み込まれる設定ファイルに属します。こうすることで、同じコードが開発者のラップトップ、テストサーバー、本番環境で修正なしに実行できます。設定だけが変わります。

三つ目の規律は変更の記録を残すことです。開発者が論理的な作業単位を完了するたびに、その状態を保存します。実際には、これはコミットを行うことを意味します。コミットとは、一貫性のある単位を形成する一つ以上のファイルに対する変更のスナップショットです。保存ボタンを追加する場合、コミットにはフロントエンドの変更、バックエンドのエンドポイント、関連するテストすべてを含めるべきです。作業の半分だけ、あるいは無関係な変更を混ぜてはいけません。

コミットは履歴を作ります。後でコードが本番環境で動いていて何かが壊れたとき、その履歴が最初に調べる場所になります。誰が何を変更したのか?いつ?なぜ?コミットメッセージがそれらの質問に答えるべきです。

ローカルから共有へ

ローカルリポジトリにコミットすることは良い習慣ですが、それだけでは十分ではありません。コードはまだ一つのマシンにあります。そのラップトップが壊れたら、作業は失われます。別の開発者が変更をレビューする必要があっても、それらを見ることができません。自動化システムがコードをビルドしてテストする必要があっても、アクセスできません。

次のステップは、コードを共有リポジトリにプッシュすることです。これはチームがアクセスできる中央の場所です。コードが他の人に見えるようになる場所です。自動化ツールがそれを取得できる場所です。アイデアから動作するソフトウェアへの旅が本当に始まる場所です。

コードが共有リポジトリに到達すると、チェック、テスト、そしてサーバー上で動作するものにビルドできるようになります。しかし、その前にあと一つのステップがあります。コードはレビューされる必要があります。他の人によるか、自動化ツールによるかのどちらかです。このレビューは、元の開発者が見逃したミスをキャッチします。コードがチームの標準に従っていることを保証します。パイプラインのさらに先に進む前に、その変更が理にかなっていることを確認します。

動くコードと出荷可能なコードのギャップ

あなたのマシンで動くコードと、出荷準備ができたコードの間には違いがあります。そのギャップはスキルの問題ではありません。環境の問題です。一度もラップトップの外で実行されたことのないコードは、現実に対してテストされていません。最終的に実行される共有環境に対して検証されていません。

このギャップを埋めるには、最初はオーバーヘッドに感じられる習慣が必要です。依存関係の記録、設定の分離、クリーンなコミット、共有リポジトリへのプッシュ。それぞれのステップは小さく見えますが、それらが一緒になることで、コードを個人的なものから、チームの誰でもビルド、テスト、デプロイできるものへと変えます。

コードをプッシュする前の実用的チェックリスト

次に共有リポジトリに変更をプッシュする前に、このクイックチェックを実行してください。

  • すべての依存関係が、自動インストール可能な設定ファイルに記録されていますか?
  • 環境固有の値(データベースURL、APIキー、パス)がコードから分離されていますか?
  • コミットには一つの論理的な変更が含まれ、関連するすべてのファイルが含まれていますか?
  • コミットメッセージは何が変わったのか、なぜ変わったのかを説明していますか?
  • サーバーが使用するものと一致するクリーンな環境でコードを実行しましたか?

これら五つすべてに「はい」と答えられるなら、コードはラップトップを離れる準備ができています。

次に来るもの

共有リポジトリにプッシュされたコードは、もはやプライベートではありません。チームから見え、自動化プロセスが利用可能です。しかし、それはまだ単なるコードです。実際の環境に対してテストされていません。他の変更と統合されていません。デプロイ可能なアーティファクトにビルドされていません。

次のステップは、このコードが他のすべてと組み合わされたときに実際に動作することを検証することです。その検証こそが継続的インテグレーションの始まりです。しかしその前に、基盤がしっかりしていなければなりません。つまり、開発者のマシンを離れてどこか別の場所で実行されることを意識して書かれたコードです。その意識こそが、信頼性の高いソフトウェアデリバリーへの最初の本当の一歩なのです。