見出し画像

KPI自動通知Botで始める 数字に執着するプロダクトマネジメント

こんにちは!

dely, Inc.で新規事業開発をしている奥原 (@okutaku0507) といいます。役割としては、プロダクトマネージャー (PdM) 兼エンジニアをしています。

この記事は「dely #2 Advent Calendar 2020」の23日目の投稿です。

22日の投稿は、共にリテール事業部で働くkassyさんによる「2020年 UIデザイナーが読んで良かった本 9冊」でした。UIデザインの知識はプロダクト開発に関わる方全員にあって良いと思うので、是非読んでみてください。文章中にもありますが、プロダクト開発において「倫理」は非常に重要だと考えさせられています。

今年は「dely #1 Advent Calendar 2020」もあるので、是非お楽しみください。

はじめに

僕はdely, Inc.に入社した2016年9月からずっとサーバーサイド (Ruby on Rails) のエンジニアをしています。そして、2018年の5月ごろからPdMになり、レシピ動画サービスであるクラシルのプロダクトマネジメントを担当していました。同時に開発部のGMを兼務した後、現在は新規事業のPdM兼エンジニアをしています。僕が担当している新規事業については、こちらの記事を読んでいただくか、実際にプロダクト (クラシルチラシ) を見ていただければと思います。

このnoteは「KPI自動通知Botで始める 数字に執着するプロダクトマネジメント」というタイトルで、PdMが自らKPIをSlackに自動で通知するBotを作成し、数字に執着して改善プロセスを回すチーム作りができることをゴールとします。非エンジニアの方でもわかるように、サンプルコードを添付し、詳細を解説していきますので、是非ともチャレンジしてみてください。最後には僕へのMessengerリンクを貼っておきますので、わからなくなったら気軽に質問していただければ嬉しいです。

KPIとは何か

当たり前かも知れませんが、KPIとは何かを書いておきます。

KPI(Key Performance Indicator)は、「ケーピーアイ」と読み、日本語では「重要経営指標」「重要業績指標」などと訳されるもの。

KPIはKGI (Key Goal Indicator : 重要目標達成指標) を分解した指標であり、KPIを達成することでKGIが達成されるという関係性を持ちます。具体的な例として、継続率がKGIとなるプロダクトでは、オンボーディング突破率や一人当たりのお気に入り数がKPIとなる可能性があります。これは簡略したものであり、それぞれの因果関係を仮説検証しながら進めなければなりません。もっと分解して考えるのならば、継続率を構成している要因を洗い出して、階層をどんどん掘っていきます。その工程はこのnoteでは取り扱いませんので、他の記事や本を参考にしていただければと思います。

画像2

なぜKPIを追うことが重要なのか

なぜ、僕たちPdMはKPIを定めて追っていく必要があるのでしょうか。それは「プロダクトを成功に導くため」であるという簡素な理由だと考えています。重要なことはKPIを達成することが目的ではなく、KPIを達成すれば得られるであろうとしているプロダクトの成長を目的としていることを忘れてはなりません。いわゆる手段の目的化は注意深くしていないとすぐに起こってしまいます。常に、なぜKPIを追っているのかというKPIツリーなどを作成して、本来の目的に立ち返る機会を作りましょう。

North Star Metricをさがせ

「North Star Metric」という概念を紹介させていただきます。

North Star Metricとは、
それさえ追っていけば、プロダクトが本質的に成長する1つの指標のことです。

梶谷健人さん (@kajikent) のnoteが非常に詳しく説明されているので、是非読んでいただければと思います。

North Star Metricを追うということは、簡単に書くならば、遠すぎるKGIと細かすぎるKPIの橋渡しをする指標であると解釈しています。例えばですが、KGIである「継続率」を改善するぞという指標はとてもではないですが、それだけを指標に改善施策を回すことは難しいです。継続率は結果指標であるため、非常に多くの要因に左右されます。例えば、時期的な要因や新規ユーザーの流入チャンネルなどプロダクト外部の要因が起因した継続率の上下は日常茶飯事です。次にKPIである「一人当たりのお気に入り数」を上げる施策を検討することは比較的容易だと思います。しかしながら、一人当たりのお気に入り数が2倍になったといって、KGIはびくともしないことはザラにあります。KPIの達成のためにハック的な思考に陥ってしまうこともあります。

そのため、プロダクトを健全にグロースしていく「それさえ追っていけば、プロダクトが本質的に成長する1つの指標」が必要です。海外のサービスの例を紹介します。これを追えば本当にプロダクトは成長するの?と疑問に思う方もいると思いますので、梶谷さんのnoteを含めて、参考になる記事を紹介しているので、読んでみてください。

画像3

数字に執着する仕組みを創る

具体的な方法に移る前に、数字に執着する仕組みを創る概略を説明します。数字に執着して改善サイクルをスピーディに回せるチームを創ることで、日々のプロダクトのことを考える機会が増えて、本質的な改善に繋がる、異常が合った場合にすぐに検知できるなどの利点があります。

一つ注意しなければならないことは、数字に踊らされないことです。数字が下がったりした場合はすぐに対策を打ちたくなりますが、行き当たりばったりに対処療法をしていては、プロダクトの健全な成長には繋がりません。しかしながら、数字についてチームで話す機会は非常に重要です。そして、日々の会話の中で自然と話せることが必要だと考えています。

数字に執着

Slack自動通知Bot作成の概略

前置きが長くなってしまいましたが、本題であるSlackに自動通知するBotを作成する方法を紹介していきます。できる限り詳細にサンプルコードを交えながら進めていきますので、わからないところがありましたらご指摘ください。最初に全体像を見てみます。

1. KPIを取得する (Redash/Google Analytics)
2. Slackアプリを作成する
3. Slack自動通知処理

という3ステップで説明していきます。全て、2020年12月19日の時点の情報となるため、各サービスのアップデートによって仕様変更やサービスの終了があった場合は申し訳ありません。

RedashでKPIを自動通知

KPIを取得する方法を、弊社で使用しているRedashを例に紹介します。可視化ツールは非常に多くあるので、それぞれのサービスによって操作が異なるため、サポート仕切れないことをご了承ください。また、データの取得や可視化には様々な方法が存在するため、一例として考えていただければと思います。

Redashとは様々なデータソースを接続してSQLでデータを取得、グラフを簡単に作成することができるツールです。OSSで公開されており、EC2インスタンスなど自分たちで借りたサーバーにホスティングすることが可能です。

RedashではSQLを書くことでデータを取得、簡単にグラフ化することができるのですが、当たり前ですがSQLを書くことができないとデータを取得することができません。SQLをどう覚えるかは非常に難しい問題で、僕もとても苦労しました。まず、入門としては本で基礎を勉強するのが良いと思います。僕も使った本を紹介します。あとは、ひたすら実戦でSQLを書きまくることと、継続して書き続けることしかないと僕は考えているので、業務の中で積極的に使っていくことをお勧めします。投げやりになってしまいましたが、その中でも継続するテクニックがあるので、僕に直接聞いていただくか別の機会でnoteを書きたいと思います。​

サンプルとして、ユーザー数を日毎に出してみます。

SELECT Date(created_at) AS date,
      COUNT(*) AS count
FROM users
GROUP BY Date(created_at)
ORDER BY Date(created_at) DESC

Redashで以下のような結果が得られたとします。「Table」というタブの右に「+ New Visualization」というボタンがあり、ここから簡単にグラフを作成することができます。

スクリーンショット 2020-12-19 17.25.50

X軸とY軸の項目を選択することで、以下のようないい感じのグラフが表示されます。 右下の「Save」をクリックすることで作成したグラフを保存することができます。

スクリーンショット 2020-12-19 17.26.18

右上の「Show Data Only」をクリックすると、SQLを書いていたエディターが閉じてグラフだけが表示されると思います。

スクリーンショット 2020-12-19 17.30.45

下記のグラフのようにTableとChartタブができれば、自動通知の準備はOKです。

スクリーンショット 2020-12-19 17.30.53

RedashはSlackとの連携が簡単に行うことができます。ちょっと古い記事ですが、Gunosyの記事を共有します。Redash公式のページもありましたので共有しておきます。

RedashがSlackに連携することができましたら、自分のDMで下記のリマインドを設定します。@redashbotは渡されたRedashのURLを展開してくれるBotでdelyで内製されています。OSSでも公開されていますので導入してみてください (redashbotの導入はエンジニアに依頼してください) 。

/remind チャンネル名 " @redashbot 実行させたいredashのURL " at 定期実行させたい時間 (9:00 every day)

そうすると指定した時間に下記のようなリマインダーが投稿されたら成功です。

画像9

Redashの結果をテキストで自動通知

Redashでグラフを自動で投稿することができるようになりました。しかしながら、グラフだと数値の推移は把握しやすいけれども、具体的な数値をパッと確認することは難しいです。Redashの数値をAPIで取得し、
Google Apps Script (GAS) を用いて通知する方法を紹介します。

Redashの結果はAPIを用いてJSONフォーマットで取得することが可能です。Redashのクエリを書いていたページの右上のメニューから「Show API Key」をクリックします。表示されたモーダルから「Results in JSON format」に書かれているURLをコピーします。

画像10

今回は便宜上、JSONを取得することができる「JSON OKIBA」を使わせていただきます (とても便利) 。

以下のAPIを用いて、サンプルコードを書いていきます。

https://jsondata.okiba.me/v1/json/pKTkT201219091525

スクリーンショット 2020-12-19 18.18.56

GAS (ガス) という言葉を一度は聞いたことがあると思います。このnoteでは、GASの詳しい書き方については扱いません。Redashとの連携のサンプルコードを書きますので、適宜変更してください。

GASを用いてSlack通知するためには、上で説明した以下のアプローチを行います。項目を分けて説明していきます。

・Slackアプリを作成する
・Slack自動通知処理

Slackアプリを作成する

Slackに通知するためには、Slackアプリをまずは作成します。SlackアプリはSlackプラットフォーム内で様々な処理をするためのアプリケーションです。下記よりアプリを作成します。

右上にある「Create New App」からWorkspaceを指定して、新しいアプリを作成します。

スクリーンショット 2020-12-19 18.57.43

スクリーンショット 2020-12-19 18.57.58

作成後にSlackのチャンネルにメッセージを送信するための「Incoming Webhook」を作成します。言葉を聞いたことがある方もいると思います。作成したSlackアプリの左メニューにある「Incoming Webhooks」をクリックしてください。その後、右上にあるボタンをクリックして、Incoming WebhookをOnにしてください。

スクリーンショット 2020-12-19 19.13.30

スクリーンショット 2020-12-19 19.13.42

Onにすると、下に「Add New Webhook to Workspace」というボタンが出現します。ボタンをクリックすると、通知するチャンネルを選択します。便宜的に#generalにしますが、通知したいチャンネルを指定してください。

スクリーンショット 2020-12-19 19.13.51

スクリーンショット 2020-12-19 19.14.04

Webhookを作成すると以下のようになります。また、指定したチャンネルでWebhookを設定したことが通知されます。

スクリーンショット 2020-12-19 19.14.23

スクリーンショット 2020-12-19 19.14.32

これでSlackに通知する準備はOKです。お疲れ様でした。次はGASを書いて行きます。

Slack自動通知処理

GASを書くにあたり、新しいファイルを作成します。Google Driveで右クリックをして、以下のメニューから「Google Apps Script」を選択してください。

スクリーンショット 2020-12-19 19.15.14

こんな画面になります。これは新しいコードを編集することができる画面です。

スクリーンショット 2020-12-19 20.40.21

「コード.gs」を以下のように書き換えます。コピペで大丈夫です。

function report() {  
  // jsonを取得 (json_urlはRedashで取得したURLに変更してみてください)
  var json_url = 'https://jsondata.okiba.me/v1/json/pKTkT201219091525';
  var json = UrlFetchApp.fetch(json_url).getContentText();
  var jsonData = JSON.parse(json);
  var rows = jsonData['query_result']['data']['rows'];

  // 日付を取得
  var day = new Date();
  var text = '';
  text += "【" + Utilities.formatDate(day, 'Asia/Tokyo', 'yyyy年MM月dd日') + "】 \n"
  // データを書き込む
  text += "```\n";
  for (var i = 0; i < rows.length; i++) {
    var row = rows[i];
    text += "[" + row['date'] + "]" + " " + row['count'] + "\n";
  }
  text += "```";
  sendSlack(text);
}

function sendSlack(slackText){
  // Webhook
  var webHookUrl = "先ほど作成したWebhook";
 
  var jsonData =
      {
        "text" : slackText
      };
 
  var payload = JSON.stringify(jsonData);
 
  var options =
      {
        "method" : "post",
        "contentType" : "application/json",
        "payload" : payload,
      };
 
  // リクエスト
  UrlFetchApp.fetch(webHookUrl, options);
}

「report」を選択して、再生ボタンを押してみてください。

スクリーンショット 2020-12-19 20.54.11

こんな通知が#generalに流れたら成功です!今日からエンジニアです!!!👏

スクリーンショット 2020-12-19 20.48.19

これは本当に最初の一歩です。RedashとGASを組み合わせれば様々なデータを自由に加工して通知することができます。是非ともこれで終わらずに、チャレンジしてみてください。

Redashを定期実行させて、それをGASで時間を指定してSlack通知をするところまで作り込みます。Redashの左下にある「Never」をクリックすると定期実行するモーダルが表示されます。

スクリーンショット 2020-12-19 17.32.48

モーダルの「Refresh every」を1dayにして、「On time」を08:00に設定します。こうすることで、毎朝8時に書かれたSQLが実行されるようになります。

スクリーンショット 2020-12-19 17.33.02

次にGASの定期実行を設定します。先ほどスクリプトを実行した再生ボタンの左にある時計のマークをクリックします。

スクリーンショット 2020-12-19 21.31.07

GASではトリガー (きっかけ) を設定してスクリプトを実行します。今回は時間をトリガーにしますが、 Google スプレッドシートに入力されたらなどのトリガーを設定することも可能です。

スクリーンショット 2020-12-19 21.31.18

下記のようにトリガーを設定します。こうすることで午前8 ~ 9時の間に指定してスクリプトを実行することが可能です。ぴったりに実行できないのは仕様なので仕方がありません。

スクリーンショット 2020-12-19 21.31.39

以上で、8時に元データであるRedashを更新し、8 ~ 9時に通知用のスクリプトを実行することで、常に新しい数値をSlackに追知する仕組みをつくることができました。

Google AnalyticsでKPIを自動通知

Google Analyticsを用いて、Slack通知をする方法を紹介します。Google Analyticsを日常的に使っている方は多いと思います。今回はGoogle AnalyticsにおけるサービスのDAUをGoogle スプレッドシートに書き込んで、前週と比較した数値をSlackに通知します。

まず、新規でGoogle スプレッドシートを作成します。今回は非常に簡単なアドオンを使用してGoogle Analyticsのデータを取得します。インストール手順は以下の記事を参照してください。

アドオンをインストールすることができたら、メニューから「Create new report」を選択します。

スクリーンショット 2020-12-19 21.41.47

AccountとPropertyを選択してMetricsを「1 Day Active Users」にします。これがDAUを意味しています。

スクリーンショット 2020-12-19 21.54.17

設定することができたら「Run reports」を実行してみましょう。以下のように「dau」というタブができれば成功です。

スクリーンショット 2020-12-19 21.43.13

スクリーンショット 2020-12-19 21.54.32

アドオンのもう一つの機能であるスケジュールをしておくことで、定期実行をすることができます。

スクリーンショット 2020-12-19 21.49.27

スクリーンショット 2020-12-19 21.49.41

次に、DAUを日々シートに書き込んでいく処理を書いていきます。まず「raw_data」という新しいタブを作成します。ヘッダーは下記のようにしておきます。

スクリーンショット 2020-12-19 21.57.08

次に先ほども書きましたGASを書いていきます。メニューの「ツール > スクリプト エディタ」を選択することで先ほどと同様のスクリプトを編集する画面が表示されます。

スクリーンショット 2020-12-19 21.58.57

表示された新しいスクリプトエディタに以下をコピペしてください。適宜、変更する場所があるので、忘れずに変更してください。この後に行うグラフ通知も一緒に書かれています。

function write() {
  var ss = SpreadsheetApp.openById('スプレッドシートID');
 
  // シートを取得 (DAU)
  var dauSheet = ss.getSheetByName('dau');
  var dau = dauSheet.getRange('B12').getValue();
 
  // シートを取得 (書き込み用)
  var writtenSheet = ss.getSheetByName('raw_data');
 
  // セルの最後の行を取得
  var lastRowNum = writtenSheet.getLastRow();
  var insertedRowNum = lastRowNum + 1;
 
  // 日付を代入
  var day = new Date();
  day.setDate(day.getDate() - 1);
  writtenSheet.getRange('A' + insertedRowNum).setValue(Utilities.formatDate(day, 'Asia/Tokyo', 'yyyyMMdd'));
 
  // データを入力
  writtenSheet.getRange('B' + insertedRowNum).setValue(dau);
}

function dailyReport() {
  var text = "";
 
  var day = new Date();
  day.setDate(day.getDate() - 1);
  text += "【" + Utilities.formatDate(day, 'Asia/Tokyo', 'yyyy年MM月dd日') + "】\n"
 
  // シートを取得
  var ss = SpreadsheetApp.openById('スプレッドシートID');
  var writtenSheet = ss.getSheetByName('raw_data');
 
  // セルの最後の行を取得
  var lastRowNum = writtenSheet.getLastRow();
  var lastRowNumPreviousWeek = lastRowNum - 7;
 
  // データを取得 (最新)
  var dau = writtenSheet.getRange('B' + lastRowNum).getValue();
 
  // データを取得 (前週)
  var dauPreviousWeek = writtenSheet.getRange('B' + lastRowNumPreviousWeek).getValue();

  // 通知用文言を作成
  text += "```\n";
  text += "アクティブユーザー (DAU) = " + dau.toLocaleString() + " " + "(" + dauPreviousWeek.toLocaleString() + ")" + "\n";
  text += "```\n";  
 
  // slack通知
  sendSlack(text);
 
  // グラフも送信
  dailyReportGraph();
}

function sendSlack(slackText){
  // WebHookUrl
  var webHookUrl = "先ほど作成したWebhook";  
 
  var jsonData =
      {
        "text" : slackText,
      };
 
  var payload = JSON.stringify(jsonData);
 
  var options =
      {
        "method" : "post",
        "contentType" : "application/json",
        "payload" : payload,
      };
 
  // リクエスト
  UrlFetchApp.fetch(webHookUrl, options);
}

function dailyReportGraph(){
  // シートを取得
  var ss = SpreadsheetApp.openById('スプレッドシートID');
  // DAUグラフ
  var sheet = ss.getSheetByName('DAU_Graph');
  var charts = sheet.getCharts();
  var chartName = 'DAU.png'
  var chartImage = charts[0].getBlob().getAs('image/png').setName(chartName);
  sendGraphSlack(chartImage, chartName);
}

function sendGraphSlack(chartImage, chartName){
  var url        = 'https://slack.com/api/files.upload';
  var token      = '後ほど取得するトークン';
  var channel    = '#general'
 
  //パラメータ作成
  var payload = {
    'token'      : token,
    'channels'   : channel,
    'file'       : chartImage,
    'filename'   : chartName,
    'text' : chartName,
  };
 
  var params = {
    'method'  : 'post',
    'payload' : payload
  }; 
 
  UrlFetchApp.fetch(url, params);
}

スクリーンショット 2020-12-19 22.31.25

変更すべき箇所は以下です。

スプレッドシートID : URLから取得
先ほど作成したWebhook : 上記で作成したWebhook URL
後ほど取得するトークン : このあと取得します

スプレッドシートIDはURLにあるこちらです (下記の図でグレーアウトされている部分) 。

スクリーンショット 2020-12-19 21.52.45

それぞれを解説していきます。

「function write()」という処理では、先ほど作成した「dau」タブから最新のDAUを取得して「raw_data」に書き込みます。実行すると以下のように最新のデータ (実行時は2020年12月19日なので、前日が最新となる) が書き込まれます。スクリプトの中にある「B」などのアルファベットは列を表しているので、適宜変更してください。

スクリーンショット 2020-12-19 22.26.44

先ほどと同様に定期実行されるようにトリガーを設定しましょう。

スクリーンショット 2020-12-19 21.31.07

スクリーンショット 2020-12-19 22.29.32

ここまでで、アドオンでのGoogle Analyticsのレポートを7 ~ 8時に実行し、最新のDAUを取得、GASで8 ~ 9時に最新のDAUを「raw_data」タブに書き込む処理が完成しました。

次に「function dailyReport()」でRedashでのSlack通知と同様にSlackへの通知を行います。以前取得したWebhookを指定することができていれば、以下のような通知が#generalに流れると思います。これで先週と比較したDAUを通知する仕組みが整いました。

スクリーンショット 2020-12-19 22.39.01

忘れずにトリガーを設定して自動化しておきます。これで9 ~ 10時にDAUレポートが自動で投稿されるようになりました!🎉

スクリーンショット 2020-12-19 22.39.50

最後にDAUの推移をグラフ化して通知する方法を紹介します。今までの方法では、日々のDAUを確認することはできますが、DAUがどのように変化しているのかを確認することができません。そのため、異常値に気がつきにくい、健全に成長しているのかわからないなどの問題が出てきます。弊社のブログでも紹介しているので、是非ご覧ください。

まず、Google スプレッドシート上でグラフを作成して、グラフのメニューから個別のシートに移動させ「DAU_Graph」というタブ名に変更しておきます。

スクリーンショット 2020-12-19 22.05.57

グラフをSlackに通知する方法は、今までのWebhookとは異なる方法を用いる必要があります。

Slackアプリから、先ほど作成したtestアプリを選択します。「Add features and functionality」という大項目から「Permissions」を選択します。

スクリーンショット 2020-12-19 22.08.05

「Scopes」という項目から「Bot Token Scopes」の「Add an OAuth Scope」ボタンをクリックして「files:write」を追加します。グラフを送信するための権限をアプリに付与しています。

スクリーンショット 2020-12-19 22.09.40

権限の追加が完了しましたら、アプリを再度インストールし直す必要がありますので、上の方にある「Reinstall to Workspace」をクリックしてインストールし直してください。

スクリーンショット 2020-12-19 22.08.23

ここでスクリプトの中にあった変更点である「後ほど取得するトークン」を取得することができました (Bot User OAuth Access Token) 。コピーしてスクリプトの「function sendGraphSlack(chartImage, chartName)」内に埋め込んでください。

そして「dailyReportGraph()」を実行して#generalにグラフが通知されれば完了です!🎊

すでにグラフを送信する処理も「dailyReport()」に記述されているので、これで毎朝9 ~ 10時にDAUの具体的な数値とグラフを同時にSlackに自動通知する仕組みが完成しました。お疲れ様でした。

スクリーンショット 2020-12-19 22.58.41

スクリーンショット 2020-12-19 22.18.48

さいごに

ここまで、長く複雑な設定を行っていただきありがとうございました。どこかでつまずいてしまった場合、このnoteを書いた責務として僕にチャットしていただければできる限り対応させていただきます (全てに対応させていただくことはできないことをあらかじめご了承ください) 。

画像1

数字に執着して、プロダクトの成長を常にディスカッションできるチームは本当に強いと思います。その第一歩としてKPIをSlackに自動で通知して日々数字に触れる癖をつける方法をご紹介しました。少しでも、読んでいただいている方のお役に立てたら幸いです。

読んでいただき、本当にありがとうございました。良いお年をお過ごしください。

明日は弊社SRE、joeさんによる「delyのSREチームがオンコールトレーニングを導入する理由」です。お楽しみに。

delyでは一緒にプロダクト開発に向き合ってくれる仲間を募集しています。

また、プロダクト開発チームがテーマ毎に話すイベントも開催しているので、興味を持っていただいた方は是非ともご参加ください。


この記事が気に入ったらサポートをしてみませんか?