コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

そーだいさんが来てくれたから小さくリリースすることについて振り返ってみた

こちらの記事はコドモン Advent Calendar 2022の18日目の記事です。

こんにちは! コドモンプロダクト開発部で認証/認可のリプレイスを主に担当しております、エンジニアの関口です。

11/2にそーだいさんにお越しいただき、弊社開発部に向けた熱い激励を頂戴しました。レガシーシステムと向き合うエンジニアにとって非常に深い共感を覚える内容でしたので、今回はいただいたお話の中から特に「小さくリリースせよ」とお話しいただいたことに絞って内容をご紹介させていただきます。あわせて、弊社開発部の取り組みや、これからの課題を率直にお伝えしようと思います。

小さくリリースせよ

以下、そーだいさんの教えを私なりに要約します。

リリースしなければソフトウェアの価値は永久にユーザーには届かない。しかしリリースは怖い。まずはリリースの恐怖を克服しなければならない。そのためのhowはこれである。

  1. 段階リリースを活用する
    • feature-toggleを活用する
    • LBやDNSによる振り分け
  2. 見えないところから小出しにする
    • DBとか、CSSやJS、APIなど、表に出ないものを先にリリースして、最後に表に出るものをお披露目する
  3. 常にロールバックできる道を確保しておく
  4. リリースするものを小さくする
    • スコープを小さくする
    • 今あるものに新たな複雑さを導入しない
    • easyよりsimpleを優先してリリースする

これによりリリースはずっと安心できるものになる。失敗した際に被るダメージは極めて限定的で、コントロールできるものになるからだ。あとは、早く小さく問題を把握して素早くフィードバックを得て、価値のあるリリースを積み重ねるだけである。

弊社の取り組み

以下、お話しいただいた内容の中から、我々の普段の取り組みの実例をご紹介します。

段階リリースを活用する(feature-toggleを活用する)

コドモンでは「利用施設」が重要な単位となりますので、ユーザーが所属する施設単位でfeature-toggleを取得するWeb APIエンドポイントを用意し、それをアプリケーションに埋め込んで利用施設単位での段階リリースをよく行います。feature-toggleをONにしたい施設と機能名をDBのテーブルに登録することで、アプリケーションに埋め込んだWeb APIエンドポイントの応答が変化し、feature-toggleが切り替わる仕組みです。切り戻したい場合は登録したデータをdeleteするだけなのでrevertやdeployも不要ですし、様子を見ながらリリース数を増やすこともできるので、インフラへの負荷やユーザーのリアクションを確認しながら漸進的にリリースすることも可能で、非常に重宝しています。直近の私のチームで利用した例を挙げると、施設の連絡帳入力のweb画面で既存の振る舞いを変えずにAPIの呼び出し本数を削減する対応の中で利用していました。

段階リリースを活用する(LBやDNSによる振り分け)

こちらはまだ実績はないものの、これからリリースしようとしている成果物の中で利用する予定です。もう間もなくリリース予定のPHPのバージョンアップや、将来予定されている保護者向けアプリケーションの認証/認可のリプレイスではこの戦略をとる想定で準備を進めています。feature-toggleと比べて、切り戻しはネットワークやLBの設定変更を行うだけとなりますので、revertもdeployも不要という点では一致しますが、こちらはアプリケーションにfeature-toggleのコードを埋め込む必要がないことが大きな利点になります。一方で冗長化するサーバーの見積りや運用に手間がかかるのと、段階リリースを並列させるとインフラコストを増加させる危険性があることが短所になりそうです。

余談ですが、こちらの手法はAWSのRDS-proxyを導入することにより、つい最近になってようやく弊社でも実現可能となったものです。それまではPHPとApacheの特性に起因するDB接続数の上限がボトルネックとなりwebサーバーの同時稼働台数を増やせない課題に直面していましたが、RDS-proxyによりこのボトルネックが消滅しこの課題は解消しました。SREチームに感謝です。

見えないところから小出しにする

リプレイス後のシフトの開発に携わっていた際に私がよくやっていたのは、フロントのボタンやアンカーなどにfeature-toggleを仕込んでリリースして先にガワだけを確認し、後からAPIを出して、最後にもう一度APIを疎通させるリリースをする、でした。これの嬉しいところはfeature-toggleと組み合わせつつ、本番でユーザーの見えないところで部分部分を確認しながら漸進的にリリースできることだと思っています。

常にロールバックできる道を確保しておく

こちらは弊社の資料室のリプレイスを主導していた開発メンバーが実現への道を切り拓いてくれました。動作検証やDatadogの状況から何か危険を察知した瞬間にGithub Actions一発で指定したtagにロールバックできるようになっています。察知すべき危険は本番での動作確認だけなく、このようにhandbookに明記されています。

リリース後の確認事項

リリースするものを小さくする(スコープを小さくする)

こちらもリプレイス後のシフトの開発でやっていた例を紹介します。ひと月分のシフトをExcelの表で出力する機能を実装した際に、ユーザーストーリーを分割してリリースしていった過程になります。付箋一枚が1リリースです。一つひとつを小さくすることで様々なメリットはありますが、ちゃんと進んでいることが可視化できるし、見積りがより精緻になることが一番のメリットだと感じています。

ユーザーストーリーボードの例

リリースするものを小さくする(今あるものに新たな複雑さを導入しない/easyよりsimpleを優先してリリースする)

この2つのテーマはやや抽象度が高かったので、私なりの理解で肉付けしたものをご説明しようと思います。私の理解ではこの2つは強い関連を持ちます。まず後者のほうを考えるため、以下のマトリックスで図にしてみます。

easy-simple-matrics

この上で思い浮かべてください。今あなたの提供しているサービスでは利用数量*利用単価で利用者への請求料金を計算しています。しかし、3か月後からは新しい会員区分を設けて、特定の会員区分の会員に対して一定の割引をすることに決めました。この機能では、DBから値を取得し顧客へ請求料金を表示するviewまでのすべてが一つのスクリプトに書かれています。どんな実装が考えられるでしょうか?

一番easyに振り切った実装は、現在のスクリプトの請求料金計算をしている箇所に、この計算ロジックを特定の会員区分を判定するif文と一緒にねじ込む方法です。しかしながら、お察しの通りこの実装は今あるものに新たな複雑さを導入します。すべてを一つのスクリプトに書いているので、ユニットテストも難しいでしょう。先の表の4象限の左下に位置するソリューションであることは論をまちません。私の思いつく限りこの方法が正当化されうる状況は、請求料金計算をしているのが一箇所だけであるとか、半年あるいは一年後にサービスの提供が止まることが決まっているなどの時間をかけるだけ無駄というような極めて特殊な状況かと思います。

私が考えるもう少しsimpleに寄せた実装だと、まず請求料金計算のインターフェースを定義し、新旧のロジックを表すそれぞれの実装クラスとそのインスタンスを返すファクトリを用意します。新ロジックは旧ロジックのクラスをラップし、会員区分によって割引を適用する実装がよさそうです。

クラス図

あとは、元のスクリプトの請求料金を計算する最初の箇所の前でこの請求料金計算のインスタンスを生成し、請求計算している箇所を置き換えていくだけです。置き換えとリリースは以下の手順で進めます。

1. 元のスクリプトはそのままに、まずは上のクラス図で実装したクラスを全部リリースする

<スクリプトのイメージ>
// スクリプトは元のまま
$billAmount1 = $unitPrice1 * $quantity1;
// ...省略
$billAmount2 = $unitPrice2 * $quantity2;

2. 元のスクリプトの請求計算にfeature-toggleを埋め込んでリリースする。feature-toggleがonの場合は新しく実装した旧のほうの請求料金計算クラスの計算メソッドを実行し、offの場合は今のスクリプトのロジックをそのまま実行する

<スクリプトのイメージ>
// featureToggleがOnの場合はSimpleCalculatorに移動した旧の計算方法を利用する
$calculator = $factory->simple();
if ($featureToggleOn) {
    $billAmount1 = $calculator->calculate($unitPrice1, $quantity1) ;
} else {
    $billAmount1 = $unitPrice1 * $quantity1;
}

// ...省略

3. 段階リリース
4. 安定稼働が確認できたらfeature-toggleを消して、新しく実装した旧のほうの請求料金計算クラスにロジックを一本化する

<スクリプトのイメージ>
$calculator = $factory->simple();  
$billAmount1 = $calculator->calculate($unitPrice1, $quantity1) ;

// ...省略

$billAmount2 = $calculator->calculate($unitPrice2, $quantity2) ;

5. 再び元のスクリプトの請求計算にfeature-toggleを埋め込んでリリースする。feature-toggleがonの場合は新のほうの請求料金計クラスのロジックを実行し、offの場合は今のスクリプトのロジックをそのまま実行する

<スクリプトのイメージ>
// featureToggleがOnの場合は新しい(会員区分によって割引あり)の計算を適用する
$calculator = $featureToggleOn ? $factory->discount($memberCategory) : $factory->simple();  
$billAmount1 = $calculator->calculate($unitPrice1, $quantity1) ;

// ...省略

$billAmount2 = $calculator->calculate($unitPrice2, $quantity2) ;

6. 検証用のユーザーでfeature-toggleをonにして、本番環境で新ロジックを確認
7. すべてのユーザーでfeature-toggleをonにして、問題なく運用できることを確認(実質的なリリース)
8. 安定稼働が確認できたらfeature-toggleを消して、新のほうの請求料金計クラスにロジックを一本化する

<スクリプトのイメージ>
// 請求料金は会員区分によって割引がある
$calculator = $factory->discount($memberCategory);  
$billAmount1 = $calculator->calculate($unitPrice1, $quantity1) ;

// ...省略

$billAmount2 = $calculator->calculate($unitPrice2, $quantity2) ;

ご覧の通りクラスもメソッドも増えるので決してeasyな道ではありませんが、スクリプト上の請求額の計算がよりsimpleになることがご確認いただけると思います。会員区分による値引きの実装はDiscountCalculatorに押し込められスクリプト上には現れてきませんし、値引きがない会員の計算は旧ロジックを押し込めたSimpleCalculatorに移譲されますが、その実装の正しさは3.までに本番運用で確認されています。今後の請求額計算の変更や機能追加はcalculator interfaceの下に押し込められ、スクリプトの変更はFactoryのメソッドの選択ぐらいで済むでしょう。先の表の第2象限にいくイメージのソリューションではないかと思います。

今回の実例は実装面だけですが、実際にこのeasy-simpleのトレードオフは設計やテストなどあらゆる箇所で出くわすものなので、認識していると応用が効く場面は結構ありそうです。また、simpleなのが好ましいのは一般論として間違いありませんが、思考停止してむやみやたらにsimpleに突き進むことをせずに、ちゃんとバランスを考えた上で選択する必要もあります(そーだいさんも迷ったらsimpleを選ぶとつぶやいておられました)。最後に、 自戒も込めて言うとsimpleを達成するhowとなる知識やスキル、柔軟な発想力など日頃からの研鑽が非常に大事です(私の家の本棚で積読されている書籍達の怨嗟の声が聞こえてきます……)。

我々がこれからしようとしていること

上述の通り、小さくリリースする習慣は開発部の個々のチームで日々実践されています。しかし、我々のメインプロダクトは巨大なモノリスであり、各チームのリリースが毎日一つに集約されて一つの大きなリリースになっています。そーだいさんのお話とはやや異なる次元ではありますが、リリースを小さくする余地はまだあるといえるのです。

リリースが集約されるがために、メインプロダクト内全体で同じリリース制約が共有されます。最上流だったり重要業務にかかるリリースと同程度の同程度の慎重さをもって、最下流で他に影響を及ぼしようがないような内容をリリースする必要があるのが現状です。この制約にどう立ち向かい、より小さなリリースを実現してアジャイルな開発を促進していくのかは今後の弊社開発部にとって大きな課題です。

そーだいさんのお話を聞いたおかげで、弊社開発部ができていることを改めて認識し自信を持つことができた上で、上記のような課題に気づくきっかけにもなりました。改めましてそーだいさんにはよいお話をいただきましたと感謝を述べたい次第です。ありがとうございました! 


コドモンの開発チームのTwitterを始めました! アドベントカレンダーの新着記事も毎日ツイートしていくので、ぜひフォローしてください😊

twitter.com