継続的インテグレーション

継続的インテグレーションとは、チームの各メンバーが、少なくとも毎日、同僚の変更と合わせてコードベースに変更をマージするソフトウェア開発プラクティスです。これらの統合のそれぞれは、統合エラーをできるだけ早く検出するために、自動化されたビルド(テストを含む)によって検証されます。チームは、このアプローチにより、デリバリー遅延のリスクが軽減され、統合の労力が軽減され、新機能による迅速な拡張のために健全なコードベースを育成するプラクティスが可能になることを発見しています。
2024年1月18日
私が大規模なソフトウェアプロジェクトを初めて目撃した時のことを鮮明に覚えています。私はイギリスの大手電子機器会社で夏季インターンシップをしていました。QAグループの一員である私のマネージャーが、あるサイトを案内してくれ、私たちは、人がキュービクルで働いている、巨大で陰鬱な、窓のない倉庫に入りました。これらのプログラマーがこのソフトウェアのコードを数年間書いており、プログラミングは完了したものの、それぞれのユニットが統合され、数ヶ月間統合されていることを聞きました。私のガイドは、統合を完了するのにどれくらいの時間がかかるのか誰も本当に知らないと言っていました。このことから、私はソフトウェアプロジェクトのよくある物語、つまり、複数の開発者の作業を統合するのは長く、予測不可能なプロセスであることを学びました。
私はこのような長期の統合に閉じ込められたチームのことを何年も聞いていませんが、だからといって統合が苦痛のないプロセスであるという意味ではありません。開発者は、新しい機能の開発に数日間取り組んでおり、共通のメインブランチから変更を定期的にフィーチャーブランチにプルしている可能性があります。彼女が変更をプッシュする準備をする直前に、彼女がやり取りしているコードを変更する大きな変更がメインに適用されます。彼女は、同僚にとっては良いことですが、彼女にとってはうまくいかない、この変更で自分の作業を統合する方法を見つけるために、機能の仕上げから変更する必要があります。うまくいけば、変更の複雑さは、ソースコードのマージであり、アプリケーションを実行したときにのみ表示される、見慣れないコードをデバッグすることを強制する、陰湿な障害ではありません。
少なくともそのシナリオでは、彼女はプルリクエストを送信する前にそれを見つけることができます。プルリクエストは、変更をレビューするのを待っている間に十分に不安になる可能性があります。レビューには時間がかかり、彼女は次の機能からコンテキストスイッチを強制されます。その期間中の困難な統合は非常に不快になる可能性があり、レビュープロセスをさらに長引かせます。また、統合テストは、プルリクエストがマージされた後にのみ実行されることが多いため、それで終わりではない可能性もあります。
時間が経つにつれて、このチームはコアコードに大きな変更を加えることがこのような問題を引き起こすことを学び、それを行わなくなります。しかし、それは定期的なリファクタリングを防ぐことで、コードベース全体で不要なものを成長させてしまいます。不要なコードベースに遭遇した人は、それがどのようにこのような状態になったのか疑問に思い、多くの場合、その答えは、不要なものを取り除くことを人々に思いとどまらせるほどの摩擦のある統合プロセスにあるのです。
しかし、これはそうである必要はありません。Thoughtworksの同僚や世界中の他の多くの人が行っているほとんどのプロジェクトでは、統合を非イベントとして扱います。個々の開発者の作業は、共有されたプロジェクト状態から数時間しか離れておらず、数分でその状態に統合できます。統合エラーは迅速に発見され、迅速に修正できます。
この対照は、高価で複雑なツールの結果ではありません。その本質は、チームの全員が、少なくとも毎日、管理されたソースコードリポジトリに対して頻繁に統合するという単純なプラクティスにあります。このプラクティスは「継続的インテグレーション」(または一部の界隈では「トランクベース開発」と呼ばれています)。
この記事では、継続的インテグレーションとは何か、そしてそれをうまく行う方法について説明します。私がそれを書いた理由は2つあります。まず、常にこの職業に新しい人が入ってきており、彼らがその憂鬱な倉庫を回避する方法を示す必要があるからです。しかし、2つ目に、継続的インテグレーションは誤解されている概念であるため、このトピックには明確さが必要です。継続的インテグレーションを行っていると言う人はたくさんいますが、ワークフローを説明すると、重要な部分が欠けていることが明らかになります。継続的インテグレーションを明確に理解することで、コミュニケーションが円滑になり、作業方法を説明するときに何を期待すべきかがわかります。また、経験を向上させるためにさらにできることがあることに人々が気づくのに役立ちます。
私はこの文章を2001年に最初に書き、2006年に更新しました。それ以来、ソフトウェア開発チームに対する通常の期待は大きく変化しました。1980年代に見た数ヶ月間の統合は遠い昔のことであり、バージョン管理やビルドスクリプトなどのテクノロジーは一般的になりました。私は、継続的インテグレーションの価値を確信する20年の経験を基に、当時の開発チームによりよく対応できるように、2023年に再びこの記事を書き直しました。
継続的インテグレーションによる機能開発
継続的インテグレーションとは何か、どのように機能するかを説明する最も簡単な方法は、小さな機能の開発でどのように機能するかを示す簡単な例を示すことです。私は現在、魔法のポーションの大手メーカーと協力しており、ポーションの効果がどれくらい続くかを計算するために製品品質システムを拡張しています。すでにシステムでサポートされているポーションは12個あり、飛行ポーションのロジックを拡張する必要があります。(それらが早すぎる時間で効果がなくなることは、顧客の維持に大きな影響を与えることを学びました。)飛行ポーションは、考慮すべきいくつかの新しい要因を導入します。そのうちの1つは、二次混合中の月の満ち欠けです。
私は、中央リポジトリからgit pull
で現在のメインラインをチェックアウトすることにより、最新の製品ソースのコピーをローカル開発環境に取得することから始めます。
ソースが自分の環境に揃ったら、製品をビルドするコマンドを実行します。このコマンドは、自分の環境が正しく設定されていることを確認し、ソースをコンパイルして実行可能な製品にし、製品を起動して、それに対して包括的なテストスイートを実行します。これは数分しかかかりませんが、新しい機能の追加を開始する方法を決定するためにコードを調べ始める間に行います。このビルドはほとんど失敗しませんが、念のため行います。失敗した場合、変更を加える前に知っておきたいからです。ビルドが失敗した状態で変更を加えると、失敗の原因は自分の変更であると勘違いしてしまいます。
次に、ワーキングコピーを取得して、月の満ち欠けに対処するために必要なことはすべて行います。これには、製品コードの変更と、自動化されたテストの一部を追加または変更することが含まれます。その間、自動化されたビルドとテストを頻繁に実行します。1時間ほどすると、月のロジックが組み込まれ、テストが更新されます。
これで、変更を中央リポジトリに統合する準備ができました。このための最初のステップは、同僚が作業中に変更をメインラインにプッシュした可能性があるため、再びプルすることです。実際、そのような変更が2つあり、それをワーキングコピーにプルします。それらの上に自分の変更を組み合わせて、再度ビルドを実行します。通常、これは余計なことのように感じられますが、今回はテストが失敗します。テストは、何がうまくいかなかったのかについて何らかの手がかりを与えてくれますが、プルしたコミットを見て何が変更されたかを確認するとより便利です。誰かが関数の調整を行い、そのロジックの一部を呼び出し元に移動したようです。彼らはメインラインコードのすべての呼び出し元を修正しましたが、私は変更で新しい呼び出しを追加しました。もちろん、彼らはまだそれを見ることができませんでした。同じ調整を行い、ビルドを再度実行すると、今回はパスします。
それを解決するのに数分かかったので、再びプルすると、また新しいコミットがあります。ただし、今回のビルドは正常に動作するため、git push
で変更を中央リポジトリにプッシュできます。
ただし、プッシュしたからといって完了したわけではありません。メインラインにプッシュすると、継続的インテグレーションサービスがコミットに気づき、変更されたコードをCIエージェントにチェックアウトして、そこでビルドします。ビルドは自分の環境では正常だったので、CIサービスで失敗することはないと思っていますが、「自分のマシンでは動作する」はプログラマーの間でよく知られたフレーズであるのには理由があります。CIサービスのビルドを失敗させるような何かが見落とされることはまれですが、まれであることは決してないのと同じではありません。
統合マシンのビルドには時間がかかりませんが、熱心な開発者は飛行時間を計算する次のステップを考え始めるには十分な長さです。しかし、私は昔ながらの人間なので、少し足を伸ばしたり、メールを読んだりする時間を楽しんでいます。すぐにCIサービスからすべて正常であるという通知が届くので、変更の次の部分のためにプロセスを再び開始します。
継続的インテグレーションの実践
上記の物語は、継続的インテグレーションが、普通のプログラマーがどのように作業するかを感じてもらうための例です。しかし、どんなことにも言えるように、日常業務でこれを行うには、いくつか整理しなければならないことがあります。そこで、これから、私たちが実践する必要がある主要な慣行について説明します。
すべてをバージョン管理されたメインラインに置く
今日では、ほとんどすべてのソフトウェアチームが、ソースコードをバージョン管理システムに保管しています。これにより、すべての開発者が、製品の現在の状態だけでなく、製品に加えられたすべての変更を簡単に見つけることができます。バージョン管理ツールを使用すると、システムの開発の任意の時点にロールバックできます。これは、システムの履歴を理解したり、Diffデバッグを使用してバグを見つけるのに非常に役立ちます。この記事を書いている時点では、主要なバージョン管理システムはgitです。
しかし、バージョン管理は一般的になっている一方で、バージョン管理を十分に活用できていないチームもあります。私のバージョン管理の完全性のテストは、最小限に構成された環境(たとえば、バニラオペレーティングシステムのみがインストールされたラップトップ)を使用して、リポジトリをクローンした後、簡単に製品をビルドして実行できる必要があるということです。これは、リポジトリが製品のソースコード、テスト、データベーススキーマ、テストデータ、構成ファイル、IDE構成、インストールスクリプト、サードパーティライブラリ、およびソフトウェアをビルドするために必要なツールを確実に返す必要があることを意味します。
オペレーティングシステムのみがロードされたラップトップを使用して、リポジトリを使用することで、製品をビルドして実行するために必要なすべてのものを取得できる必要があります。
リポジトリはこれらの要素をすべて「返す」べきだと述べたことに気付くかもしれません。これは、それらを格納することと同じではありません。コンパイラをリポジトリに格納する必要はありませんが、適切なコンパイラにアクセスできるようにする必要があります。去年の製品ソースをチェックアウトする場合、現在使用しているバージョンではなく、去年使用していたコンパイラでビルドできる必要があります。リポジトリは、変更不可能なアセットストレージへのリンクを保存することでこれを実現できます。変更不可能なアセットストレージとは、アセットがID付きで保存されると、常に正確にそのアセットが返されるという意味です。アセットストレージを信頼し、常に特定のバージョンを参照し、「最新バージョン」を決して参照しないことを条件に、ライブラリコードでもこれを行うことができます。
同様のアセットストレージスキームは、動画など、サイズが大きすぎるものにも使用できます。リポジトリのクローンを作成するということは、多くの場合、不要なものまで含めてすべてを取得することを意味します。アセットストアへの参照を使用することで、ビルドスクリプトは特定のビルドに必要なものだけをダウンロードするように選択できます。
一般に、ソース管理には何かをビルドするために必要なすべてを格納する必要がありますが、実際にビルドしたものは何も格納すべきではありません。ビルド成果物をソース管理に保持する人もいますが、それは臭い、つまり、より深い問題(通常は、ビルドを確実に再作成できない)を示す兆候だと考えています。ビルド成果物をキャッシュすることは役立ちますが、それらは常に使い捨てとして扱われるべきであり、人々が不必要に依存しないように、速やかに削除することが通常は良いでしょう。
この原則の2つ目の要素は、特定の作業のコードを簡単に見つけられるようにする必要があるということです。これの一部は、リポジトリ内とより広範なエンタープライズ内の両方で、明確な名前とURLスキームを使用することです。また、バージョン管理システム内のどのブランチを使用するかを理解するのに時間を費やす必要がないことも意味します。継続的インテグレーションは、製品の現在の状態として機能する単一の共有ブランチである明確なメインラインがあることに依存しています。これが、本番環境にデプロイされる次のバージョンです。
gitを使用するチームは、メインラインブランチの名前として「main」を使用することがほとんどですが、「trunk」または古いデフォルトの「master」も見られます。メインラインは中央リポジトリのブランチであるため、main
と呼ばれるメインラインにコミットを追加するには、まずローカルコピーのmain
にコミットしてから、そのコミットを中央サーバーにプッシュする必要があります。追跡ブランチ(origin/main
などと呼ばれる)は、ローカルマシン上のメインラインのコピーです。ただし、継続的インテグレーション環境では、毎日多くのコミットがメインラインにプッシュされるため、追跡ブランチは古くなっている可能性があります。
可能な限り、製品とその環境を定義するためにテキストファイルを使用する必要があります。これは、バージョン管理システムがテキストファイル以外のファイルを格納および追跡できるものの、通常はバージョン間の違いを簡単に確認する機能を提供しないためです。これにより、どのような変更が行われたかを理解することが非常に困難になります。将来、より多くのストレージ形式が意味のある差分を作成する機能を持つようになる可能性はありますが、現時点では、明確な差分はほぼ完全にテキスト形式用に予約されています。そこでも、理解しやすい差分を生成するテキスト形式を使用する必要があります。
ビルドを自動化する
ソースコードを実行中のシステムに変えるプロセスは、コンパイル、ファイルの移動、データベースへのスキーマのロードなど、複雑になることがよくあります。ただし、ソフトウェア開発のこの部分のほとんどのタスクと同様に、自動化できます。その結果、自動化する必要があります。奇妙なコマンドを入力したり、ダイアログボックスをクリックしたりするように人に依頼するのは時間の無駄であり、間違いを生み出す温床です。
コンピュータは、単純で反復的なタスクを実行するように設計されています。コンピュータの代わりに人間が反復的なタスクを実行するとすぐに、すべてのコンピュータが深夜に集まってあなたを笑います。
-- ニール・フォード
最新のプログラミング環境のほとんどには、ビルドを自動化するためのツールが含まれており、そのようなツールは長い間存在しています。最初にそれらに出会ったのは、最古のUnixツールの1つであるmakeでした。
ビルドの手順はすべてリポジトリに格納する必要があり、実際には、テキスト表現を使用する必要があることを意味します。これにより、それらがどのように機能するかを簡単に調べることができ、最も重要なことに、変更があった場合の差分を確認できます。したがって、継続的インテグレーションを使用するチームは、ビルドを実行したり環境を構成したりするためにUIでクリックする必要があるツールを避けます。
ビルドを自動化するために通常のプログラミング言語を使用することも可能です。実際、単純なビルドはシェルスクリプトとしてキャプチャされることがよくあります。ただし、ビルドが複雑になるにつれて、ビルド自動化を念頭に置いて設計されたツールを使用する方が適切です。これは、そのようなツールには一般的なビルドタスク用の組み込み関数があるためです。しかし、主な理由は、ビルドツールがそのロジックを編成するための特定の方法、つまり依存関係ネットワークと呼ばれる代替の計算モデルで最適に機能するためです。依存関係ネットワークは、ロジックを依存関係のグラフとして構造化されたタスクに編成します。
ごく単純な依存関係ネットワークは、「テスト」タスクが「コンパイル」タスクに依存していることを示す可能性があります。「テスト」タスクを呼び出すと、コンパイルタスクを実行する必要があるかどうかを確認し、必要に応じて最初に実行します。コンパイルタスク自体に依存関係がある場合、ネットワークは最初にそれらを呼び出す必要があるかどうかを確認し、依存関係チェーンに沿って後方に順に処理します。このような依存関係ネットワークは、タスクに時間がかかることが多く、必要ない場合には無駄になるため、ビルドスクリプトに役立ちます。最後にテストを実行してからソースファイルを変更した人がいない場合、潜在的に長いコンパイルの実行を回避できます。
タスクを実行する必要があるかどうかを判断するための最も一般的で簡単な方法は、ファイルの変更時間を確認することです。コンパイルへの入力ファイルのいずれかが、出力よりも後に変更された場合、そのタスクが呼び出された場合にコンパイルを実行する必要があることがわかります。
よくある間違いは、自動ビルドにすべてを含めないことです。ビルドには、リポジトリからデータベーススキーマを取得し、実行環境で起動することが含まれている必要があります。以前の経験則を詳しく説明します。誰でも、クリーンなマシンを持ち込み、リポジトリからソースをチェックアウトし、単一のコマンドを発行することで、自分の環境で実行中のシステムを持つことができる必要があります。
単純なプログラムではビルドに1行または2行のスクリプトファイルしか必要ないかもしれませんが、複雑なシステムには、ビルドに必要な時間を最小限に抑えるために微調整された、大規模な依存関係グラフがあることがよくあります。たとえば、このWebサイトには1000を超えるWebページがあります。私のビルドシステムは、このページのソースを変更した場合、この1ページだけをビルドする必要があることを認識しています。ただし、公開ツールチェーンのコアファイルを変更した場合は、すべてを再構築する必要があります。いずれにしても、エディターで同じコマンドを呼び出すと、ビルドシステムが実行する量を判断します。
必要なものに応じて、ビルドするものが異なる場合があります。テストコードの有無、または異なるテストセットでシステムを構築できます。一部のコンポーネントはスタンドアロンで構築できます。ビルドスクリプトでは、さまざまなケースで代替ターゲットをビルドできるようにする必要があります。
ビルドを自己テスト可能にする
従来、ビルドとは、プログラムを実行するために必要なコンパイル、リンク、およびその他の追加処理を意味していました。プログラムが実行されることがあっても、それが正しいことを行うという意味ではありません。最新の静的型付け言語は多くのバグをキャッチできますが、そのネットから漏れるバグははるかに多くなります。これは、継続的インテグレーションが要求する頻度で統合したい場合に重要な問題です。バグが製品に侵入した場合、急速に変化するコードベースでバグ修正を実行するという困難な課題に直面します。手動テストは、変更の頻度に対処するには遅すぎます。
これに直面して、バグがそもそも製品に侵入しないようにする必要があります。これを行う主な手法は、可能な限り多くのバグを洗い出すために各統合の前に実行される包括的なテストスイートです。もちろん、テストは完璧ではありませんが、多くのバグをキャッチできます。十分に役立つ程度にです。私が使用していた初期のコンピュータは、起動時に目に見えるメモリ自己テストを実行しました。これが私がこれを自己テストコードと呼ぶようになった理由です。
自己テストコードの記述は、プログラマーのワークフローに影響を与えます。プログラミングタスクは、プログラムの機能を変更することと、変更された動作を検証するためにテストスイートを拡張することの両方を組み合わせたものです。プログラマーの仕事は、新機能が動作したときだけでなく、それを証明するための自動化されたテストを作成したときにも完了します。
この記事の最初のバージョンから20年以上が経過し、プログラミング環境が、そのようなテストスイートを構築するためのツールを提供する必要性をますます受け入れるようになってきたと感じています。この動きを最も大きく後押ししたのは、ケント・ベックとエリック・ガンマが最初に作成したJUnitで、1990年代後半にJavaコミュニティに大きな影響を与えました。これは他の言語にも同様のテストフレームワークを刺激し、多くの場合Xunitフレームワークと呼ばれています。これらは、プログラマーが製品コードと連携して簡単にテストを構築できるようにする、軽量でプログラマーフレンドリーなメカニズムを重視していました。多くの場合、これらのツールには、テストが成功した場合は緑色、失敗した場合は赤色に変わるグラフィカルなプログレスバーがあり、「グリーンビルド」や「レッドバー」のようなフレーズが生まれるようになりました。
健全なテストスイートであれば、悪意のある悪戯者がテストを赤色に変えることなく、損害を与えることは決して許さないでしょう。
このようなテストスイートのテストは、テストが緑色であれば、製品に重大なバグがないと確信できることであるべきです。私は、悪戯者が、行をコメントアウトしたり、条件文を反転させたりするような、製品コードに簡単な変更を加えることができるが、テストを変更することはできないと想像するのが好きです。健全なテストスイートであれば、悪戯者がテストを赤色に変えることなく、損害を与えることは決して許さないでしょう。そして、テストの失敗は、ビルドの失敗に十分であり、99.9%のグリーンでも赤色です。
自己テストコードは継続的インテグレーションにとって非常に重要であるため、必要な前提条件です。多くの場合、継続的インテグレーションを実装する上での最大の障壁は、テストのスキル不足です。
自己テストコードと継続的インテグレーションが密接に結びついていることは驚くべきことではありません。継続的インテグレーションは、もともとエクストリームプログラミングの一部として開発され、テストは常にエクストリームプログラミングの中核的な実践でした。このテストは、テスト駆動開発(TDD)の形で行われることが多く、これは、直前に書いたテストを修正しない限り、新しいコードを書いてはならないと指示するプラクティスです。TDDは、統合前であれば、製品コードの後にテストを書くことができるため、継続的インテグレーションには必須ではありません。しかし、ほとんどの場合、TDDが自己テストコードを書くための最良の方法であると感じています。
テストはコードベースの健全性を自動的にチェックする役割を果たし、テストはコードの自動検証の重要な要素ですが、多くのプログラミング環境は追加の検証ツールを提供しています。リンターは、不適切なプログラミングプラクティスを検出し、コードがチームの推奨するフォーマットスタイルに従っていることを確認できます。脆弱性スキャナーは、セキュリティの脆弱性を見つけることができます。チームは、これらのツールを評価し、検証プロセスに含める必要があります。
もちろん、テストですべてを見つけることができると期待することはできません。よく言われるように、テストはバグがないことを証明するものではありません。しかし、完璧だけが、自己テストビルドから恩恵を受ける唯一のポイントではありません。不完全なテストでも、頻繁に実行されれば、まったく書かれない完璧なテストよりもはるかに優れています。
全員が毎日メインラインにコミットをプッシュする
コードが数時間以上統合されずに放置されることはありません。
-- ケント・ベック
統合は、主にコミュニケーションに関するものです。統合により、開発者は他の開発者に、自分が行った変更を伝えることができます。頻繁なコミュニケーションにより、人々は変更が開発されるにつれて、迅速にそれを知ることができます。
開発者がメインラインにコミットするための1つの前提条件は、コードを正しくビルドできることです。これには、当然、ビルドテストに合格することも含まれます。コミットサイクルと同様に、開発者は最初に自分の作業コピーをメインラインと一致するように更新し、メインラインとの競合を解決し、ローカルマシンでビルドします。ビルドが成功したら、メインラインにプッシュすることができます。
誰もが頻繁にメインラインにプッシュすると、開発者は2人の開発者間に競合があるかどうかをすぐに把握できます。問題を迅速に解決するための鍵は、問題を迅速に見つけることです。開発者が数時間ごとにコミットすると、競合は発生してから数時間以内に検出でき、その時点ではほとんど何も起こっていないため、解決が簡単です。数週間検出されない競合は、解決するのが非常に難しい場合があります。
コードベースの競合には、さまざまな形式があります。最も簡単に見つけて解決できるのは、テキストの競合です。これは、2人の開発者が同じコードフラグメントを異なる方法で編集した場合によく「マージ競合」と呼ばれます。2番目の開発者が更新されたメインラインを自分の作業コピーにプルすると、バージョン管理ツールはこれらを簡単に検出します。より困難な問題は、セマンティック競合です。もし私の同僚が関数の名前を変更し、私が新たに追加したコードでその関数を呼び出した場合、バージョン管理システムは私たちを助けることができません。静的に型付けされた言語では、コンパイルエラーが発生し、これは非常に簡単に検出できますが、動的言語ではそのような助けを得ることはできません。また、静的に型付けされたコンパイルでさえ、同僚が私が呼び出す関数の本体を変更し、その動作に微妙な変更を加えた場合には役に立ちません。これが、自己テストコードが非常に重要である理由です。
テストの失敗は、変更の間に競合があることを警告しますが、競合が何であるか、どのように解決するかを把握する必要があります。コミット間の変更は数時間しかないため、問題が潜んでいる可能性のある場所はそれほど多くありません。さらに、あまり変更されていないため、差分デバッグを使用してバグを見つけることができます。
私の一般的な経験則では、すべての開発者は毎日メインラインにコミットする必要があります。実際には、継続的インテグレーションの経験が豊富な人は、それよりも頻繁に統合します。統合の頻度が高ければ高いほど、競合エラーを探す場所が減り、競合をより迅速に解決できます。
頻繁なコミットは、開発者が自分の作業をそれぞれ数時間の小さな塊に分割することを奨励します。これは進捗状況を追跡するのに役立ち、進捗状況を把握できます。多くの場合、最初は数時間で何か意味のあることができるとは感じませんが、メンタリングと実践が役立つことがわかりました。
メインラインへのプッシュはすべてビルドをトリガーする必要がある
チームの全員が少なくとも毎日統合する場合、これはメインラインが健全な状態を維持することを意味するはずです。しかし実際には、まだ問題が発生します。これは、規律の欠如、プッシュ前の更新とビルドの怠慢、開発者のワークスペース間の環境の違いが原因である可能性があります。
したがって、すべてのコミットが参照環境で検証されるようにする必要があります。これを行う通常の方法は、メインラインを監視する継続的インテグレーションサービス(CIサービス)を使用することです。(CIサービスの例としては、Jenkins、GitHub Actions、Circle CIなどのツールがあります。)メインラインがコミットを受け取るたびに、CIサービスはメインラインのヘッドを統合環境にチェックアウトし、フルビルドを実行します。この統合ビルドが緑色になった場合にのみ、開発者は統合が完了したと見なすことができます。すべてのプッシュでビルドを行うことで、失敗した場合、障害が最新のプッシュにあることがわかり、修正する必要のある場所を絞り込むことができます。
ここで強調したいのは、CIサービスを使用する場合、バージョン管理システムの参照インスタンスのメインブランチであるメインラインでのみ使用することです。複数のブランチを監視およびビルドするためにCIサービスを使用することは一般的ですが、統合のポイントは、すべてのコミットを単一のブランチ上で共存させることです。CIサービスを使用して、異なるブランチの自動ビルドを行うことは有用かもしれませんが、それは継続的インテグレーションと同じではありません。継続的インテグレーションを使用するチームは、製品の単一のブランチを監視するためにのみCIサービスを必要とします。
今日ではほぼすべてのチームがCIサービスを使用していますが、それなしで継続的インテグレーションを実行することも完全に可能です。チームメンバーは、メインラインのヘッドを統合マシンに手動でチェックアウトし、ビルドを実行して統合を検証できます。しかし、自動化がこれほど自由に利用できるのに、手動プロセスを行う意味はほとんどありません。
(これは、Thoughtworksの同僚が、特に最初のCIサービスであるCruise Controlなど、継続的インテグレーション用の多くのオープンソースツールに貢献してきたことを述べるのに適切なポイントです。)
壊れたビルドはすぐに修正する
継続的インテグレーションは、メインラインが健全な状態に保たれている場合にのみ機能します。統合ビルドが失敗した場合は、すぐに修正する必要があります。ケント・ベックが言うように、「ビルドを修正するよりも優先度の高いタスクは誰も持っていない」のです。これは、チームの全員がビルドを修正するために作業を中断する必要があるという意味ではなく、通常は物事を再び機能させるには数人しか必要ありません。これは、ビルドの修正を緊急の優先度の高いタスクとして意識的に優先順位を付けることを意味します。
通常、ビルドを修正する最良の方法は、障害のあるコミットをメインラインから元に戻し、チームの残りのメンバーが作業を続行できるようにすることです。
通常、ビルドを修正する最良の方法は、メインラインからの最新のコミットを元に戻し、システムを最後に既知の正常なビルドに戻すことです。問題の原因がすぐに明らかな場合は、新しいコミットで直接修正できますが、それ以外の場合は、メインラインを元に戻すことで、一部の人が別の開発環境で問題を把握し、チームの残りのメンバーはメインラインで作業を続行できます。
一部のチームは、ペンディングヘッド(プレテスト、遅延コミット、またはゲート付きコミットとも呼ばれます)を使用することで、メインラインを壊すリスクをすべて排除することを好みます。これを行うには、CIサービスは、統合のためにメインラインにプッシュされたコミットがすぐにメインラインに反映されないように設定する必要があります。代わりに、ビルドが完了するまで別のブランチに配置され、グリーンビルド後にのみメインラインに移行されます。この手法はメインラインが壊れる危険性を回避しますが、効果的なチームであればメインラインが赤になることはめったになく、たとえ発生してもその可視性から、どうすればそれを回避できるかを学ぶことが推奨されます。
ビルドを高速に保つ
継続的インテグレーションの要点は、迅速なフィードバックを提供することです。ビルドに時間がかかるほど、継続的インテグレーションの活力が失われるものはありません。ここで、長いビルドとみなされるものに対する、ある偏屈な老人の面白さを認めざるを得ません。私の同僚のほとんどは、1時間かかるビルドはまったく理にかなっていないと考えています。私は、チームがそれほど早くできることを夢見ていたのを覚えています。そして時々、ビルドをその速度にするのが非常に難しいケースに今でも遭遇します。
しかし、ほとんどのプロジェクトでは、10分ビルドというXPのガイドラインは完全に理にかなっています。私たちの現代のプロジェクトのほとんどはこれを達成しています。それを実現するために集中的な努力をする価値はあります。なぜなら、ビルド時間から削られた1分は、開発者がコミットするたびに節約される1分になるからです。継続的インテグレーションでは頻繁なコミットが要求されるため、これは多くの時間になります。
ビルド時間が1時間になっている場合、より高速なビルドに移行することは気が遠くなるように思えるかもしれません。新しいプロジェクトに取り組み、どうすれば高速を維持できるかを考えることさえ、気が遠くなる可能性があります。少なくともエンタープライズアプリケーションの場合、通常のボトルネックはテスト(特にデータベースなどの外部サービスを伴うテスト)であることがわかりました。
おそらく最も重要なステップは、デプロイメントパイプラインのセットアップに取り組み始めることです。デプロイメントパイプライン(ビルドパイプラインまたはステージドビルドとも呼ばれます)の背後にある考え方は、実際には複数のビルドが順番に実行されるということです。メインラインへのコミットは最初のビルド(私がコミットビルドと呼ぶもの)をトリガーします。コミットビルドとは、誰かがコミットをメインラインにプッシュするときに必要なビルドです。コミットビルドは迅速に実行する必要があるため、バグ検出能力を低下させるいくつかのショートカットが採用されます。重要なのは、バグ発見のニーズとスピードのバランスを取り、優れたコミットビルドが他の人が作業するのに十分な安定性を持つようにすることです。
コミットビルドが良好になると、他の人は自信を持ってコードを操作できます。ただし、他にも実行できる、より遅いテストがあります。追加のマシンは、実行に時間がかかるビルドに対して、さらにテストルーチンを実行できます。
これの簡単な例は、2段階のデプロイメントパイプラインです。最初の段階では、コンパイルを実行し、遅いサービスがテストダブル(偽のインメモリデータベースや外部サービス用のスタブなど)に置き換えられた、よりローカライズされた単体テストを実行します。このようなテストは非常に高速に実行でき、10分間のガイドライン内に収まります。ただし、大規模な相互作用、特に実際のデータベースを伴う相互作用を含むバグは見つかりません。2番目の段階のビルドでは、実際のデータベースをヒットし、よりエンドツーエンドの動作を伴う、別のテストスイートを実行します。このスイートは実行に数時間かかる場合があります。
このシナリオでは、人々は最初の段階をコミットビルドとして使用し、これをメインのCIサイクルとして使用します。2次ビルドが失敗した場合、これは「すべてを停止する」品質と同じではないかもしれませんが、チームはコミットビルドを実行し続けながら、そのようなバグをできるだけ迅速に修正することを目指しています。2次ビルドははるかに遅くなる可能性があるため、すべてのコミット後に実行されない場合があります。その場合、コミット段階からの最後の良好なビルドを選択して、できる限り頻繁に実行されます。
2次ビルドでバグが検出された場合、それはコミットビルドに別のテストが必要な兆候です。可能な限り、後の段階での失敗が、バグをキャッチするコミットビルドでの新しいテストにつながるようにして、コミットビルドでバグが修正された状態を維持したいと考えています。このようにして、何かを通過させると、コミットテストが強化されます。バグを露出する高速実行テストを構築する方法がない場合があり、その場合は2次ビルドでのみその条件をテストすることに決める場合があります。幸いなことに、ほとんどの場合、適切なテストをコミットビルドに追加できます。
スピードアップする別の方法は、並列処理と複数のマシンを使用することです。特にクラウド環境では、チームはビルド用にサーバーの小規模なフリートを簡単に立ち上げることができます。テストが十分に独立して実行できる場合(適切に記述されたテストでは可能)、そのようなフリートを使用すると、非常に迅速なビルド時間を実現できます。このような並列クラウドビルドは、開発者の統合前ビルドにも価値がある場合があります。
より広範なビルドプロセスを検討する際には、依存関係とのやり取りという別のカテゴリの自動化についても言及する価値があります。ほとんどのソフトウェアは、さまざまな組織によって作成された広範囲の依存ソフトウェアを使用します。これらの依存関係の変更は、製品の破損を引き起こす可能性があります。したがって、チームは依存関係の新しいバージョンを自動的にチェックし、別のチームメンバーであるかのようにビルドに統合する必要があります。これは、依存関係の変化率に応じて、通常は少なくとも毎日、頻繁に行う必要があります。契約テストの実行についても同様のアプローチを使用する必要があります。これらの依存関係の相互作用が赤になった場合、通常のビルドの失敗と同じ「ラインを停止する」効果はありませんが、チームによる調査と修正のための迅速な対応が必要です。
作業中のものを隠す
継続的インテグレーションとは、少しでも進捗があり、ビルドが正常であれば、すぐに統合することを意味します。多くの場合、これは、ユーザーに表示される機能が完全に形成され、リリース準備が整う前に統合することを示唆しています。したがって、潜在的なコード、つまり、ライブリリースに存在する未完成機能の一部であるコードに対処する方法を検討する必要があります。
潜在的なコードを心配する人もいます。これは、本番品質ではないコードがリリースされた実行可能ファイルに入れられるためです。継続的インテグレーションを実行するチームは、メインラインに送信されるすべてのコードが、コードを検証するテストとともに、本番品質であることを保証します。潜在的なコードは本番環境で実行されない場合がありますが、それはテストで実行されるのを妨げるものではありません。
キーストーンインターフェースを使用して、コードが本番環境で実行されないようにすることができます。これは、新しい機能へのパスを提供するインターフェースが、コードベースに追加する最後のものになるようにします。テストでは、その最終インターフェース以外のすべてのレベルでコードをチェックできます。適切に設計されたシステムでは、このようなインターフェース要素は最小限である必要があり、したがって短いプログラミングエピソードで簡単に追加できます。
ダークローンチを使用すると、変更をユーザーに表示する前に、本番環境で変更をテストできます。この手法は、パフォーマンスへの影響を評価するのに役立ちます。
キーストーンは潜在的なコードのほとんどのケースをカバーしていますが、それが不可能な場合にはフィーチャーフラグを使用します。フィーチャーフラグは、潜在的なコードを実行しようとするたびにチェックされます。これらは環境の一部として(おそらく環境固有の構成ファイルで)設定されます。これにより、潜在的なコードをテスト用にアクティブにすることができますが、本番環境では無効にすることができます。フィーチャーフラグは、継続的インテグレーションを有効にするだけでなく、A/Bテストやカナリアリリースの実行時切り替えも容易にします。次に、機能が完全にリリースされたら、フラグがコードベースを乱雑にしないように、このロジックを速やかに削除するようにします。
抽象化による分岐は、潜在的なコードを管理するための別の手法であり、特にコードベース内の大規模なインフラストラクチャの変更に役立ちます。基本的にこれは、変更されているモジュールへの内部インターフェースを作成します。その後、インターフェースは、時間の経過とともに実行パスを徐々に置き換えながら、古いロジックと新しいロジックの間をルーティングできます。永続化プラットフォームの変更など、そのような普及した要素を切り替えるためにこれが行われたのを見てきました。
新しい機能を紹介するときは、問題が発生した場合にロールバックできることを常に確認する必要があります。並列変更(別名:拡張-縮小)は、変更を可逆的なステップに分割します。たとえば、データベースフィールドの名前を変更する場合、最初に新しい名前で新しいフィールドを作成し、次に古いフィールドと新しいフィールドの両方に書き込み、次に既存の古いフィールドからデータをコピーし、次に新しいフィールドから読み取り、その後でのみ古いフィールドを削除します。このような変更を一度に行うことは不可能だったでしょうが、これらの手順のいずれかを逆にすることができます。継続的インテグレーションを使用するチームは、変更を小さくして元に戻しやすくすることで、このように変更を分割することをよく検討します。
本番環境のクローンでテストする
テストの目的は、システムが本番環境で抱えるであろう問題を、管理された条件下で洗い出すことです。この重要な部分として、本番システムが実行される環境があります。異なる環境でテストする場合、すべての違いは、テストで発生したことが本番環境で発生しないリスクをもたらします。
したがって、テスト環境は本番環境を可能な限り正確に模倣するように設定する必要があります。同じバージョンのデータベースソフトウェアを使用し、同じバージョンのオペレーティングシステムを使用します。本番環境にあるすべての適切なライブラリを、システムが実際に使用しない場合でもテスト環境に入れます。同じIPアドレスとポートを使用し、同じハードウェア上で実行します。
仮想環境は、これを実現することを以前よりはるかに容易にしました。コンテナ内で本番ソフトウェアを実行し、開発者のワークスペースであっても、テスト用に全く同じコンテナを確実に構築します。これを行うための労力とコストは価値があり、環境の不一致によって生じた穴から這い出した単一のバグを追跡するのに比べれば、通常はわずかなものです。
一部のソフトウェアは、異なるオペレーティングシステムやプラットフォームバージョンなど、複数の環境で実行されるように設計されています。デプロイメントパイプラインは、これらのすべての環境で並行してテストを行うようにする必要があります。
注意すべき点は、本番環境が開発環境ほど優れていない場合です。本番ソフトウェアは、スマートフォンなどの不安定なWi-Fi接続で接続されたマシンで実行されるのでしょうか?そうであれば、テスト環境でネットワーク接続が悪い状態を模倣するようにしてください。
全員が何が起こっているかを確認できる
継続的インテグレーションはすべてコミュニケーションに関するものなので、誰もがシステムの現状と、加えられた変更を容易に確認できるようにする必要があります。
コミュニケーションで最も重要なことの1つは、メインラインビルドの状態です。CIサービスには、実行中のビルドの状態を誰でも確認できるダッシュボードがあります。多くの場合、Slackなどの内部ソーシャルメディアツールにビルド情報をブロードキャストするために他のツールと連携します。IDEには、これらのメカニズムへのフックがあることが多く、開発者は作業の多くに使用しているツール内にとどまりながらアラートを受け取ることができます。多くのチームはビルドの失敗に関する通知のみを送信しますが、私は成功時にもメッセージを送信する価値があると思います。そうすることで、人々は定期的なシグナルに慣れ、ビルドの長さを把握できます。言うまでもなく、CIサーバーからでも、毎日「よくやった」と言われるのは良いことです。
物理的なスペースを共有するチームは、ビルドのために常にオンの物理ディスプレイを持っていることがよくあります。通常、これは簡略化されたダッシュボードを表示する大型スクリーンの形をとります。これは、多くの場合、メインラインコミットビルドの赤/緑の色を使用して、壊れたビルドを全員に警告するのに特に役立ちます。
私が気に入っていた古い物理ディスプレイの1つは、赤と緑のラバランプの使用でした。ラバランプの特徴の1つは、しばらくオンにすると泡立ち始めることです。赤いランプが点灯したら、泡立ち始める前にチームがビルドを修正すべきであるという考えでした。ビルドステータスの物理ディスプレイはしばしば遊び心があり、チームのワークスペースに少し風変わりな個性を加えていました。踊るウサギの楽しい思い出があります。
ビルドの現在の状態に加えて、これらのディスプレイは、プロジェクトの健全性を示す指標となる可能性のある最近の履歴に関する役立つ情報を表示できます。20世紀初頭に戻ると、私は安定したビルドを作成できないという歴史を持つチームと働いていました。私たちは各日ごとに小さな四角がある1年間を表示したカレンダーを壁に貼りました。毎日、QAグループは、コミットテストに合格した1つの安定したビルドを受け取った場合、その日に緑色のステッカーを貼り、それ以外の場合は赤い四角を貼りました。時間の経過とともに、カレンダーはビルドプロセスの状態を明らかにし、緑の四角が非常に一般的になり、カレンダーが消えるまで、着実に改善を示しました。その目的は果たされました。
デプロイを自動化する
継続的インテグレーションを行うには、複数の環境が必要です。コミットテストを実行するための環境、おそらくデプロイメントパイプラインのさらに多くの部分を実行するための環境です。これらの環境間で実行可能ファイルを1日に何度も移動するので、これを自動的に行う必要があります。したがって、アプリケーションを任意の環境に簡単にデプロイできるようにするスクリプトを用意することが重要です。
仮想化、コンテナ化、サーバーレスの最新ツールを使用すると、さらに進めることができます。製品をデプロイするスクリプトだけでなく、必要な環境をゼロから構築するスクリプトも用意します。このようにして、既製のベアボーン環境から開始し、製品を実行するために必要な環境を作成し、製品をインストールして実行できます。すべて完全に自動化されています。機能フラグを使用して作業中のものを隠している場合、これらの環境はすべての機能フラグをオンにして設定できるため、これらの機能はすべての差し迫った相互作用でテストできます。
この自然な結果として、これらの同じスクリプトを使用すると、同様の容易さで本番環境にデプロイできます。多くのチームはこれらの自動化を使用して1日に何度も新しいコードを本番環境にデプロイしますが、頻度が少ない場合でも、自動デプロイメントはプロセスの高速化とエラーの削減に役立ちます。また、テスト環境へのデプロイに使用するのと同じ機能を使用するだけなので、安価なオプションです。
本番環境に自動的にデプロイする場合、便利な追加機能の1つは自動ロールバックです。悪いことは時々起こります。そして、臭い茶色の物質が回転する金属を襲った場合は、最後に既知の正常な状態にすばやく戻ることができると良いでしょう。自動的に復元できると、デプロイの緊張も大幅に軽減され、人々はより頻繁にデプロイして、新しい機能をユーザーにすばやく提供するようになります。ブルーグリーンデプロイメントを使用すると、デプロイされたバージョン間でトラフィックを切り替えることにより、新しいバージョンをすばやくライブにすることができ、必要に応じて同様にすばやくロールバックできます。
自動デプロイメントにより、カナリアリリースの設定が容易になり、製品の新しいバージョンを一部のユーザーにデプロイして、全体にリリースする前に問題を洗い出すことができます。
モバイルアプリケーションは、テスト環境(この場合はデバイス上)へのデプロイを自動化することが不可欠な好例であり、アプリストアの管理者を発動する前に、新しいバージョンを探索できるようにします。実際、デバイスに依存するソフトウェアには、テストデバイスに新しいバージョンを簡単に取得する方法が必要です。
このようなソフトウェアをデプロイするときは、バージョン情報が表示されるようにすることを忘れないでください。「バージョン情報」画面にはバージョン管理に結びつくビルドIDを含める必要があり、ログには実行されているソフトウェアのバージョンを簡単に確認できるようにする必要があり、バージョン情報を提供するAPIエンドポイントがある必要があります。
インテグレーションのスタイル
これまで、私は統合への1つのアプローチについて説明してきましたが、それが普遍的でない場合、他の方法があるはずです。他のことと同様に、私が与える分類には曖昧な境界がありますが、私は統合を処理する3つのスタイルを考えるのが役立つと思っています。プリリリース統合、フィーチャーブランチ、継続的インテグレーションです。
最も古いのは、私が80年代に倉庫で見たものです。プリリリース統合です。これは、統合をソフトウェアプロジェクトの1フェーズと見なします。これはウォーターフォールプロセスの自然な一部です。このようなプロジェクトでは、作業は個人または小規模チームが行うことができるユニットに分割されます。各ユニットは、他のユニットとの相互作用が最小限のソフトウェアの一部です。これらのユニットは個別に構築およびテストされます(「単体テスト」という用語の本来の使用法)。次に、ユニットの準備ができたら、それらを最終製品に統合します。この統合は1回行われ、その後に統合テストが行われ、リリースに進みます。したがって、作業について考えると、全員が機能に並行して取り組むフェーズと、統合での1つの努力の流れの2つのフェーズが見られます。
このスタイルでの統合の頻度は、リリースの頻度に結びついており、通常はソフトウェアのメジャーバージョンであり、通常は数か月または数年単位で測定されます。これらのチームは緊急のバグ修正には異なるプロセスを使用するため、通常の統合スケジュールとは別にリリースできます。
今日、統合で最も人気のあるアプローチの1つは、フィーチャーブランチを使用することです。このスタイルでは、機能は古いアプローチのユニットと同様に、個人または小規模チームに割り当てられます。ただし、統合する前にすべてのユニットが完了するのを待つ代わりに、開発者は機能が完了したらすぐにメインラインに統合します。一部のチームは各機能統合後に本番環境にリリースし、他のチームはリリース用にいくつかの機能をまとめて処理することを好みます。
フィーチャーブランチを使用するチームは通常、全員がメインラインから定期的にプルすることを期待しますが、これは準統合です。もし私とレベッカが別々のフィーチャーに取り組んでいる場合、毎日メインラインからプルするかもしれませんが、どちらかがフィーチャーを完了して統合し、メインラインにプッシュするまで、お互いの変更を見ることはありません。その後、もう一方が次のプルでそのコードを見て、自分の作業コピーに統合します。したがって、各フィーチャーがメインラインにプッシュされた後、他のすべての開発者は、この最新のメインラインプッシュを自分のフィーチャーブランチと結合するための統合作業を行います。
これは、各開発者がメインラインの変更を自分のローカルブランチに結合するだけなので、準統合に過ぎません。完全な統合は、開発者が変更をプッシュし、別のラウンドの準統合を引き起こすまで発生しません。たとえ私とレベッカが両方ともメインラインから同じ変更をプルしても、それらの変更と統合しただけであり、お互いのブランチとは統合していません。
継続的インテグレーションでは、毎日、全員が変更をメインラインにプッシュし、他の全員の変更を自分の作業に取り込みます。これにより、統合作業が大幅に増えますが、各統合作業ははるかに小さくなります。数日分の作業を組み合わせるよりも、数時間分のコードベースでの作業を組み合わせる方がはるかに簡単です。
継続的インテグレーションのメリット
3つの統合スタイルの相対的なメリットについて議論する場合、議論のほとんどは実際には統合の頻度に関するものです。プレリリース統合とフィーチャーブランチングの両方は、異なる頻度で動作することができ、統合スタイルを変更せずに統合頻度を変更することも可能です。プレリリース統合を使用している場合、毎月のリリースと年間のリリースには大きな違いがあります。フィーチャーブランチングは、通常、より高い頻度で動作します。なぜなら、統合は、多数のユニットをまとめてバッチ処理するのを待つのではなく、各フィーチャーが個別にメインラインにプッシュされるときに発生するからです。チームがフィーチャーブランチングを実行しており、すべてのフィーチャーが1日未満の作業で構築できる場合、それらは事実上継続的インテグレーションと同じです。しかし、継続的インテグレーションは、高頻度スタイルとして定義されている点が異なります。継続的インテグレーションは、統合頻度をそれ自体が目標として設定することを重視しており、フィーチャーの完了やリリース頻度に縛られていません。
したがって、ほとんどのチームは、スタイルを変更せずに頻度を上げることで、後述する要因で有用な改善が見られるでしょう。フィーチャーのサイズを2か月から2週間に短縮することには大きなメリットがあります。継続的インテグレーションには、高頻度統合をベースラインとして設定し、それを継続可能にする習慣と実践を設定するという利点があります。
デリバリー遅延のリスクの軽減
複雑な統合にどれくらいの時間がかかるかを推定するのは非常に困難です。Gitでのマージが難しい場合もありますが、すべてがうまく機能する場合もあります。また、マージは迅速に行えたが、微妙な統合バグを見つけて修正するのに何日もかかる場合もあります。統合間の時間が長ければ長いほど、統合するコードが増え、時間が長くなります。さらに悪いことに、予測不可能性が増加します。
これらすべてが、プレリリース統合を悪夢の特別な形にします。統合はリリースの前の最後のステップの1つであるため、すでに時間が逼迫しており、プレッシャーがかかっています。予測困難なフェーズを終盤に持つことは、緩和が非常に難しい大きなリスクがあることを意味します。それが、私の80年代の記憶が非常に強い理由であり、統合地獄にはまって、統合バグを修正するたびにさらに2つ発生するというプロジェクトを何度も見てきました。
統合頻度を上げるためのあらゆるステップは、このリスクを軽減します。統合するものが少なければ少ないほど、新しいリリースが準備できるまで未知の時間が少なくなります。フィーチャーブランチングは、この統合作業を個々のフィーチャーストリームにプッシュすることで役立ちます。そのため、放置しておけば、フィーチャーが準備でき次第、ストリームはメインラインにプッシュできます。
しかし、放置しておけばという点が重要です。他の誰かがメインラインにプッシュすると、フィーチャーが完了する前に統合作業が発生します。ブランチは分離されているため、あるブランチで作業している開発者は、他のフィーチャーがプッシュされる可能性や、それらを統合するために必要な作業量について、あまり把握していません。優先度の高いフィーチャーが統合の遅延に直面する危険性がありますが、優先度の低いフィーチャーのプッシュを防止することでこれを管理できます。
継続的インテグレーションは、事実上、デリバリーリスクを排除します。統合は非常に小さいため、通常、問題なく進みます。面倒な統合は、解決するのに数分以上かかるものです。最悪のケースは、競合が発生して誰かが作業を最初からやり直すことですが、それでも失う作業は1日分未満であり、したがって、利害関係者の委員会を悩ませる可能性は低いでしょう。さらに、ソフトウェアを開発する際に定期的に統合を行っているため、問題を解決するための時間が増えている間に問題に直面することができ、それらを解決する方法を練習できます。
たとえチームが定期的に本番環境にリリースしていない場合でも、継続的インテグレーションは、全員が製品の状態を正確に把握できるため重要です。リリース前に実行する必要がある隠れた統合作業はなく、統合作業はすでに組み込まれています。
インテグレーションに費やす時間の削減
統合に費やされる時間と統合のサイズがどのように一致するかを測定した本格的な調査を見たことはありませんが、私の経験的な証拠は、その関係が線形ではないことを強く示唆しています。統合するコードが2倍多い場合、統合を実行するのに4倍の時間がかかる可能性が高くなります。これは、3つのノードを完全に接続するには3本の線が必要ですが、4つのノードを接続するには6本の線が必要なのによく似ています。統合はすべて接続に関するものであるため、非線形的な増加になります。これは、私の同僚の経験にも反映されています。
フィーチャーブランチを使用している組織では、この失われた時間の多くは個人が感じます。メインラインへの大きな変更に対してリベースを試みるのに数時間費やすことは、イライラの元になります。完了したプルリクエストのコードレビューを数日待つこと、その待機期間中に別のメインラインの大きな変更が行われた場合はさらにイライラの元になります。2週間前に完了したフィーチャーの統合テストで見つかった問題をデバッグするために、新しいフィーチャーの作業を中断しなければならないことは、生産性を低下させます。
継続的インテグレーションを行っている場合、統合は一般的にイベントではありません。私はメインラインをプルダウンし、ビルドを実行してプッシュします。競合がある場合、私が書いたわずかなコードは頭に新しいので、通常、簡単に見ることができます。ワークフローは規則的であるため、私たちはそれに慣れており、できるだけ多くを自動化するようにインセンティブが与えられます。
これらの非線形効果の多くと同様に、統合は人々が間違った教訓を学ぶ罠に簡単になりえます。難しい統合は非常にトラウマになる可能性があり、チームが統合の頻度を減らすべきだと判断することがありますが、これは将来的に問題を悪化させるだけです。
ここで何が起こっているかというと、チームメンバー間のより緊密なコラボレーションが見られます。2人の開発者が競合する決定を下した場合、統合時にそれがわかります。したがって、統合間の時間が短ければ短いほど、競合を検出するまでの時間が短くなり、競合が大きくなりすぎる前に対応できます。高頻度統合では、ソース管理システムがコミュニケーションチャネルになり、そうでない場合は言えないことを伝達することができます。
バグの削減
バグ - これらは、信頼を破壊し、スケジュールと評判を混乱させる厄介なものです。デプロイされたソフトウェアのバグは、ユーザーを私たちに対して怒らせます。通常の開発中に発生するバグは、私たちの邪魔になり、ソフトウェアの残りの部分を正しく動作させることを困難にします。
継続的インテグレーションはバグを取り除くものではありませんが、バグを劇的に見つけやすく、削除しやすくします。これは高頻度統合というよりも、自己テストコードの不可欠な導入によるものです。継続的インテグレーションは、適切なテストがなければ、健全なメインラインを維持できないため、自己テストコードなしでは機能しません。したがって、継続的インテグレーションは、定期的なテストの体制を確立します。テストが不十分な場合、チームはすぐにそれに気づき、是正措置を講じることができます。セマンティックな競合によりバグが発生した場合、統合するコードが少ないため、検出が簡単です。頻繁な統合は、Diffデバッグともうまく連携するため、数週間後に気づいたバグでも、小さな変更に絞り込むことができます。
バグは累積的でもあります。バグが多ければ多いほど、各バグを削除するのが難しくなります。これは、複数のエラーの結果として障害が発生するバグの相互作用が発生することが理由の1つです。これにより、各エラーを見つけることが難しくなります。また、心理的な側面もあります。バグが多いと、人々はバグを見つけて取り除くためのエネルギーが少なくなります。したがって、継続的インテグレーションによって強化された自己テストコードは、欠陥によって引き起こされる問題を軽減する上で、別の指数関数的な効果をもたらします。
これは、多くの人が直感的ではないと感じる別の現象につながります。変更を導入することがバグを導入することをどれほど頻繁に意味するかを理解すると、人々は信頼性の高いソフトウェアを持つためには、リリース率を遅くする必要があると結論付けます。これは、Nicole Forsgren氏が率いるDORA調査プログラムによって強く否定されました。彼らは、エリートチームがより迅速に、より頻繁に本番環境にデプロイし、これらの変更を行ったときに障害発生率が劇的に低いことを発見しました。また、この調査では、チームがアプリケーションのコードリポジトリに3つ以下のアクティブなブランチを持ち、少なくとも1日に1回はブランチをメインラインにマージし、コードフリーズや統合フェーズがない場合に、チームのパフォーマンスレベルが高いことも発見されています。
持続的な生産性を実現するためのリファクタリングが可能
ほとんどのチームは、時間の経過とともにコードベースが劣化することを観察しています。初期の決定はその時点では優れていましたが、6か月の作業後には最適ではなくなっています。しかし、チームが学んだことを取り入れてコードを変更することは、既存のコードの奥深くに変更を導入することを意味し、時間と手間がかかり、リスクも大きい難しいマージにつながります。誰もが、将来的に良い変更となる変更を加えた人が、他の人の作業を壊して何日も費やした時のことを覚えています。その経験から、誰も既存のコードの構造をやり直したくありません。たとえそれが、誰もが構築するのが面倒になったとしても、新しいフィーチャーの配信が遅れることになるからです。
リファクタリングは、この腐敗のプロセスを軽減し、実際に逆転させるために不可欠なテクニックです。定期的にリファクタリングを行うチームは、コードの小さな、動作を保持する変換を使用することで、コードベースの構造を改善するための規律あるテクニックを持っています。これらの変換の特性は、バグを導入する可能性を大幅に減らし、特に自己テストコードの基盤によってサポートされている場合は、迅速に実行できます。あらゆる機会にリファクタリングを適用することで、チームは既存のコードベースの構造を改善し、新しい機能の追加をより簡単かつ迅速にすることができます。
しかし、この喜ばしい物語は、統合の苦労によって台無しになる可能性があります。2週間のリファクタリングセッションはコードを大幅に改善するかもしれませんが、他の全員が過去2週間古い構造で作業していたため、マージに時間がかかるという結果になります。これにより、リファクタリングのコストが法外なレベルに上昇します。頻繁な統合は、リファクタリングを行う人と他のすべての人が定期的に作業を同期することを保証することで、このジレンマを解決します。継続的インテグレーションを使用する場合、誰かが私が使用しているコアライブラリに侵入的な変更を加えた場合、これらの変更に合わせて調整する必要があるのは数時間のプログラミングだけです。彼らが私の変更の方向性と衝突する何かをした場合、すぐにそれについて知ることができるため、より良い方法を考え出すために彼らと話し合う機会があります。
この記事ではこれまで、高頻度統合の利点に関するいくつかの直感に反する概念を提起しました。つまり、統合を頻繁に行うほど、統合に費やす時間が少なくなり、頻繁な統合はバグの減少につながるということです。ソフトウェア開発において、おそらく最も重要な直感に反する概念は、コードベースを健全に保つために多くの労力を費やすチームは、より迅速かつ安価に機能を提供できるということです。テストの作成とリファクタリングに投資する時間は、納期のスピードにおいて目覚ましいリターンをもたらし、継続的インテグレーションは、チーム環境でそれを実現するための重要な要素です。
本番環境へのリリースはビジネス上の意思決定
新しく構築された機能をステークホルダーにデモンストレーションしていると想像してください。彼女は、「これは本当にクールで、ビジネスに大きな影響を与えるだろう。これを公開できるようになるまでどれくらいかかりますか?」と反応します。その機能が統合されていないブランチで表示されている場合、特に本番環境へのパスに十分な自動化がない場合は、答えは数週間または数か月になる可能性があります。継続的インテグレーションを使用すると、リリース準備完了のメインラインを維持できます。つまり、製品の最新バージョンを本番環境にリリースする決定は、純粋にビジネス上の決定です。ステークホルダーが最新のものを公開したい場合、自動化されたパイプラインを実行してそれを行うのに数分で済みます。これにより、ソフトウェアの顧客は機能がリリースされるタイミングをより適切に制御できるようになり、開発チームとの緊密な連携が促進されます。
継続的インテグレーションとリリース準備完了のメインラインは、頻繁なデプロイの最大の障壁の1つを取り除きます。頻繁なデプロイは、ユーザーが新しい機能をより迅速に入手し、それらの機能に関するフィードバックをより迅速に提供し、一般的に開発サイクルでより協力的になることができるため、価値があります。これは、顧客と開発の間の障壁を打ち破るのに役立ちます。これらの障壁は、私が考えるに、ソフトウェア開発を成功させるための最大の障壁です。
継続的インテグレーションを使用すべきではない場合
これらのメリットはすべてかなり魅力的です。しかし、私と同じくらい経験豊富(または皮肉屋)な人は、メリットのリストだけでは常に疑念を抱きます。コストがかからないものはほとんどなく、アーキテクチャとプロセスに関する決定は、通常、トレードオフの問題です。
しかし、継続的インテグレーションは、献身的で熟練したチームが利用するのにほとんど欠点がないまれなケースの1つであることを認めます。散発的な統合によって課せられるコストは非常に大きいため、ほとんどすべてのチームが統合頻度を上げることでメリットを得ることができます。メリットが積み重なるのをやめる時期にはある程度の限界がありますが、その限界は数日ではなく数時間で、これはまさに継続的インテグレーションの領域です。自己テストコード、継続的インテグレーション、およびリファクタリングの相互作用は特に強力です。私たちはThoughtworksでこのアプローチを20年間使用しており、私たちの唯一の疑問は、それをより効果的に行う方法です。コアアプローチは証明されています。
しかし、それは継続的インテグレーションがすべての人に適しているという意味ではありません。私が「献身的で熟練したチームがそれを利用するのにほとんど欠点がない」と言ったことに気づくかもしれません。これらの2つの形容詞は、継続的インテグレーションが適していないコンテキストを示しています。
「献身的」とは、製品にフルタイムで取り組んでいるチームを意味します。これに対する良い反例は、1人または2人のメンテナーと多くの貢献者がいる古典的なオープンソースプロジェクトです。このような状況では、メンテナーでさえプロジェクトに週に数時間しか費やしておらず、貢献者をよく知らず、貢献者がいつ貢献するか、または貢献する際に従うべき基準について、十分に把握していません。これが、フィーチャーブランチワークフローとプルリクエストにつながった環境です。このような状況では、継続的インテグレーションは現実的ではありませんが、統合頻度を上げるための努力は依然として価値があります。
継続的インテグレーションは、通常、商用ソフトウェアの場合のように、製品にフルタイムで取り組んでいるチームに適しています。しかし、古典的なオープンソースとフルタイムモデルの間には、多くの中間地点があります。チームのコミットメントに合った統合ポリシーを使用するために、判断する必要があります。
2番目の形容詞は、必要なプラクティスに従うチームのスキルを見ています。チームが強力なテストスイートなしで継続的インテグレーションを試みると、バグをスクリーニングするためのメカニズムがないため、あらゆる種類のトラブルが発生します。自動化しないと、統合に時間がかかりすぎて、開発の流れを妨げます。メインラインへのプッシュがグリーンビルドで行われることを保証する規律がないと、メインラインは常に壊れてしまい、全員の作業を妨げることになります。
継続的インテグレーションの導入を検討している人は誰でも、これらのスキルを念頭に置く必要があります。自己テストコードなしで継続的インテグレーションを導入しても機能せず、適切に行われた場合の継続的インテグレーションがどのようなものかについて、不正確な印象を与えることになります。
そうは言っても、スキルの要求が特に難しいとは思いません。このプロセスをチームで機能させるために、ロックスター開発者は必要ありません。(実際、ロックスター開発者はしばしば障壁になります。なぜなら、自分自身をそのように考える人は、通常、あまり規律がないからです。)これらの技術的なプラクティスのスキルは、それほど習得が難しいものではなく、通常、問題は良い教師を見つけ、規律を具体化する習慣を形成することです。チームが流れをつかむと、通常、快適でスムーズ、そして速く感じます。
継続的インテグレーションの導入
継続的インテグレーションのようなプラクティスを導入する方法を説明することの難しいことの1つは、パスが開始する場所によって大きく異なることです。これを書いている時点では、あなたがどのようなコードに取り組んでいるか、あなたのチームがどのようなスキルと習慣を持っているか、さらにはより広範な組織の状況さえ知りません。私のような人ができることは、いくつかの一般的な標識を指摘することだけです。それがあなた自身の道を見つけるのに役立つことを願っています。
新しいプラクティスを導入するときは、なぜそれを行っているのかを明確にすることが重要です。上記のメリットのリストには、最も一般的な理由が含まれていますが、コンテキストが異なれば、それらの重要度のレベルも異なります。一部のメリットは、他のメリットよりも理解するのが難しいです。統合における無駄を減らすことは、フラストレーションのたまる問題に対処し、進捗状況に合わせて簡単に感じることができます。システム内の不要なものを減らし、全体的な生産性を向上させるためにリファクタリングを有効にすることは、より見えにくいものです。効果が見られるまで時間がかかり、反事実を感知するのは困難です。しかし、これはおそらく継続的インテグレーションの最も価値のあるメリットです。
上記のプラクティスのリストは、継続的インテグレーションを機能させるためにチームが習得する必要があるスキルを示しています。これらのいくつかには、統合頻度が高くなる前に価値をもたらす可能性があります。自己テストコードは、コミットが少ない場合でも、システムに安定性をもたらします。
1つの目標は、統合頻度を2倍にすることを目指すことです。フィーチャーブランチが通常10日間実行される場合は、それを5日間に短縮する方法を見つけ出します。これには、より優れたビルドとテストの自動化、および大きなタスクをより小さく、独立して統合されたタスクに分割する方法に関する創造的な思考が含まれる場合があります。統合前のレビューを使用する場合は、テストカバレッジを確認し、より小さなコミットを推奨するために、これらのレビューに明示的な手順を含めることができます。
新しいプロジェクトを開始する場合は、最初から継続的インテグレーションを開始できます。ビルド時間を監視し、10分ルールよりも遅くなり始めたらすぐに対処する必要があります。迅速に行動することで、コードベースが非常に大きくなって大きな苦痛になる前に、必要な再構築を行うことができます。
何よりもまず、助けを得る必要があります。以前に継続的インテグレーションを行ったことのある人を見つけて、支援してもらいましょう。他の新しいテクニックと同様に、最終結果がどのようなものかを知らない場合は、それを導入することは困難です。このサポートを受けるには費用がかかるかもしれませんが、そうでなければ時間と生産性の損失で支払うことになります。(免責事項/広告 - はい、Thoughtworksではこの分野でコンサルティングを行っています。結局のところ、私たちは犯す可能性のある間違いのほとんどを犯してきました。)
よくある質問
継続的インテグレーションはどこから来たのか?
継続的インテグレーションは、1990年代にエクストリームプログラミングの一部としてケントベックによってプラクティスとして開発されました。当時、リリース前の統合が標準であり、リリース頻度は多くの場合年単位で測定されていました。リリースサイクルを短縮して、反復開発を促進するための一般的な動きがありました。しかし、リリース間隔を数週間で考えているチームはほとんどありませんでした。ケントはプラクティスを定義し、彼が取り組んだプロジェクトでそれを開発し、それが依存する他の主要なプラクティスとどのように相互作用するかを確立しました。
Microsoftは、毎日のビルド(通常は夜間)を行うことで知られていましたが、テスト計画や継続的インテグレーションの重要な要素である欠陥の修正に重点を置いていませんでした。
一部の人は、グレイディブーチがこの用語を作り出したと評価していますが、彼は自身のオブジェクト指向設計の本の中で、単一の文で非公式な説明としてフレーズを使用しただけでした。彼はそれを定義されたプラクティスとして扱っていませんでした。実際、索引には表示されていませんでした。
継続的インテグレーションとトランクベース開発の違いは何ですか?
CIサービスが普及するにつれて、多くの人がそれらを使用してフィーチャーブランチで定期的なビルドを実行しました。上記で説明したように、これは継続的インテグレーションではありませんが、多くの人が(そして考えて)継続的インテグレーションを行っていると言いましたが、実際には大幅に異なることを行っており、それが多くの混乱を引き起こしています。
一部の人々は、新しい用語であるトランクベース開発を造語することによって、このセマンティック拡散に取り組むことにしました。一般的に、私はこれを継続的インテグレーションの同義語と見なしており、「フィーチャーブランチでJenkinsを実行する」こととの混同に悩まされる傾向がないことを認識しています。この2つを区別しようとしている人もいますが、これらの区別は一貫性も説得力もないと感じています。
私は、新しい名前を作ることがセマンティック拡散に対抗する良い方法だとは思わないこと、そしてこのテクニックの名前を変更することが、特にケントベックのように、継続的インテグレーションを最初に支持し、開発した人々の業績を乱暴に消してしまうため、トランクベース開発という用語を使用しません。
私がその用語を避けているにもかかわらず、トランクベース開発という旗の下に書かれた継続的インテグレーションに関する多くの優れた情報があります。特に、ポールハマットは彼のウェブサイトで多くの優れた資料を書いています。
フィーチャーブランチでCIサービスを実行できますか?
簡単な答えは「はい - しかし、あなたは継続的インテグレーションを行っていない」です。ここでの重要な原則は、「誰もが毎日メインラインにコミットする」ということです。フィーチャーブランチで自動ビルドを行うことは便利ですが、それはセミインテグレーションにすぎません。
しかしながら、このような方法でデーモンビルドを利用することが継続的インテグレーションの本質であるという誤解がよくあります。この誤解は、これらのツールを継続的インテグレーションサービスと呼ぶことから生じており、より適切な用語としては「継続的ビルドサービス」のようなものが挙げられます。CIサービスを利用することは継続的インテグレーションを行う上で役立ちますが、ツールと実践を混同すべきではありません。
継続的インテグレーションと継続的デリバリーの違いは何ですか?
継続的インテグレーションの初期の説明では、開発チームの開発環境におけるメインラインへの開発者統合のサイクルに焦点が当てられていました。このような説明では、統合されたメインラインから本番リリースまでの道のりについてはあまり語られていませんでした。しかし、それが人々の念頭になかったわけではありません。「デプロイの自動化」や「本番環境のクローンでのテスト」といったプラクティスは、本番環境への道のりが認識されていたことを明確に示しています。
状況によっては、メインライン統合の後にあまり多くのことがなかった場合もありました。90年代後半にケントがスイスで取り組んでいたシステムを私に見せてくれたことを覚えています。そのシステムでは、毎日自動的に本番環境にデプロイしていました。しかし、これはSmalltalkシステムであり、本番デプロイのための複雑な手順はありませんでした。2000年代初頭のThoughtworksでは、本番環境へのパスがより複雑になる状況がしばしばありました。これが、継続的インテグレーションを超えた、そのパスに対処する活動があるという概念につながりました。その活動は、継続的デリバリーとして知られるようになりました。
継続的デリバリーの目的は、常に最新のビルドをリリースできる状態に製品を保つことです。これは本質的に、本番環境へのリリースをビジネス上の意思決定とすることを保証するものです。
最近では、多くの人々にとって、継続的インテグレーションは開発チームの環境におけるメインラインへのコード統合を意味し、継続的デリバリーは本番リリースに向かう残りのデプロイメントパイプラインを意味します。継続的デリバリーを継続的インテグレーションを包含するものとして扱う人もいれば、緊密に連携したパートナーとして捉え、CI/CDという頭字語を用いる人もいます。継続的デリバリーは単に継続的インテグレーションの同義語に過ぎないと主張する人もいます。
継続的デプロイメントはこれらすべてにどのように適合しますか?
継続的インテグレーションは、誰もが少なくとも毎日、バージョン管理のメインラインにコードを統合することを保証します。継続的デリバリーは、誰かが望むときにいつでも製品をリリースできるようにするために必要なステップを実行します。継続的デプロイメントは、デプロイメントパイプラインのすべての自動テストに合格した場合、製品が自動的に本番環境にリリースされることを意味します。
継続的デプロイメントでは、継続的インテグレーションの一環としてメインラインにプッシュされたすべてのコミットは、デプロイメントパイプライン内のすべての検証が成功した場合、自動的に本番環境にデプロイされます。継続的デリバリーは、これが可能であることを保証するだけです(したがって、継続的デプロイメントの前提条件となります)。
プルリクエストとコードレビューはどのように処理しますか?
プルリクエスト(GitHubの成果物)は、現在、ソフトウェアプロジェクトで広く利用されています。基本的には、メインラインへのプッシュにプロセスを追加する方法を提供します。通常は、事前統合コードレビューが含まれ、プッシュをメインラインに受け入れる前に他の開発者の承認が必要になります。プルリクエストは、主にオープンソースプロジェクトでのフィーチャーブランチの文脈で開発され、プロジェクトのメンテナーが貢献がプロジェクトのスタイルと将来の意図に適切に適合していることを確認できるようにします。
事前統合コードレビューは、通常、統合プロセスに大きな摩擦を加えるため、継続的インテグレーションにとって問題となる可能性があります。数分以内に完了できる自動化されたプロセスの代わりに、コードレビューを行う人を見つけ、彼らの時間をスケジュールし、レビューが受け入れられるまでフィードバックを待たなければなりません。組織によっては数分でフローに乗せることができるかもしれませんが、数時間または数日かかることも容易にあり得ます。これは、継続的インテグレーションを機能させるためのタイミングを崩すことになります。
継続的インテグレーションを行う人々は、コードレビューがワークフローにどのように適合するかを再構築することで、これに対処します。ペアプログラミングは、コードが書かれると同時に継続的なリアルタイムコードレビューを作成し、レビューのためのフィードバックループを大幅に高速化するため、人気があります。Ship / Show / Askプロセスは、チームが必要な場合にのみブロッキングコードレビューを使用することを推奨しており、統合後のレビューは統合頻度を妨げないため、より良い選択肢であることが多いと認識しています。多くのチームは、リファインメントコードレビューが健全なコードベースを維持するために重要な力であると考えていますが、継続的インテグレーションがリファクタリングに適した環境を生み出す場合に最適に機能します。
事前統合レビューは、つながりの弱い開発者から即興的に貢献が現れるオープンソースの状況から生まれたことを忘れてはなりません。その環境で有効なプラクティスは、緊密に連携したフルタイムのスタッフチームのために再評価する必要があります。
データベースはどのように処理しますか?
データベースは、統合頻度を上げると、特定の課題を提示します。バージョン管理されたソースにデータベーススキーマ定義とテストデータ用のロードスクリプトを含めるのは簡単です。しかし、これは、本番データベースなど、バージョン管理外のデータには役立ちません。データベーススキーマを変更する場合は、既存のデータの処理方法を知る必要があります。
従来のリリース前統合では、データ移行は大きな課題であり、移行を実行するためだけに特別なチームが立ち上げられることがよくありました。一見すると、高頻度統合を試みると、維持できないほどのデータ移行作業が発生するでしょう。
しかし実際には、視点を変えることでこの問題は解消されます。私たちはThoughtworksでの初期のプロジェクトで継続的インテグレーションを使用している際にこの問題に直面し、同僚のPramod Sadalageが開発した進化的データベース設計アプローチに移行することで解決しました。この方法論の鍵は、データベーススキーマとデータを、データベーススキーマとデータを変更する一連の移行スクリプトを通じて定義することです。各移行は小さいため、推論とテストが簡単です。移行は自然に構成されるため、数百の移行を順番に実行して、重要なスキーマ変更を実行し、データを移行することができます。これらの移行をアプリケーションのデータアクセスコードと同期してバージョン管理に保存することで、ソフトウェアの任意のバージョンを、正しいスキーマと正しく構造化されたデータで構築できます。これらの移行は、テストデータと本番データベースで実行できます。
最終的な考え
ほとんどのソフトウェア開発は、既存のコードの変更に関するものです。コードベースに新しい機能を追加するためのコストと応答時間は、そのコードベースの状態に大きく依存します。粗雑なコードベースは、修正がより困難で、コストもかかります。粗雑さを最小限に抑えるために、チームはコードを定期的にリファクタリングし、変化するニーズを反映し、チームが製品に取り組むことから学ぶ教訓を取り入れるために、その構造を変更できる必要があります。
継続的インテグレーションは、このような進化的設計エコシステムの重要な構成要素であるため、健全な製品にとって不可欠です。自己テストコードと共に、それによってサポートされながら、リファクタリングの基盤となります。これらの技術的なプラクティスは、エクストリームプログラミングで同時に生まれ、チームが変化するニーズと技術的な機会を活用するために、製品の定期的な機能強化を提供することを可能にします。
さらに読む
このようなエッセイでは、ごく一部の範囲しかカバーできませんが、これは重要なトピックであるため、より多くの情報を示すために、私のウェブサイトにガイドページを作成しました。
継続的インテグレーションをより詳しく調べるには、Paul Duvallのこのテーマに関する適切なタイトルの本(Jolt賞を受賞しました - 私が達成したことよりも多い)をご覧ください。継続的デリバリーのより広範なプロセスについては、Jez HumbleとDave Farleyの本をご覧ください - こちらも私を抑えてJolt賞を受賞しました。
私のソースコードブランチ管理のパターンに関する記事では、より広い文脈を見ており、継続的インテグレーションがブランチ戦略の選択というより広い意思決定空間にどのように適合するかを示しています。いつブランチするかを選択する動機は、統合することを知ることです。
継続的インテグレーションに関する元の記事では、2000年にMattがThoughtworksプロジェクトで継続的インテグレーションをまとめるのに貢献したときの私たちの経験について説明しています。
私が先に示したように、多くの人々は「トランクベース開発」という用語を使用して継続的インテグレーションについて書いています。Paul Hammantのウェブサイトには、多くの役立つ実用的な情報が含まれています。Clare Sudberyは最近、O'Reillyを通じて入手可能な有益なレポートを執筆しました。
謝辞
まず第一に、Kent Beckと、クライスラー包括的報酬(C3)プロジェクトの多くの同僚に感謝します。これは、意味のある量のユニットテストを用いた継続的インテグレーションを実際に目にする最初の機会でした。それは私に何が可能かを示し、長年にわたって私を導くインスピレーションを与えてくれました。
Atlasで継続的インテグレーションを構築および維持してくれたMatt Foemmel、Dave Rice、および他のすべての人々に感謝します。そのプロジェクトは、大規模なCIの兆候であり、既存のプロジェクトにもたらしたメリットを示していました。
Paul Julius、Jason Yip、Owen Rodgers、Mike Roberts、その他多くのオープンソース貢献者は、最初のCIサービスであるCruiseControlのいくつかのバリアントの構築に参加しました。CIサービスは必須ではありませんが、ほとんどのチームは役立つと考えています。CruiseControlおよびその他のCIサービスは、ソフトウェア開発者が継続的インテグレーションを使用することを普及させ、可能にする上で大きな役割を果たしました。
2023年秋、Michael Lihsが記事への修正案をメールで送ってくれ、それが大幅な見直しのきっかけとなりました。Birgitta Böckeler、Camilla Crispim、Casey Lee、Chris Ford、Clare Sudbery、Evan Bottcher、Jez Humble、Kent Beck、Kief Morris、Mike Roberts、Paul Hammant、Pete Hodgson、Rafael Detoni、Rouan Wilsenach、Trisha Geeがこの改訂をレビューし、コメントしました。
私がThoughtworksで働いている理由の1つは、才能のある人々が行った実践的なプロジェクトへの優れたアクセスを得るためです。私が訪れたほとんどすべてのプロジェクトが、継続的インテグレーションに関する興味深い情報を与えてくれました。
大幅な改訂
2024年1月18日:改訂版を公開
2023年10月18日:最新の状態にするための書き直しを開始
2006年5月1日:アプローチの説明を明確にし、最新の状態にするための記事の完全な書き直し
2000年9月10日:オリジナル版公開。