フィーチャーフラグが技術負債になるとき

フィーチャーフラグを使って機能を出荷し始めてから6ヶ月が経ちました。新しい機能ごとに独自のフラグが追加されています。数週間前にテストが完了し、今では全ユーザーに公開されている機能もあります。しかし、フラグはコードに残ったままです。コードベースには、誰も覚えていない条件分岐が散らばっています。新しくチームに加わった開発者がファイルを開くと、5つの異なるフラグチェックが目に入ります。どれがまだ有効で、どれを削除できるのか?誰にもわかりません。

これがフィーチャーフラグの隠れたコストです。プログレッシブデリバリーの強力なツールですが、チームがしばしば無視する有効期限が伴います。安全にリリースできる仕組みが、削除の計画を立てなければ、メンテナンスの負担に変わります。

なぜフラグが蓄積するのか

問題は無邪気に始まります。チームはテスト中に新機能へのアクセスを制御する必要があります。フラグを追加します。機能は動作するので、より多くのユーザーにロールアウトします。そして、次のプロジェクトに移ります。フラグはコードに残ります。なぜなら、削除は即座に利益のない余分な作業に感じられるからです。

時間が経つにつれて、コードベースは使われなくなった条件で埋め尽くされます。設定ファイルは長くなります。プラットフォームのダッシュボードには数十のフラグが表示されますが、その半分は恒久的に有効化されています。すべてのデプロイに不要な複雑さが伴います。何かが壊れたとき、開発者はもはや重要でないフラグロジックを追跡するのに時間を浪費します。

根本原因は単純です。チームはフラグの作成と有効化に集中する一方で、すべてのフラグにライフサイクルがあることを忘れています。フラグは生まれ、目的を果たし、そして消えなければなりません。その最終段階の計画がなければ、フラグはリリースごとに複合する技術負債になります。

フィーチャーフラグのライフサイクル

健全なフィーチャーフラグは明確な段階を経ます。チームが作成を決定した時点から始まります。その瞬間に、誰かがフラグの目的を記録する必要があります。どの機能を制御するのか、誰が切り替えられるのか、いつ削除すべきか。このメタデータはオーバーヘッドに見えるかもしれませんが、数週間後にチームがどのフラグをクリーンアップすべきか判断する際に不可欠になります。

作成後、フラグはロールアウトフェーズを経ます。まず、内部テスト用に機能を有効にします。次に、一部のユーザーに。そして、全ユーザーに。最終段階では、機能はもはや実験的ではありません。アプリケーションの一部です。フラグが存在する理由はありません。

ここでほとんどのチームがつまずきます。「全ユーザー」段階に達すると、フラグについて考えるのをやめます。機能は動作します。チームは次に進みます。フラグはコードに残り、静かに複雑さを増していきます。

次の図は、4つの主要な段階と、フラグを次の段階に進めるアクションをまとめたものです。

flowchart TD Created -->|ロールアウト開始| Active Active -->|ロールアウト完了| Stale Stale -->|クリーンアップ予定| Removed Stale -->|アクションなし| Stale Removed -->|フラグ再有効化| Active

フラグの腐敗を防ぐ2つの実践

フラグの蓄積を防ぐには、スケジュールされたクリーンアッププロセスと、古いフラグを検出する方法の2つが必要です。

開発サイクルにクリーンアップを組み込む

フラグの削除を定期的なワークフローの一部にしましょう。各スプリントの終わりに、2週間以上全ユーザーに有効化されているフラグのリストをレビューします。それらのフラグをコードとフラグ管理プラットフォームから削除します。A/Bテストや未完了の機能のためにまだ必要な場合は、新しい削除予定日でメタデータを更新します。

これは単純に聞こえますが、規律が必要です。このステップを1回のスプリントでスキップしたチームは、次のスプリントでもスキップすることがよくあります。やがて、クリーンアップのバックログは誰も取り組みたくないほど大きくなります。

古いフラグを自動検出する

手動レビューだけでは不十分です。自動検出が必要です。多くのフィーチャーフラグプラットフォームは、一定期間変更またはチェックされていないエントリをフラグできます。プラットフォームがこれをサポートしていない場合は、フラグ設定を読み取り、最終変更タイムスタンプと比較する簡単なスクリプトを作成します。数週間触れられておらず、全ユーザーに有効化されているフラグは、削除の有力な候補です。

さらに進んで、CIパイプラインにリンティングステップを追加するチームもあります。リンターは、設定されたしきい値より長くコードベースに存在するフラグをチェックし、開発者に警告します。これにより、古いフラグが恒久的なものになる前にキャッチできます。

例えば、次のスクリプトはソースコード内のフラグ名を検索し、フラグ管理APIに問い合わせて、全ユーザーに対して恒久的に有効化されているかどうかを確認します。

#!/bin/bash
FLAG_NAME="MY_FLAG"
# ソースコード内の出現回数をカウント
OCCURRENCES=$(grep -r "$FLAG_NAME" src/ --include='*.js' | wc -l)
# フラグ管理APIにステータスを問い合わせ
STATUS=$(curl -s "https://flags.example.com/api/flags/$FLAG_NAME" | jq -r '.status')
# フラグが恒久的に有効で、かつ参照されている場合、警告
if [ "$STATUS" = "permanently_enabled" ] && [ "$OCCURRENCES" -gt 0 ]; then
  echo "WARNING: フラグ $FLAG_NAME は恒久的に有効ですが、$OCCURRENCES 箇所で使用されています。"
  echo "コードと設定から削除を検討してください。"
fi

クリーンアップがコードの衛生管理を超えて重要な理由

フィーチャーフラグのクリーンアップは、コードベースを整理整頓するだけではありません。システムに対するチームの信頼を維持することです。開発者がフラグがまだ使用中かどうか確信が持てないと、削除するのを恐れるようになります。未知の依存関係が壊れるのではと心配します。そのため、フラグは残り続け、設定はより複雑になります。長く残れば残るほど、削除は難しくなります。なぜなら、誰も影響が及ぶ可能性のあるすべての場所を追跡できないからです。

この信頼の低下は現実的な結果をもたらします。新しい機能の実装に時間がかかるのは、開発者が古いフラグロジックを回避しなければならないからです。デバッグは遅くなります。なぜなら、すべての条件分岐を評価する必要があるからです。新しいチームメンバーのオンボーディングは難しくなります。貢献する前に各フラグの履歴を学ばなければならないからです。

クリーンなフラグライフサイクルは、その信頼を回復します。すべてのフラグに既知の目的と計画された削除日があるとき、開発者は見ているコードが重要であると信頼できます。恐れずにフラグを削除できます。古い実験を解読する代わりに、新しい機能の構築に集中できます。

フラグライフサイクル管理の実践的チェックリスト

  • 新しいフラグを作成する際に、目的、所有者、削除予定日を記録する。
  • 各スプリントの終わりにすべてのフラグをレビューする。2週間以上全ユーザーに有効化されているものを削除する。
  • 自動ツールを使用して、最近変更またはチェックされていないフラグを検出する。
  • 設定可能なしきい値より古いフラグについて警告するCIリンティングステップを追加する。
  • 削除日が変更された場合はフラグのメタデータを更新する。認識なしに日付を漂流させない。
  • コードと設定の両方からフラグを削除する。設定に残ったフラグは依然として責任である。

まとめ

フィーチャーフラグは恒久的なものではありません。一時的な足場のように扱いましょう。必要なときに設置し、構造が自立したら取り外します。機能が完全にリリースされた後もコードに残るフラグは、安全網ではありません。死重です。初日から削除を計画すれば、プログレッシブデリバリーパイプラインは、リーンで高速、そして信頼できる状態を保てます。