コードに必要なのは、人間の目とロボットの目、その両方

新しい機能を書き終えたところだ。ロジックはしっかりしている。エッジケースも処理した。マージする準備はできている。しかし、ここで一つ問題がある。コードを書いているとき、あなたは「意図した動作」を見ているのであって、「実際に起こること」を見ているわけではない。タイポはすり抜ける。null値の処理を忘れる。自分の変更が、1時間前に同僚がプッシュした何かを静かに壊してしまう。

これは信頼の問題ではない。人間の性質の問題だ。どんな開発者にも、一人で作業しているときには盲点がある。解決策は、より多くのシニアエンジニアを雇ったり、もっとコメントを書いたりすることではない。ラップトップから共有コードベースへコードが移動する過程に、チェックプロセスを組み込むことだ。

人間によるチェック:コードレビュー

最も自然なチェック方法は、他の誰かにあなたのコードを読んでもらうことだ。これがコードレビューである。1~2人の同僚があなたの変更を見て、質問をする。「このロジックは筋が通っているか?」「チームで合意したパターンに従っているか?」「気づいていない副作用はないか?」

レビューはまた、学びの場でもある。ジュニア開発者は、よりクリーンなアプローチについてフィードバックを得る。シニア開発者は、誰かが単純な問題を複雑にしすぎていることに気づく。レビューで交わされる会話は、どんな自動化ツールよりも、将来のバグを防ぐのに役立つことが多い。

しかし、レビューにも限界がある。人間は疲れる。金曜日の午後5時に、500行の差分を注意深く読む人はいない。そして、すべての分岐が正しく動作するかを頭の中でコードを実行して検証できる人間もいない。ここで自動化の出番となる。

ロボットによるチェック:継続的インテグレーション

コードが変更されるたびに実行される自動チェックには、名前がある。継続的インテグレーション(CI)だ。コードをプッシュすると、誰が指示するでもなくCIが起動する。プロジェクトをビルドし、テストを実行し、セキュリティの脆弱性をチェックし、コードフォーマットのルールを強制する。

CIは、人間が苦手とする退屈で反復的な作業を処理する。誰かが誤ってセミコロンを削除したか?CIがキャッチする。依存関係の更新によってテストが壊れたか?CIが報告する。既知の脆弱性のあるライブラリが持ち込まれたか?CIがフラグを立てる。

ここで重要な点がある。CIはコードレビューの代わりにはならない。それぞれ目的が異なる。

CIが行うこと レビューが行うこと
ルールと一貫性をチェックする 設計とアプローチを評価する
テストを自動実行する 論理的なギャップを見つける
標準を強制する チーム全体で知識を共有する
決して疲れない 自動化が見逃すものをキャッチする

CIは何も壊れていないことを確認する。レビューはコードが適切に設計されていることを確認する。両方が必要だ。

両者が出会う場所:プルリクエスト

プルリクエストは、人間によるチェックと自動チェックが融合する場だ。別のブランチを作成し、コードを書き、メインブランチにマージするリクエストを開く。プルリクエストは、誰もが以下のことを確認できる単一の場所となる。

以下は、プルリクエストプロセスが自動チェックと人間によるチェックをどのように組み合わせるかを示すフローチャートです。

flowchart TD A[開発者がコードをプッシュ] --> B[CIがテストを実行] B --> C{CIは成功したか?} C -->|いいえ| D[コードを修正して再プッシュ] D --> B C -->|はい| E[コードレビューを依頼] E --> F{レビューは承認されたか?} F -->|いいえ| G[フィードバックに対応してプッシュ] G --> B F -->|はい| H[メインブランチにマージ]
  • どのファイルが変更されたか
  • CIがその変更について何を言っているか
  • レビュアーが何をコメントしたか

CIがパスし、少なくとも1人のレビュアーが承認するまで、誰もマージしない。この組み合わせにより、勘に頼らない確信が得られる。コードが自動的にチェックされ、人間によってレビューされたことが分かる。マージ後も何か問題が発生したとしても、少なくともプロセスが実行されたことは分かっており、何がすり抜けたのかを突き止めることができる。

これが実際に重要な理由

このプロセスをスキップするチームは代償を払う。CIがなければ、数秒で発見できたはずのバグが、数時間後に本番環境で発見される。レビューがなければ、誰も疑問を呈さないため、悪い設計パターンが広がる。両方がなければ、コードのマージはギャンブルになる。

健全なチェックプロセスは、典型的なワークフローでは次のようになる。

  1. mainからブランチを作成する
  2. コードを書いてプッシュする
  3. ブランチ上でCIが自動実行される
  4. プルリクエストを開く
  5. CIの結果がプルリクエストに表示される
  6. チームメイトがコードをレビューする
  7. フィードバックに対応してプッシュし、CIが再実行される
  8. CIがパスし、レビュアーが承認し、マージする

これは官僚主義ではない。保険だ。各ステップが、異なる種類の問題を本番環境に到達する前にキャッチする。

すぐに使える実践的チェックリスト

コードチェックプロセスを設定または改善する場合、以下が必須項目だ。

  • 基本を自動化する: ビルド、テスト、リンター、セキュリティスキャンは、すべてのプッシュで実行する。例外は認めない。
  • 少なくとも1人のレビュアーを必須にする: 人間の承認なしではプルリクエストをマージできないようにする。
  • レビューを小さく保つ: 50~100行のレビューは徹底している。500行以上のレビューは流し読みになる。
  • CIの失敗でマージを止めさせる: 議論なしに、誰も失敗したCIチェックをオーバーライドできないようにする。
  • 自分の差分を先にレビューする: 誰かに頼む前に、自分の変更を自分で読む。明らかなミスは自分で見つけられる。

まとめ

コードレビューとCIは、オーバーヘッドではない。コードが動くことを「願う」のと、チェックされたことを「知る」のとの違いだ。レビューは人間が見えるものをキャッチする。CIは人間が見逃すものをキャッチする。両者が組み合わさることで、コードのマージを、信仰の飛躍から、測定された一歩へと変える。

マージ後も、コードはビルドされ、デプロイされ、実行される必要がある。しかし、少なくとも、そのコードが良好な状態でそこに到達したことは分かっている。