[XP]継続的インテグレーション(CI)の定義と運用事例(2024 Advent Calendar 20日目)
この記事は私をシニアエンジニアにしてくれた「真のアジャイル開発」体験記の20日目の記事です。このアドベントカレンダーは「ある機能開発チームでスクラム, XP, DevOps を一度に実践したら真のアジャイル開発ができた」という内容です。執筆者は全てプログラミングをするパンダです。
継続的インテグレーション
本記事ではXPの継続的インテグレーションと自分たちのプロジェクトでの運用を紹介します。
継続的インテグレーションの定義
ソフトウェア開発において、変更箇所のテストと統合(インテグレーション)はタイムリーに完了させることが理想的です。統合に時間がかかれば、それだけ予期せぬコストが増加してしまうからです。現代的な開発ではCI環境を整備するのが当たり前になっているため、「XPではCIをやろうと主張しているんだ」といっても目新しさはそれほどないでしょう。しかし、温故知新で原点に立ち返ることで読者の方にとって何か新しい発見があるかもしれません。
継続的インテグレーション(CI)におけるビルドとテスト結果の確認には、非同期と同期の2つのアプローチがあります。どちらの場合もコードをプッシュしたことがトリガーとなって実施される点は共通していますが、非同期の場合は結果がメールやSlackで通知され、同期の場合は結果が出るまで画面を見て待つ形になります。
Kent Beck氏は同期的な方法を好むそうです。その理由は、ペアプログラミングの相手と一緒にCIの結果を待つ間に振り返りを行えるからです。この待ち時間に「これまでどんな作業をしたのか」「もっと良い方法はなかったか」などを二人で話し合うことができます。
一方、非同期の場合は結果を待つ間に別のタスクに着手することが多いです。しかし、CIでビルドやテストが失敗した場合、元のタスクを再び思い出すためにコンテキストスイッチが発生してしまいます。コンテキストスイッチのコストの重さは侮れません。頭が切り替えられないと作業の効率が低下する可能性があります。この懸念は同期的な確認方法で回避できます。
インテグレーション(統合)を実施した後は必ずデプロイまでやり切ることが重要です。これにより、統合の成果を確実に活用する流れを作ることができます。
継続的インテグレーションの運用
GitHub Actions の登場によってCI環境の整備がより身近で簡単になりました。自分のチームではCircle CIとGitHub ActionsによってCI環境が既に整備されていたため、特筆すべきことはあまりありません。そこで、ここでは一般的な話としてCIについて触れてみます。
CIには大きく2つの側面があります。一つはコードを統合すること、もう一つは自動でビルドとテストを実行することです。
新しいコードを書いても長時間統合しなければ、そのコードが問題なく動作するかどうかが分からなくなります。同僚が同時に開発を進めていれば、コードのコンフリクトが発生するリスクも高まります。
さらに、ビルドとテストを自動化しない限り、新しく書いたコードが信頼できるかどうかを判断するのは困難です。特に、コードベースが大きくなり、関心ごとや依存関係が適切に分割されている場合、ローカルで全てのテストを実行する開発者は少ないでしょう。多くの場合、変更を加えた箇所やその影響範囲のみのテストを実行して、開発中のコードに対するフィードバックを得る程度だと思います。
しかし、自分が変更した範囲でテストが通っても、考慮漏れによって別の箇所にバグが生じる可能性があります。そのようなバグがあるコードを知らずにマージしてデプロイしてしまえば、ユーザーがそのバグに気付き、信頼を失う原因になりかねません。信頼を失えばビジネスが上手くいかず、売り上げが伸び悩みます。最悪の場合は会社が倒産し、自分の仕事が失われる可能性もあります。会社の明日を守るためには、今日のテストが重要です。
CIに馴染みがない人向けの説明する
CIに馴染みがない場合は、Gitを用いたバージョン管理を例に考えると分かりやすいです。たとえば、本番で稼働しているコードを管理するmainブランチから、新機能を追加するためのfeatureブランチを作成します。featureブランチでコードを変更し、それをリモートのリポジトリにプッシュします。
このプッシュをトリガーとしてCI環境でビルドとテストが実行されます。また、mainブランチやdevelopブランチにfeatureブランチをマージする際にもビルドとテストが実行されます。もし失敗した場合はリリースを中止してバグの原因を特定します。このように、デプロイ前に何度も自動テストを実施することで、ユーザーにバグを届けるリスクを未然に防ぐことができます。
手動テストは変更があるたびに繰り返す必要があるため、テストはできる限り自動化することが推奨されます。ただし、全てを自動化した場合、flakyテスト(実行結果が不安定なテスト)が問題になることがありますが、この話題については深掘りせずここまでにとどめます。
継続的インテグレーションの運用: CIの待ち時間に振り返りをする
ペアプログラミング中にCIの結果を待つ場面は自分も経験しますが、その時間をペア作業の振り返りに使うことはあまりしていません。CIの待ち時間を利用してSlackの新規メッセージを確認したり、スプリントバックログを見直したり、休憩時間に充てることが多いからです。
ただし、一度Kent Beck氏の提案に基づき、CIの待ち時間を振り返りに活用してみたことがあります。それはポイントが1の小さいストーリーに取り組んだ際のことです。このストーリーは実装が簡単だったため、事前に考えた方法でサッと実装を完了してコードをpushしてテストの実行完了を待っていました。
待ち時間に「思考実験ですが、もっと簡単に実現する方法はあったと思いますか」とペア相手に問いかけてみたところ、「シェルコマンドを組み合わせるのはどうでしょうか。それならアプリケーションコードを書く必要がありません。ただ、メンテナンスコストを考えるとどちらでも大差ないですが」と提案をもらいました。
自分はストーリーの完了条件を見た段階で実装方法を決めてしまっていたため、シェルコマンド案は思いつきませんでした。そこで早速ペア相手の案を取り入れてコードを書き直しました。この経験から「小さいストーリーでもすぐに思いついた方法に飛びつかず、より良い方法を検討する」重要性を学びました。この経験から、振り返りというものは小さいものでも十分威力を発揮するのだと実感しました。
Kent Beck氏がWard Cunningham氏とペアプログラミングを行いながら、CIの実行の待ち時間に振り返りを行うことでXPの数々のプラクティスを生み出したのだろうと想像すると、このアプローチがより素晴らしいものに思えます。CIの待ち時間に振り返りをすることは、誰もが賢人になれる可能性を秘めた強力なプラクティスと言えるでしょう。
次回はDevOpsの概要を紹介します。
Happy Coding 🎉