私たちは巨大なCおよびC++コードベースを持っています。既存のシステムに新しい機能を追加する計画がありますが、これは3〜4か月後にのみ発生します。
したがって、私たちは可能な限り最良のオプションを探しています。
ifdef
を用意してくださいこの質問はかなりあいまいであり、それに取り組む人々だけが前向きに決めることができます。しかし、私はこのようなケースに関する一般的な意見を探しています。
私はいくつかの役立つヒントを投稿しようとしますが、すべての敬意を表して、これはリリース管理101です。実際にhugeコードベースがあり、組織内で並列ワークストリームが必要な場合は、それについて本を読むか、この分野でより多くの経験を持つ誰かを雇うのはうまくいくでしょう。
この状況を想定すると:
その仮定を無効にすることができれば、あなたの仕事は突然、ずっとずっと簡単になるので、ポイント#4が真であることを絶対に確認してください。 notバグ修正と機能開発を同時に行うだけでは、これまでに節約した時間を多く伝えることはできません。そうは言っても、それは時々不幸な必要性です。
正式には、次のオプションがあります。
機能ブランチ:すべての新機能の開発が行われるブランチを作成します。本質的に継続的インテグレーションを破壊するため、推奨されません。もちろん、機能ブランチの「CIビルド」のクローンを作成できますが、コードは実際には作成されていないため、実際にはactually CIではありませんintegrated。単独でテスト。
機能の切り替え:これは通常notifdef
で行われますが、構成オプション。これをビルド時ではなく実行時に設定できることは、テストとデプロイがたくさん簡単になるため重要です。コンパイル時の切り替えでは、2つの個別のビルドと2つの個別のアーティファクトを維持する必要がありますが、昇格できるのはoneのみであり、あいまいさとプロセスのショートカットを導入する傾向がある状況です。
ランタイム機能の切り替えは部分的に推奨ですが、いくつかの注意点があります:
トグルの表面積を最小限に抑えることが重要です。理想的には、チェックが1か所だけで行われるようにします。ブリッチに対して大きすぎる機能トグルは、 シングルトンと他のグローバル状態が であるのと同じ理由で悪です。アプリケーションの起動時に一度だけ確認してください。
機能の切り替えが多すぎると、時間の経過とともに独自のスパゲッティの混乱になり、基本的にプログラム内のプログラムが作成されます。機能Zは、機能Xに依存する機能Yに依存しますが、not非推奨であり、機能ABを壊します。トグルのnumberを最小限に抑え、トグル間の依存関係を適切に管理します。依存関係がない場合でも、本番環境で使用される可能性が高い各状態ですべてのエンドツーエンドテストを実行する必要があります。これは、テスターとリリースマネージャーにとってすぐに 組み合わせ爆発 になります。
パラメータ化ビルドまたは単に複数のターゲット:これは信じられないほど便利なツール/テクニックですが、私はneverが別のfeature setsをビルドするために使用されているのを見てきたため、ここではオプションとして推奨できません。通常は、さまざまなplatformsのビルドや、統合テストの実行、ドキュメントの生成などの特定の1回限りのタスクを実行するために使用します。貧弱な人の機能トグルとして使用すると、すべての機能トグルの潜在的な欠点andビルドスクリプトのすべての複雑さを隠します。これは一般にleast柔軟であり、すべてのコードのテストが最も困難です。
機能の抽象化、AKA抽象化による分岐 (実際の分岐を含まないため、危険な誤称):これは、新しい機能のすべての興味深い機能を単一のクラスにカプセル化し、リファクタリング手法(特に Extract Interface )を使用してすべてをカプセル化することを意味します既存の機能同じインターフェースの機能。次に、機能を起動する準備ができたら、実装を変更するだけです。
これは、(a)トランクまたはメインラインにいる、(b)単体テストおよび場合によっては統合テストで完全にカバーされている、(c)アプリケーションに新しいグローバル状態を導入しないという利点を兼ね備えています。これは、アプリケーションが依存性注入を使用する場合ははるかに簡単ですが、使用しない場合は不可能ではありません。これはalways可能な場合に最適な選択です。これにより、オーバーヘッドが最小限になり、リスクが最小になり、必要に応じて上記のオプション2または3と非常に簡単に組み合わせることができます。テスターがオンデマンドで利用できる機能。
このオプションを使用する場合、機能が実際に起動されたら、抽象化がまだ必要かどうかを再評価し、抽象化を維持するオーバーヘッドがまだ得ているメリットを上回る場合は、抽象化を削除する必要があります。
オプション4、2、および1は、優先度の高い順に選択する必要があります。これは、上記のオプションのallの使用経験が豊富な人からのものです。
最後の注意:バグの修正または新しい機能の追加はnotリファクタリングです。リファクタリングは「変更」を意味するのではなく、 to preserving既存の機能exactlyコードの変更中。したがって、状況を参照するために使用しないでください。あなたがしていることは、リファクタリングのoppositeです。
オプション2は "Feature Toggle" と呼ばれます。 IMHOは、「機能のエントリポイント」が小さい限り、理想的には1つの#ifdef
、数十ではありません。さらに優れているのは、実行時に切り替えることができる機能トグルです。そのため、新しい機能の現在のステータスをテストするために個別のビルドを実行する必要はありません。この方法により、ソフトウェアの「中間リリース」を簡単に本番環境に導入でき、変更の1つが誤って既存の機能を壊した場合に、以前のフィードバックを提供できます。
オプション1は、2番目に最適なオプションです。小さなエントリポイントがない場合は、より適切に機能します。現在の製品バージョンにバグが発生するリスクが低いという利点がありますが、通常はマージの労力が大きくなるという欠点があります。さらに、2つのブランチを並行してビルドして保守する必要があります。
あなたの場合、オプション3がどの程度うまく機能するのかわかりません。これはオプション1の別の形式ですが、これにより、(同じバージョンのソフトウェア内で)コードが重複する可能性があります。ケース、それをしないでください。
これは私がすることです:
リファクタリングについて、既存の機能を壊さないようにするための最善の方法は、単体テスト、パフォーマンステスト、統合テストなどのテットを記述することです。毎日テストを実行します。また、継続的な統合も良い考えです。