エンジニア目線でみるグロースハックツール Mixpanel をのりこなす 6 つのポイント

Cacoo ではユーザがどのように Cacoo を使っているのかを知り、効果的な改善を行うために Mixpanel を導入し、日々その利用状況を確認し、プロダクト開発やマーケティング施策に活用しています。

Mixpanel は Cacoo を例に上げると「図を見る」「ステンシルを図に足す」「図を共有する」といった、ユーザの行動を記録し、それを様々な切り口でビジュアライズしてくれるツールです。似たツールとして Google Analytics を思い浮かべる方もいるかと思いますが、Google Analytics がページビューや流入元などの「トラフィック」に着目してるとすれば、Mixpanel は「行動」に着目した解析ツールといえるでしょう。

Mixpanel はグロースハックツールとして紹介されることが多く、グロースやマーケティング視点でのメリットや運用のポイントを紹介した記事は見かけます。ただ、実際に導入し運用するにはプロダクトへの組み込み、定期的なレポーティングやトラッキングの見直しなどエンジニアリングが必要になってきますが、その視点の記事はあまり見かけません。そこで、このポストではエンジニア目線から 6 つのポイントを紹介したいと思います。

1. 出来るかぎりフロントエンドでトラッキングする

Mixpanel でトラッキングするには、アプリケーションのフロントエンドに JavaScript のトラッキングコードを仕込む方法と、Mixpanel が提供している HTTP 経由で API を呼び出す方法の二つがあります。後者はたとえばサーバサイドのアプリケーションコードに組み込む事で、サーバサイドの処理をトラッキングすることが可能です。この後者の方法を「バックエンドでトラッキングする」とここでは記述します。

Cacoo では導入時にはなるべくバックエンドでバッチ的にトラッキングしようと考えていました。その時点で Mixpanel がどれくらい安定しているのかが不明でユーザに影響を与えないか不安だったからです。これには、過去にヌーラボの別サービスでサードパーティ製の JavaScript が問題を引き起こしデバッグに時間がかかったという経験も影響をしていました。

ただ、すぐにバックエンドのトラッキングだと欲しい情報が取れない、取りにくいといった事が分かりました。というのも、Cacoo ではエディタ上での操作などの重要なユーザの行動がフロントエンドでしかトラッキングできないものが多かったのです。そもそも「ユーザの使いかたを詳細に把握して改善につなげる」というツールの導入の目的が果たせなくなると本末転倒なので、基本的なトラッキングは出来るかぎりフロントエンドで行うという方針に転換しました。ただ、問題が発生した場合に備え、設定を一つ変更するだけで Mixpanel のトラッキングを完全に無効にするような仕組みを合わせて組み込みました。

これは Cacoo だけではなく、最近のフロントエンドのリッチ化が進んだアプリケーションにおいて、そのユーザ体験をトラッキングしたいと思えば同じような結論に至るように思います。また、Mixpanel のサポートもフロントエンドでトラッキングしているユーザが大半とも話していたので、出来るかぎりフロントエンドでトラッキングを行うというのが正だと今は考えています。

2. バックエンドのトラッキングを非同期にする

とはいえ、例えば WebHook で通知される入金処理などの、バックエンドでしかトラッキングできないイベントも幾つかは存在します。その際には Mixpanel が標準で提供しているクライアントライブラリを利用しない手はありません。本ポスト執筆時点では、サーバサイドで使える言語としては Python・Java・PHP・Ruby のライブラリが提供されています。

Cacoo はバックエンドは Java で実装されているので、標準のライブラリを利用することにしたのですが、残念ながら標準のライブラリは非同期の呼び出しをサポートしていませんでした。ライブラリの処理そのものは先に述べたように HTTP 経由での API コールなので、バックエンドでのトラッキングといえどここは非同期に処理を行い、アプリケーション本体の処理に影響は与えないようにしました。具体的には、以下のように標準ライブラリ経由での API 呼び出しをラップするようなクラスを利用しています。

	static class MixpanelTask implements Callable<Void> {

		ClientDelivery delivery; // 標準ライブラリ提供クラス

		MixpanelAPI api; // 標準ライブラリ提供クラス

		public MixpanelTask(JSONObject... messages) {
			delivery = new ClientDelivery();
			for (JSONObject message : messages) {
				delivery.addMessage(message);
			}
			api = new MixpanelAPI();
		}

		@Override
		public Void call() throws Exception {
			api.deliver(delivery, false); // ここで HTTP 通信が発生
			return null;
		}
	}

これはどの言語でバックエンドを実装しているかにもよりますが、Mixpanel のクライアントライブラリは JavaScript のものも含め GitHub で公開 されていますので、利用する際にはその実装を確認しておいて損はないと思います。また、標準ライブラリ側に欲しい機能があればプルリクエストしてみても良いと思います。実際、私も小さな変更ではありますが Java ライブラリへのプルリクエストを一つ取り込んでもらいました。

少し横道にそれますが、サービスを利用する上で自分たちの製品に組み込む必要のあるものがオープンソースとして公開されていることは、いざというとき自分たちで問題をデバッグして解決出来る可能性が残されているということです。ですので、そういった点を外部サービス採択の際の技術的な指標の一つにしても良いのでは、と考えています。

3. フロントエンドでエラーハンドリングをする

Mixpanel にはイベントのトラッキングだけでなく、ユーザ毎に言語や有料ユーザかどうかといったプロフィール情報を保存し、取得したイベントの絞りこみやセグメンテーションなどに利用することができます。Cacoo では先に上げた言語設定などの変更の少ないプロフィールについては、セッションに一度だけトラッキングするように実装していました。するとしばらく経ってから、そういったプロフィールが入っていないユーザ情報が一定量存在することに気が付きました。

調べてみるとどうやら、mixpanel.people.set の呼び出しはされるものの、ちゃんとサービス側で保存されていないケースがあることが分かりました。JavaScript のサンプルで明示的に記載されているものは見つからなかったのですが、以下のように呼び出し時に callback 関数を渡すことができ、その返り値で API 呼び出しが成功したかどうかを判別できます。

var props = {}; // profile
mixpanel.people.set(props, function(status, data){
    if(status === 1){ // success
    }else{ // error
    }
});

callback に渡される一つ目の引数は以下のように verbose モードを有効にすると、status ( 0 か 1 の値、上の例と同様の意味 ) と error ( エラーの理由 ) を持つオブジェクトに変わりますので、デバッグ時に役に立つかもしれません。

mixpanel.set_config({ verbose: true });

ここでは mixpanel.people.set の例をあげましたが mixpanel.track でも同様にエラー処理は可能ですので、本来とれているはずのイベントやプロフィールがとれていない、といった場合にはエラー処理を組み込み、再送を行ったり、ログを残すといった処理を行うことで精度の高いトラッキングを行うことが出来るようになります。

4. Super Properties を使って実装をシンプルにする

Mixpanel には Super Properties という仕組みがあり、一度設定しておくことでその後のイベントトラッキングの呼び出し時に、明示的に指定しなくても自動的にそのプロパティをイベントのプロパティとして送信してくれます。まず Super Properties を使わない以下のようなコードがあったとします。common property1 から common property3 までが、例えば利用プランといった、どのイベントにもつけておきたいプロパティです。

mixpanel.track("Event A", {
  "common property1": 1,
  "common property2": 2,
  "common property3": 3,
  "event specific property": "a"
});

mixpanel.track("Event B", {
  "common property1": 1,
  "common property2": 2,
  "common property3": 3,
  "event specific property": "b"
});

これを Super Properties を使うと以下のようになります。

// Super Properties の登録。ここでは 30 日間有効なものとして設定
mixpanel.register({
  "common property1" : 1,
  "common property2" : 2,
  "common property3" : 3
}, 30);

mixpanel.track("Event A", {"event specific property": "a"});
mixpanel.track("Event B", {"event specific property": "b"});

こうすることで、共通的にセグメントしたいプロパティの設定し忘れを防ぎ、またイベントそのもののトラッキングをシンプルにすることが出来ます。

尚、この Super Properties は Cookie に設定されるものなので、ブラウザが変わると変わり得ます。ですので一定期間有効にしたとしても、例えばサインイン時には毎回設定をしておく、といった事をしておくとよいでしょう。

5. トラッキング用のデータは別途取得する

アプリケーションには必要ないけれど、トラッキングでは欲しいデータが必要になることがあります。Cacoo での例を一つあげると、「ステンシルの名前」のような多言語化されたリソースをトラッキングするときに、アプリケーションは翻訳されたデータだけが必要な一方で、トラッキングは英語で統一したい、ということがありました。

素早く実装するのであれば、アプリケーションで翻訳されたリソースを取得するところに英語のデータも含める手はあったのですが、アプリケーションデータにトラッキング用のデータを混ぜ込んでしまうと、今後のメンテナンス性を損なう可能性があったため、英語のリソースは Ajax で非同期で別途取得するようにしました。

結果として、アプリケーションの本流の処理に手をいれることもなく、追加したデータを取得する部分でも無理な SQL を発行せずにシンプルに保つことができました。多少のパフォーマンスのトレードオフはあるかもしれませんが、トラッキング用のデータが通常のアプリケーションの処理フローでは取れないケースでは、無理せず別途取得するほうがメリットが大きいと感じています。

6. Data Export API を活用する

Mixpanel には標準のトラッキングの API とは別に、Mixpanel 上に保存されているデータを取得するための Data Export API があります。Data Export API は標準の API とは別のエンドポイントをもち、認証も API Secret を使うといった違いがあります。またクライアントライブラリも標準ライブラリに比べて少なく、そのコードをコピーして利用するようなシンプルなものが提供されるにとどまっています。

ですが仕様はシンプルですし、これを利用すると標準の API や Mixpanel の画面上ではできないことも自動化できるので、一度先の Data Export API のページを眺めておくと良いと思います。ここでは、Cacoo でのユースケースを二つほど紹介します。

データを定期的に削除する

Cacoo のようなフリーミアム型のサービスの場合、登録したものの残念ながら使わなくなってしまうユーザが一定の割合で存在します。Mixpanel の課金はイベントの総数と登録されているユーザのプロフィール数に基づくので、離脱したユーザの情報を保持しつづけると利用しているプランの上限を超えてしまう事もありえます。Mixpanel の画面上でそういったユーザの検索は出来ますが、削除は一つずつ手動で行わないといけないため、件数が多くなると現実的ではなくなります。

そこで、Data Export API の engage で指定条件に従うユーザを抽出し、標準の API の mixpanel.people.delete_user を個々に呼び出すことで、定期的に離脱してしまったユーザのデータのスクリーニングを行っています。条件を絞り込むための where パラメータの書き方 がぱっと見ややこしく、最初苦戦しましたのでここで Python のサンプルコードを紹介します。

例えば Paid という有料かどうかの boolean 値を持つプロパティがあったとして「最後に画面を見たのが x 日より前でフリープランのユーザ」という条件を組み立てるコードは以下のようになります。

def delta_from_now(delta):
    now = datetime.now()
    return int(time.mktime(now.timetuple())) + delta

days_ago = delta_from_now(-24 * 60 * 60 * x) # x 日前を UNIX タイムで取得する
where = 'properties["Paid"] == false and number(properties["$last_seen"]) < {0}'.format(days_ago)

前日のトップ10イベントを Typetalk にポストする

Mixpanel で取得しているデータは、基本的にアプリケーションがリアルにどのように使われているか、というのを示すデータです。私のようなグロースをミッションとして持っているスタッフとしては「数字」の感覚を養いたかったことと、こういった情報はプロダクト開発にも役立つところもあると思い、開発メンバーやその他のスタッフも見れる Typetalk のトピックに前日発生したトップ 10 イベントをポストするボットを作成しました。

これには文字通り top という API を使ってデータを取得しています。こういったレポーティング用途には JQL という機能が最近追加され、そのあたりが今後は使いどころは増えていきそうな気配です。


Mixpanel は非常に簡単に導入ができ、またその導入によって様々な視点を得ることができます。複雑な SQL を叩いてようやくデータが見れるといった状況や、もしくはデータがまったくない状態と比べると、意思決定の速度や質を変えることが出来るツールだと思います。

ただ簡単に導入出来るとはいえ、特に既存のユーザを抱えるサービスに導入する場合にはエンジニアリングの視点から気になる部分があるのも事実です。この記事がそういった際に少しでも役に立てば幸いです。

ヌーラボでは、データをマーケティングやプロダクト開発に活用して、サービスや会社の成長に貢献したい!というグロースハッカーを絶賛募集しております。

カバーイメージ : Growth – Thank you! by Susana Fernandez

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる