スタディサプリ Product Team Blog

株式会社リクルートが開発するスタディサプリのプロダクトチームのブログです

内製 Feature toggles 基盤 Darklaunch v2 の機能群と改善の進め方

こんにちは、バックエンドエンジニアの @kumackey です。

『スタディサプリ』では、新機能の段階的リリースを支える仕組みとして、内製の Feature toggles 基盤「Darklaunch v2」を運用しています。本記事では、Darklaunch v2 が提供する機能群と改善の進め方について紹介します。

概要

Feature toggles とは

Feature toggles とは、コードのデプロイとリリースを分離する仕組みです。

通常の開発では、コードをデプロイするとそのまま新機能がリリースされます。デプロイとリリースが同時に発生するため、もし問題があった場合はコードを再デプロイして戻す必要があり、all-or-nothing なリスクの高いリリースになりがちです。

Darklaunchなしのrelease。デプロイとリリースが同時に発生する

Feature toggles を導入すると、コードのデプロイと新機能のリリースは独立して行われます。コード上で if 分岐を書いておき、管理画面でトグルを切り替えるだけで制御できます。デプロイなしに任意の粒度で少しずつリリースしたり、問題があればすぐに戻したりできます。

Darklaunchありのrelease。デプロイとリリースが独立している

Darklaunch v2 とは

Darklaunch v2 は、この Feature toggles を実現するための社内サービスです。以前は Rails アプリケーションの Ruby モジュールとして提供されていましたが、現在は Go のマイクロサービスとしてフルスクラッチで再構築されています。

Darklaunch v2 では、リリースを制御する単位を「キー」と呼びます。キーは新機能ごとに作成し、管理画面から設定を変更することでリリースを制御します。

開発者は SDK を使って、アプリケーションコードに if 分岐を書くだけで Feature toggles を利用できます。以下は Ruby SDK の例です。

if DarklaunchV2Client.variation('enable-new-feature', 'User.id', current_user.id, num_retries: 2)
  # 新機能の処理
else
  # 既存の処理
end

variation メソッドは、キー名と identifier(ユーザーを特定する情報)を受け取ります。管理画面での設定に基づいて true または false を返し、この結果で新機能の公開・非公開を制御できます。

variation メソッドの動作

Darklaunch v2 の全体像を以下に示します。

Darklaunch v2 概略図

機能群

ここからは、Darklaunch v2 が提供する機能群を紹介します。それぞれ、どのような課題があり、どう解決しているかを説明します。

リリース済みキーの Slack リマインド

課題

Feature toggles を使った新機能のリリースが完了した後、キーがそのまま放置されてしまうことがあります。リリース済みのキーが残り続けると、以下の問題が発生します。

  • 開発者にとっては、コード上の不要な分岐が技術的負債になる
  • Darklaunch v2 基盤側としては、キー数やリクエスト数が増加し続ける

ちなみに Microsoft Office では約 12,000 もの Feature flags が存在するそうで、キーの放置は広く知られた課題です。

解決策

Argo CronWorkflow で定期実行されるジョブが、30日以上更新がないリリース済みのキーを検出し、Slack で通知します。

リリース済みキーの Slack リマインド通知フロー

通知メッセージには、キー名や前回更新からの経過日数、管理画面へのリンクが含まれます。さらに Claude Code Action によるキーの削除 PR 自動作成リンクも付いています。

Slack 通知の例

リクエスト数と最後にリクエストされた日時

課題

前述のリマインドでキーの放置に気づいた後、実際に削除してよいかの判断材料が必要です。「本当にもう使われていないのか?」が分からないと、削除に踏み切れません。

解決策

Darklaunch v2 API では、variation リクエストのたびに Datadog StatsD でメトリクスを記録しています。管理画面のキー詳細ページには、このメトリクスを利用した 2 つの情報が表示されます。

  1. リクエスト数のグラフ: Datadog の iframe を埋め込み、リクエスト数の推移をグラフで表示
  2. 最後に使われたとき: Datadog API を使って、最後にリクエストがあった時期を算出

管理画面でのリクエスト数と最後に使われたとき

この情報があることで、キーを削除する際に「最後に使われたのが約 3 ヶ月前なら、もう消して大丈夫だろう」と安心して判断できるようになります。

Owner team によるフィルタリング

課題

Darklaunch v2 のキーは組織全体で共有されているため、一覧には全チームのキーが混在して表示されます。キーの数は現在 100 個以上あり、自チーム関連のキーを探すのが大変になっていました。

フィルタ前のキー一覧

解決策

一覧ページに Owner team のドロップダウンフィルターを設けています。チームを選択すると、そのチームが管理するキーだけに絞り込まれます。自チームのキーの状態をすばやく把握でき、他チームのキーに埋もれることがなくなりました。

フィルタ後のキー一覧

Ruby SDK での Fail-safe 機能

課題

Darklaunch v2 はさまざまなサービスで使われており、停止するとサービス全体へ障害が波及しかねません。

Fail-safe が必要な背景

解決策

Ruby SDK では、API へのリクエストが失敗した場合に false を返すことで安全側に倒す設計です。variation() が true を返すと新機能が有効になるため、エラー時に false を返せば「意図せず新機能が出てしまう」事故を防げます。

Ruby SDK の Fail-safe フロー

Ruby SDK でのテストの stub ライブラリ

課題

Darklaunch v2 を利用するアプリケーションのテストを書くには工夫が必要でした。DarklaunchV2Client のメソッドを直接 stub するか、HTTP リクエストを stub しなければなりません。

# DarklaunchV2Client を直接 stub する場合
allow(DarklaunchV2Client).to receive(:variation)
  .with('enable-new-feature', 'User.id', user.id, num_retries: 2)
  .and_return(true)

# HTTP リクエストを stub する場合
stub_request(:get, %r{/variation\?.*key=enable-new-feature})
  .to_return(status: 200, body: { variation: true }.to_json)

引数の順序やクエリパラメータの形式を正確に合わせる必要があり、テストが書きにくいという声がありました。

解決策

stub_darklaunch_v2 ヘルパーメソッドを提供し、テストでの利用を簡単にしています。管理画面での設定を模倣する設計で、キー、always_true、identifiers を指定するだけで stub を設定できます。

# 常に true を返す key を stub
stub_darklaunch_v2('enable-new-feature', true)

# 特定の identifier に対してのみ true を返す key を stub
stub_darklaunch_v2(
  'enable-new-feature',
  false,
  { 'User.id' => ['user-id-1', 'user-id-2'] }
)

always_truetrue の場合はどの identifier でも true を返します。false の場合は identifiers に指定された identifier のみ true を返します。管理画面での設定と同じ概念で stub を設定できるため、直感的にテストを書けます。

内部では DarklaunchV2Client::Mock::Server クラスが管理画面の挙動をシミュレートしています。variation をはじめとするすべてのメソッドに対応しています。

改善の進め方

ここまで紹介してきた機能群は、一度作って終わりではなく、利用者からのフィードバックを受けて継続的に改善してきたものです。ここでは、その進め方を紹介します。

Slack サポートチャンネル

Darklaunch v2 には専用の Slack チャンネルがあり、利用者とのコミュニケーションの場として機能しています。チャンネルでは主に以下の 3 種類のやり取りが行われています。

  • 問い合わせ
  • 要望
  • 機能追加・改善のアナウンス

問い合わせ: 使い方や挙動に関する質問・トラブルシューティングの相談が寄せられます。問い合わせの内容は、UI の改善や FAQ の整備に活かされます。

Slack での問い合わせ

要望: 利用者から「こうしてほしい」という要望が直接届きます。要望をそのまま実装するのではなく、「なぜそれが必要なのか」を掘り下げて本質的な課題を明確にすることを大切にしています。この対話を起点に機能改善が生まれることも多くあります。

Slack での要望

機能追加・改善のアナウンス: 新機能をリリースした際に、チャンネルで周知します。利用者に実際に使ってもらう機会を作ることで、次のフィードバックにつながります。

Slack でのアナウンス

定期的なサーベイと要望の収集

Slack チャンネルでは日常的にフィードバックが集まりますが、声を上げにくい利用者の意見も拾うために、定期的にサーベイを実施しています。満足度の定量評価に加えて、自由記述で具体的な要望や困りごとを集めています。

サーベイの結果

サーベイの回答からは、Slack では出てこなかった意見が見つかることもあります。

まとめ

本記事では、内製 Feature toggles 基盤 Darklaunch v2 の機能群と改善の進め方について紹介しました。

  • リリース済みキーの Slack リマインド: 放置されたキーを定期的に検出・通知し、技術的負債の蓄積を防止
  • リクエスト数と最後にリクエストされた日時: Datadog メトリクスを活用し、キーの削除判断を支援
  • Owner team フィルタリング: 自チームのキーに素早くアクセスできる
  • Fail-safe 機能: エラー時に false を返す安全設計で、基盤の障害がサービス全体に波及することを防止
  • テスト stub ライブラリ: 管理画面の設定と同じ概念でテストを書ける

これらの機能は、Slack サポートチャンネルやサーベイを通じた利用者からのフィードバックを起点に改善を続けています。社内基盤は、作ったら終わりではなく、利用者との対話を通じて継続的に改善していくことが重要です。Darklaunch v2 は、開発者が安心してリリースできる体験を提供し続けるために、今後も改善を続けていきます。