Feature Flagを利用したリリース戦略

黒曜
@kokuyouwind

$ whoami

  • 森 俊介 / 黒曜

  • @kokuyouwind

  • 株式会社Misoca

  • SRE

本番リリース

スモールリリース

  • アジャイルプラクティス

  • 小さく、高頻度にリリースする
    • 素早く価値を届けられる

    • フィードバックを速く得られる

開発メリット

  • 変更粒度が小さくなることで……

    • コードレビューしやすくなる

    • コードが衝突しづらくなる

    • 問題の原因特定がしやすくなる

😆

とはいえ…

  • 現実にはそう簡単にいかないときもある

    • プレスリリース合わせで全部出す

    • ちょっとずつ出すとデータ不整合

    • 一揃いの機能がないとUXが悪い

    • etc...

🤔

ビッグバンリリース

FeatureFlag

  • 機能を動的にOn/Offできる仕組み

    • ある環境でだけ有効にする

    • あるユーザにだけ有効にする

    • 管理者が全体の有効/無効を切り替える

    • etc...

FeatureFlagのメリット

  • 一般ユーザに見せないコードを入れられる

    • 一般公開と別の単位でリリースできる

    • 公開前の機能を本番で試せる

  • 様々な目的に利用できる

    • カナリヤリリース

    • A/Bテスト

    • etc...

というわけで
FeatureFlagの話をします

アジェンダ

  • FeatureFlagの基本と分類

  • MisocaでのFeatureFlagの使い方

  • まとめ

アジェンダ

  • FeatureFlagの基本と分類

  • MisocaでのFeatureFlagの使い方

  • まとめ

FeatureFlag

if feature_enabled?
  process_with_feature
else
  process_without_feature
end
  • 機能を動的にOn/Offする仕組み

    • 真偽値を返す関数とif文の組み合わせ

基本はこれだけ

feature_enabled?

  • システム全体で共通

    • デプロイ時

    • ホットリロード

  • リクエストごとに判定

    • 特定ユーザにだけ機能を有効化

    • 特定割合のユーザに機能を有効化

    • 特定属性のユーザに機能を有効化

feature_enabled?

  • 設定管理の方法は色々

    • 環境変数

    • 設定ファイル

    • KVS

    • クラウドサービス

    • etc.

FeatureFlagの分類

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

FeatureFlagの分類

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

リリーストグル(Release Toggls)

  • リリース前機能を隠すための分岐

    • ​不完全なコードのデプロイ

    • デプロイとリリースの分離

  • ​​短期(リリースしたら消される)

  • 静的(環境全体の設定)

FeatureFlagの分類

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

実験トグル(Experiment Toggls)

  • 実ユーザで機能を実験するための分岐

    • マルチバリエイト分析
    • ​A/Bテスト
  • ​​​短期〜中期(実験期間による)

  • 動的(ユーザコホートごとに振り分ける)

FeatureFlagの分類

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

運用トグル(Ops Toggles)

  • 性能をコントロールするための分岐

    • パフォーマンス影響を見て機能デグレさせる
    • ​過負荷時の高負荷機能オフ
  • ​​​中期〜永続(上記のいずれの目的かによる)

  • 静的(環境全体の設定)

FeatureFlagの分類

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

許可トグル(Permission Toggls)

  • 特定のユーザに提供機能を変える分岐

    • プレミアム機能
    • α テスト / β テスト
  • ​​​長期〜永続

  • 動的(リクエストごとに振り分ける)

同じバケツで管理しない

  • すべてのFeatureFlagを同じ仕組みで管理するのは危険な道

    • ​​​カテゴリごとに異なる設計を要する

    • 同じ方法で管理すると、将来の痛みにつながる

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

個人的見解

  • 短期的なFeatureFlagでの静的/動的の別はさほど重要ではない

    • ​柔軟に管理できたほうが便利

      • ​内部ユーザでの動作確認

      • カナリヤリリース

    • ​短いスパンで消えるコードなので管理上のつらさも表面化しない

アジェンダ

  • FeatureFlagの基本と分類

  • MisocaでのFeatureFlagの使い方

  • まとめ

MisocaでのFeatureFlag

Feature Toggles (aka Feature Flags) / Pete Hodgson https://martinfowler.com/articles/feature-toggles.html

主にこのへん

MisocaでのFeatureFlag

  • リリーストグル

    • リリースせず開発期間が長期に渡る機能

    • 時間合わせでのリリースが必要な機能

  • 実験トグル・Opsトグル

    • パフォーマンス改善
      (A/Bテスト、カナリヤテストを兼ねる)

実装

  • LaunchDarklyを検討したが見送り

    • 高機能だが費用が結構かかる
    • ​機能を使いこなせなさそう
  • ​​​Redisをバックエンドに自作

    • rollout gemとほぼ同じ仕組みに落ちついた
    • 2ファイル200行弱

設定と分岐

# initialize
Misoca.feature_flag = Misoca::FeatureFlag.new
Misoca.feature_flag.add_target(
  :new_feature,
  'なんかすごい新機能'
)

# use
if Misoca.feature_flag.enabled?(:new_feature, current_user)
  Misoca::UseCase::NewFeature.new.call
else
  Misoca::UseCase::CurrentFeature.new.call
end

ユーザごとの有効化

class Misoca::FeatureFlag::Target
  def enable_for_user(user)
    redis.sadd(user_key, user.id)
  end
end

Misoca.feature_flag.enable_for_user(user)

feature_flag:new_feature:users
(有効化したユーザidの集合)

42

123456

114514

user id: 123456

redis.sadd
123456

比率での有効化

feature_flag:new_feature:segments
(有効化したユーザid下2桁の集合)

00

12

33

redis.sadd 04 87

04

87

例: 有効化率を3% から 5% に

元々含まれない
2桁の数を2つ選ぶ
(3%から5%なので+2)

04

87

比率での有効化

class Misoca::FeatureFlag::Target
  def enable_percentage(percent)
        current_segments = redis.smembers(segment_key)
        unused_segments = ALL_SEGMENTS - current_segments
        diff_percent = percent - current_segments.count

        diff_percent.positive?
          ? redis.sadd(segment_key,
             unused_segments.sample(diff_percent))
          : redis.spop(segment_key, diff_percent.abs)
  end
end

Misoca.feature_flag.enable_percentage(50)

有効判定

有効化したユーザidの集合

42

123456

114514

user id: 123456

redis.sismember 123456

有効化したユーザid下2桁の集合

00

12

33

redis.sismember 56

04

87

運用

  • 内部ユーザ向けページから有効状況を見れる

  • フラグの切り替え

    • ​内部ユーザ向けページ(自分のみ)

    • Thorタスク実行(比率指定)

実用例

  • 軽減税率対応

  • 電卓機能

  • PDFのS3キャッシュ

軽減税率対応

軽減税率対応

  • スモールリリースの難しい機能

    • 影響範囲が広く、作業量が多い
    • ​プレスリリース合わせでの有効化
  • ​​​リリーストグルを使用

    • コードを適宜masterにマージ
    • 内部ユーザはできたところから本番で動作確認
    • フラグを外して一般リリース

電卓機能

電卓機能

  • ヘルプ更新のため、時間合わせリリース

    • コード量自体はそこまで大きくない​
  • ​​​リリーストグルを使用

    • 事前に内部ユーザで動作確認

    • デプロイせず設定変更のみでリリース

      • ​デプロイ絡みでのトラブルの心配がない

PDFのS3キャッシュ

PDFのS3キャッシュ

  • PDF生成が重いため、S3に2次キャッシュ

    • 初回PDF生成時にS3へ書き込み​
    • 1次キャッシュがない場合、S3から取得
  • ​​​どの程度改善するか見積りづらい

    • S3へのwrite処理が増える

    • S3 Readの重さによっては改善しない可能性も

PDFのS3キャッシュ

10%のユーザにリリース→APMで効果測定

PDF Render: 800ms

S3 PUT: 94ms

S3 GET(miss): 40ms

S3 GET(hit): 72ms

生成時に+134ms,
読み込み時に728ms改善

いける!

利用者の声

富山県 30代会社員
H.Mさん

デプロイとリリースが競合しないので
予想外のトラブルを避けられますし、
ロールバックも本当に簡単でした。
分岐処理を後から徐々に外せるのも良いですね。

不満は一切ないです。
FeatureFlag最高!

※効果には個人差があります

気をつけること

  • 汎用のフラグを使わない

    • FeatureFlagを外すときに大変
    • 一緒に有効にしたくない機能まで有効になる
    • ​新しいフラグを簡単に足せると良い
  • ​​​リリースしたらきちんとフラグを消す

    • うっかりすると無限にフラグが増える

    • うっかりするとリリース前に巻き戻る

気をつけること2

  • パフォーマンス影響をチェックする

    • 軽いバックエンドとアルゴリズムにする
      • Redis setはRWともO(1)、1~2ms程度
    • 繰り返し判定しない
      • 1msでも100回呼んだら100msになる
      • 判定結果をキャッシュすると良い

アジェンダ

  • FeatureFlagの基本と分類

  • MisocaでのFeatureFlagの使い方

  • まとめ

まとめ

  • 大きいリリースはFeatureFlagを使って
    小分けにデプロイすると便利

  • 小さいものでもFeatureFlagを使うと
    デプロイせずにリリースできて便利

  • 簡単にフラグを足せる/消せる仕組みを
    整備するのが必要