SaaSのためのDevOps SaaS 「Controlle」のご紹介

![ogp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53219/bdd4e4f7-1966-7f85-01aa-53a99b59cdbf.png)
SaaSのためのDevOps SaaS [controlle.io](http://controlle.io) の体験が良かったのでご紹介。
DevOpsと謳っていますが厳密には下記の粒度の支援を行ってくれます。

- Feature management
    - SaaSによくあるアップセル対象になるオプション機能管理
    - プライシングによって変わる利用ユーザー数の管理とか(というかプライシングに関わるもの全て？)
    - プロダクト開発界のStripe, Zuoraみたいなイメージ
- ダークローンチ
- カナリアリリース
- ABテスト(多分beta)

Feature flag(toggle) + managementみたいなイメージです。
具体的な実装は最後のほうに載せてます。
ちなみに管理画面は英語ですが、LPが日本語にも対応しているので期待してもいいかもです。

## ダークローンチ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53219/65a2d190-ad3c-052a-1b61-7ca7b5df8e9f.png)

新機能を本番環境にデプロイ後、
「開発者」や「社内」のようにセグメントを切って、対象者のみに機能を有効にする手法

つまり本番環境でQAができる。

よくあるのがSQLのパフォーマンスチューニングしてステージングでいい結果出たけど
いざ本番環境で動かすとデータの大きさから逆にパフォーマンス悪くなるみたいなやつが事前にテストできる。

本番環境でテスト出来る is 正義。

## カナリアリリース(ロールアウト)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53219/bce0a927-cbe5-5c47-e9fd-0a5f8d9cde8b.png)

カナリアリリースをアプリケーションで制御する最大のメリットは、
新機能を有効にする単位がサーバーじゃなくてユーザー(組織)である事だと思います。
新機能をいきなりARR1000万とかのエンタープライズに解放するの怖いじゃないですか...

Controlleはここの設計が素晴らしいです。

1番伝えたいのはセグメント機能の優秀さ。
既存のfeature flagのライブラリは起点がユーザーなのですが
Controlleは組織、プロジェクト、ワークスペースのように細分化できます。

Slackが導入してるとして例えると、
スレッドの表示の遅さを改善するためにSQLパフォーマンスチューニングして、
そのコードをスレッドID起点にロールアウトできます。
なんならスレッドにコメントが10件以上あるもののみ有効とかもできます。
この辺の小回りの良さがすごく気に入りました。

あとはダイナミックロールアウトという機能もあって、

- リクエストがきたブラウザがchromeなら有効
- IPアドレスがUSなら無効

みたいな割と柔軟な対応もできます。
ちょうど弊社プロダクトでダークテーマを実装しましたが、各ブラウザのテストが不十分
でも早めにユーザーの反応を知りたい！みたいな時に
chromeアクセスユーザーだけに開放しよう！みたいなユースケースが想定できました。

### Feature management
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53219/f4fcd8f9-7314-007e-ef56-10cc2536225b.png)


Controlleの一番の売りです。

ユースケースとしては下記の感じ

- 松竹梅プランがあり、松プラン、竹プランには○○機能を開放
- 竹プランはユーザー数5名まで、松プランは10名まで
- 自社でわざわざAdmin画面作らなくていい、Controlleが代替してくれる

SaaSは毎年プライシングや制限変わることも多いと思います。

プライシング変更時、初期顧客に「値上げしました！」の一言で突破できるならいいのですが、実際はそうとはなりません
これを繰り返してく内にプランが10個になったりしてテーブルの設計やアプリケーションロジックが複雑になっていきます。(弊社で実際ありました... `old_free_plan`, `new_free_plan`...)

これらをControlleに預けると複雑さから開放されます。

またSalesforceやStripeと連携できるみたいなので、CRMを正として各種スペックを定める事もできるようです。(このへんでZuoraっぽさを感じた)

### 導入、実装

client(js)側の導入は充実してて、cdn, npmから利用できます。

`useEnableFeatureはfeature` flagの呼び出しです。
引数にフィーチャーキーとターゲットID(useId, projectId, threadId...)を渡すと
対象のターゲットに対して機能が有効か無効かを返してくれます。

`useTargetはControlle` へのターゲットの追加です。
第1引数にダッシュボードで設定したグループキーを指定します。
例では users ですが、projectsとかteamとかなんでも登録できます。
第2引数でターゲットに対するメタデータを追加できます。
このメタデータを充実させると、ロールアウト時に詳細なセグメントを作成できます。

```tsx
import { LoadControlle } from '@controlle/controlle-js'
import { ControlleProvider } from '@controlle/controlle-react-js'

const controlle = LoadControlle('Your client sdk key')

const App = () => (
  <ControlleProvider controlle={controlle}>
    <Component />
  </ControlleProvider>
)

const Component = () => {
  // useEnableFeature
  const { loading, enable } = useEnableFeature({
    featureKey: 'new-ui',
    target: user.id
  })

  // useTarget
  const { register } = useTarget()

  const handleSignUp = () => {
    register('users', {id: user.id, email: user.email, name: 'LeBron James'})
  }
  
  return (
    <>
      {enable ? <NewUI /> : <OldUI />}
      <button onClick={handleSignUp}>Sign up</button>
    </>
  )
}
```

#### defaultTarget mode
defaultTargetをProviderに渡すことで初期化時に対象のターゲットのfeatureを全てフェッチしてきてくれます。
上記の `useEnableFeature` 使用時のローディング状態がなくなるので、UI的に体験はよくなりそうです。

```tsx
const App = () => {
  const target = {
    groupKey: 'team',
    targetId: 'your_target_id'
  }

  return (
    <ControlleProvider controlle={controlle} target={target}>
      <Component />
    </ControlleProvider>
  )
}
```

#### サーバーサイドSDK
試してないです...
サーバーサイドのSDKはクライアントと違い全てのfeatureの情報を初期化時にフェッチしてきてくれるそうです。

```tsx
import { Controlle } from '@controlle/controlle-node-js'

const controlle = new Controlle('Your server side sdk key')
```

まだまだ書きたい事があるので時間がある時に続編を書きます。