開発サイクルをどのように体系化するか

新井康平氏(以下、新井):それでは、クックパッドの“体系的”サービス開発というタイトルで、会員事業部の新井が発表させていただきます。

まず簡単に自己紹介させてください。名前は新井康平で、2017年に新卒で入社しましたエンジニアです。

入社してすぐはサービス開発部のチームに配属になりまして、主にユーザーさんがレシピを探す体系まわりの改善にあたっていました。仮説検証の組み立てから実際のサーバーサイドの実装、そしてそれをユーザーさんにあててみた結果の分析までを一気通貫で行うといったことをしていました。

その後、昨年末の3ヶ月ほどいったんその仕事を離れまして、cookpad studioという新規サービスのサーバーサイドのメインエンジニアとして、アプリケーションのデプロイ周りを整えたり、APIをゴリゴリ変えたりしていました。

年が明けてから本社の会員事業部の配属になりまして、主に有料会員ユーザーさんの体系回りを改善しております。スキルセットとしては、サーバーサイドで分析もしますので、RailsとSQLといったところを触っています。

本日は、クックパッドではどのように開発サイクルを回しているか、また、それをどういったかたちで体系化していくかについてお伝えしたいと思います。

開発のゴールがわからない難しさ

まず前提として、サービス開発は難しいです。理由の1つに、ゴールがわからないことがあります。ユーザーさんが持つ欲求は、本人も含めて誰にもわかりません。また、仮に1度捉えることができても、時間とともにまた変わっていきます。

例えば、僕たちが何もしなくても、独身だったユーザーさんが結婚されたり、お子さんを持たれたりと、ユーザーさんのライフステージはどんどん変化していきます。したがって、ユーザーさんが抱える欲求は常に変化し続けます。

また、今いる場所がわからないという難しさもあります。サービスの開発者は得てして自分のサービスを正しく理解できていません。「このサービスのコアの価値はこうだ」と信じて送り出したサービスが、ユーザーさんが実際に使っているところを見ると、想像とまったく違う使われ方をしていたということはよくあります。

ゴールがしっかりと定義できる製品設計のような分野ではスタートからゴールに向かって一直線に開発を進めればいいのですが、サービス開発ではゴールを定義できないので、自分たちがゴールだと思うものを仮説として立てて、実行して、ユーザーさんの反応を見るというプロセスを繰り返す必要があります。

その、仮説を立てて実行してユーザーさんの反応を見るという一連のプロセスを、社内では「開発サイクル」や「学びのサイクル」と呼んでいます。

学びのサイクルのフレームワークの1つに、リーンスタートアップで提唱されている「BMLループ」があります。

立てた仮説をプロダクトに落とすBuild、プロダクトを実際にユーザーさんにあてて利用状況を集計するMeasure、得られたデータから知見を抽出して新たな仮説を立てるLearnという3つのフェーズに分かれていて、それぞれの頭文字をとってBMLループと呼ばれています。

仮説とプロダクトのずれが起こりがち

理想としては、仮説が立った段階で、まず仮説を検証できる最小のプロダクトを最速で作ります。そのプロダクトをA/Bテストなどでユーザーさんにあて、効果測定を行います。そして得られた知見を組織内でしっかり共有し、新しい仮説を立てる、というサイクルをグルグル回していきます。

しかし、実際にBMLを回すと、だいたいどのフェーズでも失敗が起きてしまうのです。

具体的にどういった失敗があるのかを見ていきましょう。例えば、とあるレシピサービスの内部で「Push通知の効果はどのくらいかを知りたい」という要求があったとします。開発の最初の段階では「Push通知を実行して検出する」という、要求に対してしっかりと対応した機能の実装からスタートします。

しかし、開発が進んでいくと「どうせなら画像もつけたい」という話が出てきたり、「レシピがリコメンドできるようになったので、その機能も乗せましょう」「リコメンドをやるならLPも作りましょう」というように、どんどん機能が膨れ上がっていくことが多々あります。

こうなってしまうと、最初の「Push通知の効果が知りたい」という要求を正しく検証することはもはや難しくなってしまいます。これはBuildフェーズでの失敗です。

Buildフェーズでは、プロダクトが不必要に大きくなってしまって実装に時間がかかってしまったり、検証したい仮説と完成したプロダクトの機能がいまいちかみ合っていなかったり、あるいはそもそも仮説の段階で考慮漏れがあるといったことがよく発生します。

BMLループを回しても学びがない事態が起きてしまう

別のケースを見てみましょう。今度は、最初の仮説にしっかりと対応した機能を実装してリリースできたとします。リリースしたプロダクトのユーザーさんの利用状況を計測する段階でよく起こる問題が、ログが埋まっていないことの発覚です。

これはMeasureフェーズでの失敗です。

Measureフェーズでは、いざ計測しようとするとログが埋まっていなかったり、複数のA/Bテストが自動で走っていることに気がつかず衝突して計測結果に影響が出ていたり、集計SQLの間違いに気がつかないまま前に進んでいたということが起こります。

他の例を見てみましょう。BuildとMeasureが順に終わり、得られたデータから知見を得るLearnフェーズに入ったとします。施策が終わって結果が数字で出てくると、例えば「TOPのバナーを変えて入会が何パーセント増えました」というような報告が入ってきます。それで、最初は「お!」と思います。

しかしよく考えると、それが多いのか少ないのか、いまいちわからない。報告した人もいまいちわかっていない。そしてチーム全体でいまいちわかっていないまま「少なくとも数字は下がっていないし採用しよう」となります。

このように、サイクルを1周回したのに、特に学びが得られないということがよく起こってしまいます。これはLearnフェーズでの失敗です。

Learnフェーズでは、出てきた数字をどう解釈すればいいかよくわかっていなかったり、数字は動いたけれど学びはあまり得られなかったり、得られた知見が特定の人しかわからないものになってしまって、最悪の場合その人が辞めてしまったり異動したりしたときにすべて闇に消えてしまうということがあります。

BMLループを「前から順番に考えない」理由

では、これらの問題を起こさないためには、どのような点に注意すればいいのでしょうか。細かいチェックポイントはたくさんありますが、本日は大きなポイントの1つとして「BMLループを前から順番に考えない」ということをお話ししたいと思います。これを意識することで、手戻りの防止や、効率的な学びにつなげることができるのです。

それぞれの理由を説明していきます。まずは手戻りの防止からです。BMLループを、Buildが終わってからMeasure、Measureが終わってからLearnというふうに逐次的にやろうとすると、結局大きな手戻りにつながってしまいます。

しかし、前のフェーズが終わる前の段階で次のフェーズのことを考えてもいいわけです。むしろ学びのサイクル自体を1つのプロジェクトと考えると、最初にサイクル全体をしっかり設計するべきではないでしょうか?

エンジニアがテストを先に書いて、後からそのテストが通るように要件を満たしたコードを実装していくように、最初にサイクル全体を設計し、それぞれのフェーズの要件を満たすように実行していくことで、手戻りの防止につなげることができるのです。

逐次的にサイクルを回そうとする場合、仮説からプロダクトをみるとして、プロダクトの利用状況をMeasureするという流れになるわけですが、この段階で先ほど示したようにログが足りなかったり、どういう数値を計測するのかがしっかり決まっていないと、仮説の段階に手戻りになります。

そして計測のことも考えつつプロダクトをBuildして、Measureのフェーズを経て、知見抽出のLearnのフェーズに入るわけですが、また数字の解釈がわからなかったり、どの数値で施策を評価するのかが明確に決まっていないと、この段階に至っては手戻りを起こすことすら難しくなってしまいます。

せっかくサイクルを回したのに、何も得られなかったという結果になりかねないのです。

仮説が立った段階で決められるものは、サイクルの先に関するものもしっかりと設計してしまい、その設計に沿ってそれぞれのフェーズを実行する。これで、先ほど述べたような事故を事前に察知し、手戻り防止につなげることができます。

まず施策結果の予想をしっかり立てるべき

次は効率的な学びについてです。先ほどから学び学びと言っていますが、「学び」とはいったいなんなのでしょうか? 非常に定義が難しい言葉ではありますが、私はその大きな1つの要素として「サービスに対する理解と現実とのギャップ」が挙げられると思います。

もう少し具体化すると、サービスに対する理解は、現状での施策の結果の予想と考えることができます。それに対応する現実は、実際に施策を打った結果です。

これらを比較することで、「思ったよりいい数字が出た、悪い数字が出た」という事実が明らかになります。また、それはなぜだったのかを考えることで、今の自分たちがサービス、あるいは現実に対して抱えている理解のずれや勘違いが明らかになります。これが大きな学びにつながっていきます。

したがってサービスに対する理解、つまり施策結果の予想の部分を事前にしっかりと持っておくことは非常に重要です。この部分が落ちてしまうと、当然比較から得られる学びも落ちていって、最終的には数字だけがぽつんと取り残されることになります。

数字の解釈には恣意的なものが入りますし、そうなってしまうと、学びを得るのは非常に難しくなります。ですので「この施策はどういう数字で計測して、どういう結果が出そうか」というMeasureやLearnのフェーズで取り扱うことについて、事前にしっかり考えておくことが非常に重要なのです。

ここまで、サイクル全体の設計についてお話ししてきました。

指標の決定が重要

次に各フェーズについて、具体的に何を前もって考えておけばいいのか、つまりどういったことを設計しておけばいいのかについてお話しします。

まず、Buildについては、前もって絶対に検証したい仮説の明確化や検証背景の整理、検証内容・注意点などの整理をしておく必要があります。

Measureについては、計測手法の選定です。これはA/Bテストでやっていいか、すべてのユーザーさんを対象にしていいかどうか、というようなことを前もって決めておく必要があります。

そして、やはり重要なのがKPIの決定です。施策をどういう数字で評価するのか、PVなのかCVなのか、あるいはもっと複雑な手法なのかを決めると同時に、他に影響を与える指標がないかどうかの確認が必要です。

よくある話として、例えば、トップページに有料会員入会の動線となるバナーを設置してプロトタイプを作ります。そのプロトタイプを実際に出してみたところ、そのバナーからわりと多くの入会があったので「これはいけそうだ」ということで、本番サービスにリリースします。

しかし、1週間くらい経ってからよくよく見ると、別のページ、例えば検索結果ページからの有料会員入会の数字がガクッと落ちていることがあります。これは、トップページからの入会の指標と、検索結果ページからの入会の指標が、実は相反していた、影響を与え合っていたということに気がつかなかったことによる事故です。

そもそも指標やKPIがそれ単体で存在することは非常に珍しく、だいたいはそれと相反する指標や、影響を与え合ってしまう指標がセットで存在しています。したがって、それらの指標について、設計の段階で考慮漏れがないかを確認しておくことで、先ほど言ったような事故を防ぐことができます。

チーム内で「成功のイメージを共有する」

また、当然ではありますが、ログの確認はやっておく必要があります。自分たちが欲しいデータは現存するログだけでしっかりとれるのかという確認と同時に、実際に一度SQLを叩いてみて、思ったような数値が出ているかを確認しておくことが重要です。

最後に、Learnについてです。Learnフェーズでは、指標解釈の整理をしておく必要があります。

Measureフェーズで掲げたKPIに対して「この数値が高かった、あるいはこの数値が低かったときに、ユーザーさんは実際にはどういう体験をしているのか」ということをチームの中でしっかり話し合っておくことで、いきなり結果の数字を目にして混乱するという事態を防ぐことができます。

また、それと合わせて、結果の想定も非常に重要です。測定指標がどれくらいの数字になったら施策を採用するのかということは、事前に決めておく必要があります。

やはりサービスの開発者は、せっかく作ったものは出したくなってしまうものです。事前に取捨選択のラインをしっかり引いておかないと「少しでも数字が上がっていたら出していいのではないか」となんとなくゴーサインが出されて、どんどん機能が追加されていって、サービスが膨れ上がってしまいかねません。

社内では、この指標解釈の整理と結果の想定を合わせて「成功のイメージを共有する」と表現しています。今打とうとしている施策が成功したときに、ユーザーさんがどういう体験をしていて、その結果としてどういう指標がどのあたりまで変わっているのかをチーム内で事前に確認しておくわけです。

フェーズごとに適切なフレームワークを用いてサイクルの失敗を防ぐ

今あげたような設計は、実際にサイクルを回しはじめる前に(スライドを指して)このようなかたちで議論されていることが多いです。大項目の部分に注意してください。

この検証では検索DAUの増加をはかったのですが、その背景、検証の内容、メインの計測指標、サブの計測指標、それぞれの指標の結果の想定について、サイクルを回す前に事前に話し合っています。社内では実際にこういったことを行っています。

それでは、社内で利用されているツールやフレームワークについて、それぞれのフェーズに合わせて紹介していきます。

まず、Buildフェーズでは、アイデアがなかなか出てこなかったり、仮説と機能がかみ合わなくなってしまうことがよくあります。これを防ぐために社内では「価値仮説シート」というフレームワークを用いています。

このフレームワークは、順番に空欄を埋めていくことでユーザーさんの欲求としっかり対応した機能をもつプロダクトの作成を補助します。

ユーザーさんは○○したいが□□できないので△△できることに価値があるというように、一番上にターゲットとなるユーザーさん、そのユーザーさんが抱えているであろう欲求、その欲求を満たすために解決しなければならない課題、その課題を解決できる製品の特徴、というふうに考えていきます。

例えばクックパッドの人気順検索の価値仮説を例にこのシートを埋めてみますと、レシピを探すユーザーさんは今日のメニューを早く決めたいのですが、多くのレシピからさがすのはなかなか難しいので、人気のレシピを探せることに価値がある、というように空欄が埋まっていきます。

さまざまなツールが開発を補助している

このようにしてプロトタイプやプロダクトの機能を決めると、機能や特徴、方向性が決まったあとに難しくなってくるのが、いかにそのプロトタイプ、プロダクトを安全に素早くBuildするかということです。これをサポートするフレームワークとして、社内ではRailsプラグインの「Chanko」と呼ばれるものを用いています。

Chankoはユーザーセグメントごとのロジック適用や、エラー発生時のオールバックをサポートしているプラグインで、オープンソースでもあります。これとあわせて、A/Bテスト用のChanko拡張である「EazyAb」を用いて、安全に素早くA/Bテストを実現できる環境が整えられています。

次に、Measureフェーズでは、「Hakari2」と呼ばれるロギング・KPI管理ツールを用いています。これはキーワードを設定してワンライナーでコードの中に入れることでイベントのロギングができて、さらに自動でdashboardで可視化してくれる便利なツールです。

これとあわせて「papa」という管理ツールのdashboardもよく用いられています。これは先ほどありましたHakari2のログをもとに集計結果を可視化してくれるdashboardです。

(スライドを指して)下の図のように、近似ではありますが確率分布で結果が可視化されるので、今生じている差がばらつきの範疇なのか、あるいは有意差がありそうなのかという判断の目安に使われます。

レビューの共有でチーム全体を高める

最後に、Learnフェーズでは、知見の制度や参照性に問題を抱えることが多く、誤った知見が導かれてしまったり、どこに知見がたまっているのかわからなくなってしまうことがよくあります。この問題を防ぐために、社内では「Report.md」というやり方で運用しています。これは施策の分析レポートをMarkdownで作成して、それをPull Request形式で運用するやり方です。

目的は、チームのレポジトリにレビューの通った知見をプールしていくことです。具体的な手順としては、施策の担当者が、施策が終わった後に分析レポートをMarkdownで作成して、Pull Requestを提出します。

そしてそのPRをチームメンバーを中心にレビューします。

レビューが通ったらマージすることで、自然にチームのレポジトリに知見がたまっていく仕組みです。

運用が始まってまだ1年もたっていませんが、すでに多くのメリットを感じております。大きなところだと、知見のプール箇所が明確になることで参照性が上がったり、レビューを通すことで知見の精度自体が上がったりということが実感できています。

また、レビューを作成することによって、施策が終わってすぐの鮮度の高い状態でメンバーに知見が共有されたり、レビュワーの施策やサービスに対する理解が深まったり、Pull Request上の議論から施策の反省や次のアイディアが生まれるという効果も確認されています。

最近では事前にReport.mdのひな型を作成することで、サイクル全体を最初に設定することをある種強制するような取り組みを行っている部署もあります。

それでは、本日のまとめです。サービス開発では、仮説の実行から学びを得ることが最も重要です。そして、そのサイクルを確実に失敗せずに回すためには、手順を1から逐次的に実行するのではなく、まず最初にサイクル全体を設定することがポイントです。加えて、各フェーズで適切なフレームワークを用いることで、このサイクルの実行をより素早く効果的に行うことができます。

以上で発表を終わります。ご清聴ありがとうございました。