ALM Rangers
機能の切り替えを使ったソフトウェア開発
Bill Heys
コード サンプルをダウンロードする
ソフトウェア開発の概念としての機能の切り替えは、並行開発のために機能を分岐する (機能の分岐とも呼びます) 代わりに、機能の並行同時開発を進めることができるようにします。この機能の切り替えは、機能フラグ、機能スイッチ、機能フリッパー、条件付き機能などと呼ぶこともあります。機能の切り替えを行えば、開発中の機能の継続的統合が可能になります。機能の切り替えでは、実行時に個別の機能の非表示、無効化、または有効化が可能です。
あらゆるソフトウェア開発手法と同様、機能の切り替えはバージョン管理 (Microsoft Team Foundation Server など) と組み合わせて使用します。機能の切り替えを使用するだけで、バージョン管理の包括的な計画から分岐がすべて取り除かれるわけではありません。機能の切り替えの特徴として、すべての変更は、開発分岐ではなく、メイン分岐 (メインライン) にチェックインします。
機能は、その機能の開発に着手するまで、すべてのユーザーに対して無効または非表示の状態です。開発中は、その機能を開発と単体テストに対して有効にし、他のユーザーにはすべて無効にすることができます。品質保証 (QA) テストの担当者が、自分がテストする機能を有効にすることも可能です。リリースするソフトウェアでは、その機能が完成し、完了の定義 (DoD: Definition of Done) に従って完全にテストされ、品質テストに合格するまでは、その機能を非表示または無効にしておくことになります。新しいビルドを作成および配置しなくても、機能の切り替えの値を動的に変更できます。
本コラムでは、まず、機能の分岐と機能の切り替えを比較し、機能の切り替えをコードに実装するテクニックをいくつか紹介します。サンプルに Windows の電卓アプリケーションを使用して、機能の切り替えを使って実行時に機能の非表示と表示を切り替える方法を示します。
継続的インテグレーション
機能の切り替えを使えば、継続的インテグレーションが可能になります。つまり、すべての開発作業をすべてメイン分岐にチェックインするため、メインからの分岐の他のすべてのコードと継続的に統合できます。メイン分岐のコードは、チェックインのたびに、自動ビルド確認テスト (BVT) を使用してビルド サーバーでビルドおよびテストされます。
継続的な配信について説明している Microsoft patterns & practices の書籍 (「Team Foundation Server 2012 によるリリース パイプラインの構築」、英語、2013 年) によると、「継続的な配信では、バージョン管理、継続的インテグレーション、自動化、環境管理などのテクニックを使うことにより、アイデアを思いついてから、それを実際にソフトウェアとして製造するまでの時間を短縮できることを意味します」 (bit.ly/1kFV0Kx、英語)。
自動単体テストを使用して、メイン分岐にチェックインする前に、コードをテストできます。チェックインすることでビルド サーバーでビルドできなくなる場合は、コードを修正しなければチェックインは許可されません。ビルドに含まれる機能が QA テストに合格しない場合は、合格するまで配置時またはリリース時にその機能を非表示にしておきます。
機能の切り替えにより、機能が完成し、テストされ、リリースの準備が整うまで、開発中の機能を実行時に分離することができます。機能の切り替えと継続的インテグレーションとを組み合わせて使用すると、メイン分岐で直接作業できます。コードはメイン分岐にチェックインしますが、このメイン分岐はビルドや配置に対して安定した状態に保たれます (図 1 参照)。
図 1 機能の切り替えによる並行開発の継続的インテグレーションのサポート
機能の切り替えの使い方
図 2 は、サンプルの Windows 電卓フォームで機能の切り替えの使用例です。この例からは、機能の同時並行開発の課題もわかります。
図 2 3 つの新機能を示すサンプルの Windows 電卓
この例を使って、機能の分岐を使用した場合と機能の切り替えを使用した場合の並行開発の管理方法を比較します。基本の電卓を配置するときに、3 つの新機能 (キーパッド、高度な関数、およびメモリ関数) を並行して開発します。
機能の切り替えのパターン
(保留中のコードや不完全なコードなど) すべてのコードの継続的インテグレーションを可能にする、機能の切り替えの使用パターンはたくさんあります。機能の切り替えを使用することで、並行開発の分岐の必要性とその結果生じる分岐とマージの作業が軽減または削除されるため、チームの開発スピードが上がります。並行開発に関連する分岐やマージの作業は、時間がかかり、エラーが発生しやすいものでした。
機能の切り替えを検討すべき代表的なシナリオを以下に示します。
- UI の新機能を非表示または無効にする
- アプリケーションの新しいコンポーネントを非表示または無効にする
- インターフェイスのバージョン管理を行う
- インターフェイスを拡張する
- 複数バージョンのコンポーネントをサポートする
- 新機能を既存のアプリケーションに追加する
- 既存のアプリケーションの既存の機能を強化する
機能を分離するための分岐
機能の分岐手法では、チームは特定の機能 (または機能のグループ) の同時開発や並行開発を独立した機能の分岐に分離します。機能を分岐することで、リリースを運用環境に移行するタイミングを柔軟に決定できます。すべてを配置するのに、全機能の準備が整うのを待つ必要がありません。
DoD に従って機能の完成が認められると、個別の機能または機能のグループをメイン分岐にマージします。必要に応じて新しい機能の分岐を作成し、機能が完成したら削除します。各機能は DoD を満たすまで、メイン分岐にマージされません。以下の図 3 は、並行開発中に 3 つの新機能を分離する機能の分岐の例を示しています。
図 3 機能を分離するための分岐
機能の分岐のさまざまなシナリオの詳細については、分岐の手法に関する ALM Rangers の電子書籍を参照してください (aka.ms/vsarsolutions、英語)。
機能の切り替えと機能の分岐を比較する
機能の切り替えを使用すると、開発者や開発チームは継続的インテグレーション、継続的配置、および継続的リリースの各手法を実践できるようになります。このような各手法では、機能を並行開発するため、機能の分岐に比べると頻繁に機能コードの統合を行うことになります。同時に、機能の切り替えは実行時の機能の分離もサポートします。分離の対象は、リリースの準備が整った機能ではなく、開発中の機能です。
機能の切り替えでは、保留中の変更はすべてメイン分岐にチェックインします。自動ビルド プロセスでビルド サーバー上のメイン分岐のコードがすべてビルドされ、自動 BVT が正常に実行されるまで、各チェックインは保留状態のままです。このプロセスを継続的インテグレーションと呼びます。
機能の切り替えでは、ビルド内でリリースの準備が整っていない機能は非表示にするか、バイパスします。機能の実装方法によっては、機能の切り替えを使用すると作業が非常に複雑になる場合があります。ただし、UI の機能を非表示にするのは簡単です。
機能の分岐を使用するときは、開発した機能をすべて関連する機能の分岐にチェックインするため、メイン分岐のコードと他の機能分岐のコードは分離されます。新機能や強化機能は、機能のコードが完成し、必要な品質テストに合格して DoD を満たすまで、メイン分岐や他の機能分岐とマージすることはできません。上記の条件を満たす場合のみ、機能はメイン分岐に統合 (マージ) されます。以上のことから、コードの分離と継続的インテグレーションは、統合プロセスにおいて正反対の性質を持っていると言えます。
配置した新機能を有効にするのは比較的簡単で、リスクはありません。関連する機能の切り替えを表示して有効になるようにアクティブ化するだけです。これに対して、機能の分岐を使用して新機能をリリースする作業は非常に複雑で、機能をメイン分岐にマージする必要があります。これは時間がかかり、難しい処理になるのがほとんどです。
機能の削除
プロジェクトによっては、不完全な機能を削除するか取り消す必要が生じる場合があります。個々の変更セットをロールバックして機能をロールバックする作業 (必要な変更だけを行う作業) も複雑で手間がかかり、大幅なコードの変更が必要になることもあります。
機能はリリースするまで、必ずメイン分岐から分離しておく必要があります。いずれにしても、リリース スケジュールの直前に機能を取り除くのは、リスクを伴い、エラーが発生する原因になります。機能の切り替えを使用する場合は、すべての機能のすべてのコードをメイン分岐にチェックインします。そのため、実行時またはリリース時に、特定の機能を非表示または無効にするだけです。
実装の選択肢
アプリケーションの機能の切り替えの状態の保存にはいくつか方法があります。ここでは、非常に簡単な方法を 2 つ紹介します。
- アプリケーションの設定を使用して機能の切り替えを定義する (アプリケーションのビルド時に、XML アプリケーション構成ファイルで配置する)。
- データベース テーブルの行として機能の切り替えを保存する。
機能の切り替えを定義する: 設定デザイナーを使用すれば、構成ファイルで機能の切り替えを簡単に定義できます。アプリケーションの終了時に機能の切り替えの状態を保存するために、アプリケーションで実行することは何もありません。図 4は、機能の切り替えを定義するサンプルの設定ファイルです。
図 4 Windows 電卓の機能の切り替え状態を保存するサンプル ConfigSettings ファイル
機能の切り替えをデータベースの行として保存する: データベースのテーブルを使用して機能の切り替え状態を保存するには、アプリケーションに 2 つの設定ファイルが必要になります。1 つ目の CommonSettings 設定ファイルでは、アプリケーションが使用するオプションを定義します。2 つ目の DatabaseSettings 設定ファイルでは、機能の切り替えをデータベース テーブル (True の場合) または設定ファイル (False の場合) のいずれで定義および保持するかを制御します。FeatureToggleSettings ファイルには、データベースに保存する各機能の切り替えの名前を指定します (図 5 参照)。
図 5 データベース テーブルに行として保存される機能の切り替えの名前
サンプルの Windows 電卓では、機能の切り替えの行を動的に作成する方法を使用しています。アプリケーションを初めて実行するときは、テーブルは空の状態です。実行時に、機能の切り替えの名前をすべて確認し、有効になっているかどうかを判断します。テーブルにない機能の切り替えは、アプリケーションがテーブルに追加します。図 6 のサンプル FeatureToggle テーブルは、機能の切り替え行がすべて追加された状態を示しています。
図 6 初回実行後の FeatureToggle テーブル
実装に依存しないという考え方
機能の切り替えを使用して機能を無効にすると、アプリケーションのコードは、その機能に関連付けられている機能の切り替えの値 (True/False) に基づいて、コントロールを非表示または無効にします。開発中の機能の UI で、その機能を完全に非表示にするか、無効な機能として表示するかは選択できます。機能のリリース時に、その機能を表示し有効にします。
図 7は、キーパッド機能のみを表示し有効にしている Windows 電卓です。メモリ関数と高度な関数の機能は表示していますが、無効にしています。
図 7 キーパッド機能のみ表示および有効にし、メモリ関数と高度な関数の機能を表示および無効にしている Windows 電卓
このサンプル アプリケーションでは、まず、新しい FeatureToggleDemo データベースを作成します。次に、シンプルな FeatureToggle テーブルを作成します。サンプル アプリケーションをダウンロードすると、FeatureToggle データベースを作成する SQL ファイル、FeatureToggle テーブルを作成する SQL ファイル、アプリケーションで値を読み込んだり、FeatureToggle テーブルに値を保存し直すために使用する 2 つのストアド プロシージャを作成する SQL ファイルの 3 つのファイルを確認できます。
FeatureToggle テーブルを作成後、初めてアプリケーションを実行する前に FeatureToggle 行を追加する必要はありません。アプリケーションでは FeatureToggle クラス ライブラリを使用して、機能の切り替えへのアクセスや更新のアプリケーション ロジックをすべてカプセル化しています。
次のコードは、機能の切り替え状態を保持するために使用するプライベート フィールドを示します。
private Boolean _IsKeyPadFeatureEnabled = false;
private Boolean _IsKeyPadFeatureVisible = false;
private Boolean _IsMemoryFeatureEnabled = false;
private Boolean _IsMemoryFeatureVisible = false;
private Boolean _IsAdvancedFeatureVisible = false;
private Boolean _IsAdvancedFeatureEnabled = false;
図 8 のコードは、ConfigSettings ファイルに保存されている機能の切り替えの値からプライベート フィールドを設定する方法を示しています。
図 8 ConfigSettings ファイルに保存されている機能の切り替えの値
private void GetFeatureTogglesFromSettings()
{
_IsKeyPadFeatureEnabled =
Properties.ConfigSettings.Default.KeyPadFeatureEnabled;
_IsKeyPadFeatureVisible =
Properties.ConfigSettings.Default.KeyPadFeatureVisible;
_IsMemoryFeatureEnabled =
Properties.ConfigSettings.Default.MemoryFeatureEnabled;
_IsMemoryFeatureVisible =
Properties.ConfigSettings.Default.MemoryFeatureVisible;
_IsAdvancedFeatureEnabled =
Properties.ConfigSettings.Default.AdvancedFeatureEnabled;
_IsAdvancedFeatureVisible =
Properties.ConfigSettings.Default.AdvancedFeatureVisible;
tbMode.Text = Constants.configSettings;
}
図 9 のコードは、FeatureToggle データベース テーブルに保存されている機能の切り替えの値からプライベート フィールドを設定する方法を示しています。
図 9 データベース テーブルに保存されている機能の切り替えの値からプライベート フィールドを設定
private void GetFeatureTogglesFromStore()
{
_IsKeyPadFeatureEnabled = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.KeyPadFeatureEnabled);
_IsKeyPadFeatureVisible = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.KeyPadFeatureVisible);
_IsMemoryFeatureEnabled = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.MemoryFeatureEnabled);
_IsMemoryFeatureVisible = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.MemoryFeatureVisible);
_IsAdvancedFeatureEnabled = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.AdvancedFeatureEnabled);
_IsAdvancedFeatureVisible = FeatureToggles.FeatureToggles.IsEnabled(
Properties.FeatureToggleSettings.Default.AdvancedFeatureVisible);
tbMode.Text = Constants.databaseSettings;
}
図 10のコードは、アプリケーションでラッパー メソッドを使用して、機能の切り替えを評価し、Windows 電卓機能の Visible プロパティおよび Enabled プロパティを設定する方法を示しています。
図 10 ラッパー メソッドを使用して Windows 電卓の Visible プロパティおよび Enabled プロパティを設定
private void EvaluateFeatureToggles()
{
this.tbVersion.Text = Properties.CommonSettings.Default.Version;
if (Properties.CommonSettings.Default.DatabaseSettings)
{
GetFeatureTogglesFromStore();
}
else
{
GetFeatureTogglesFeatureToggleFromSettings();
}
if (_IsAdvancedFeatureEnabled)
{
_IsAdvancedFeatureVisible = true;
}
SetAdvancedFeatureEnabled(_IsAdvancedFeatureEnabled);
SetAdvancedFeatureVisible(_IsAdvancedFeatureVisible);
}
次のコードは、高度な関数機能に関連付けられている UI 要素を非表示または表示するラッパー関数を示しています。
private void SetAdvancedFeatureVisible(bool state)
{
this.btnBkspc.Visible = state;
this.btnSqrt.Visible = state;
this.btnPercent.Visible = state;
this.btnReciprocal.Visible = state;
this.btnPlusMinus.Visible = state;
}
Windows 電卓では、FeatureToggle テーブルに関連付けられているすべての処理をカプセル化するために、再利用可能なクラス ライブラリを利用します。このクラス ライブラリを使用すると、ConfigSettings ファイルを使用するコードは、FeatureToggle データベース テーブルを使用するコードとほぼ同じになります。
まとめ
機能の切り替えのメリットは、新機能や強化機能をメイン分岐にチェックインするため、ビルド時に既存のコードベースで継続的に統合およびテストできることです。これまで、新機能や強化機能のコードは、通常、リリース期日が迫るまでコードベースに統合しませんでしたが、この方法は困難かつリスクが高く、エラーが起こりやすくなります。機能の切り替えと機能の分岐はどちらも実現可能な方法です。これらの方法を使用して、必要な QA テストに合格し、完成した機能のみ搭載された、安定したリリースを実現できます。機能の切り替えと機能の分岐のどちらを使用するかは、開発者のニーズと実行する開発プロセスによって決まります。
Bill Heys は、設計プロセスとソリューションを専門とするシニア ALM コンサルタントです。以前は、マイクロソフトのシニア ALM コンサルタントでした。現在は、Microsoft Visual Studio ALM Rangers のメンバーであり、Rangers の分岐とマージのガイドの主任開発者を務め、Rangers のバージョン管理のガイドのリリースに投稿者として協力しています。連絡先は bill.heys@live.com(英語のみ) です。
この記事のレビューに協力してくれた技術スタッフの Michael Fourie (独立コンサルタント)、Micheal Learned (マイクロソフト)、Matthew Mitrik (マイクロソフト)、および Willy-Peter Schaub (マイクロソフト) に心より感謝いたします。