ゆーかん

徒然日記

react-nativeで星型とか、三角形とか、吹き出しとか出したくなった

すごく綺麗にまとめている人がいるのでこちらを参考にやっていって、疑問が出たら調べるのが一番かと。

http://browniefed.com/blog/the-shapes-of-react-native/

まぁ簡単に言うと、CSS3と同じ様に実装ができる。とはいえCSS3は初めてなので、補足知識を以下にまとめていく。

3角形の作り方

ネットにはたくさん良いコンテンツがあるんですね〜。ここに関してはここにたくさん解説がでている。

星型

この辺までくると、css3のpositionの概念の理解が必須になってくる。ここが割とまとまっている。

コード全体

react-native init StylePlaygroundして、index.ios.jsをこれに書きかえれば、楕円、三角、逆三角、矢印、星型までは見れるはず。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ScrollView,
} from 'react-native';

export default class StylePlayground extends Component {

  renderOval() {
    return <View style={[styles.oval, styles.base]}/>
  }

  renderTrianglePortrait() {
    return <View style= {[styles.triangle, styles.base ]}/>
  }

  renderTriangleOddPortrait() {
    return <View style= {[styles.triangle, styles.base, { transform: [{ rotate: '180deg' }]} ]}/>
  }

  renderCurverdTailArrow() {
    return (
      <View style={styles.curvedTailArrow}>
        <View style={styles.curvedTailArrowTail}></View>
        <View style={styles.curvedTailArrowTriangle}></View>
      </View>
    );
  }

  renderStarFive() {
    return (
      <View style={styles.starfive}>
        <View style={[styles.triangle, styles.base, styles.startfiveTop]}></View>
        <View style={styles.starfiveBefore}/>
        <View style={styles.starfiveAfter}/>
      </View>
    );
  }

  render() {
    return (
      <ScrollView contentContainerStyle={styles.container}>
        { this.renderOval() }
        { this.renderTrianglePortrait() }
        { this.renderTriangleOddPortrait() }
        { this.renderCurverdTailArrow() }
        { this.renderStarFive() }
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  base: {
    margin: 10,
  },
  oval: {
    width: 100,
    height: 100,
    borderRadius: 50,
    backgroundColor: 'gray',
    transform: [
      {scaleX: 2}
    ],
  },
  triangle: {
    width: 0,
    height: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderLeftWidth: 50,
    borderRightWidth: 50,
    borderBottomWidth: 100,
    borderLeftColor: 'transparent',
    borderRightColor: 'transparent',
    borderBottomColor: 'gray',
  },
  curvedTailArrow: {
    backgroundColor: 'transparent',
    overflow: 'visible',
    width: 30,
    height: 25,
  },
  curvedTailArrowTriangle: {
    backgroundColor: 'transparent',
    width: 0,
    height: 0,
    borderTopWidth: 9,
    borderTopColor: 'transparent',
    borderRightWidth: 9,
    borderRightColor: 'red',
    borderStyle: 'solid',
    transform: [
      {rotate: '10deg'}
    ],
    position: 'absolute',
    bottom: 9,
    right: 3,
    overflow: 'visible',
  },
  curvedTailArrowTail: {
    backgroundColor: 'transparent',
    position: 'absolute',
    borderBottomColor: 'transparent',
    borderLeftColor: 'transparent',
    borderRightColor: 'transparent',
    borderBottomWidth: 0,
    borderLeftWidth: 0,
    borderRightWidth: 0,
    borderTopWidth: 3,
    borderTopColor: 'red',
    borderStyle: 'solid',
    borderTopLeftRadius: 12,
    top: 1,
    left: 0,
    width: 20,
    height: 20,
    transform: [
      {rotate: '45deg'}
    ]
  },
  startfive: {
    width: 150,
    height: 150,
  },
  startfiveTop: {
    position: 'absolute',
    top: -45,
    left: 37,
  },
  starfiveBefore: {
    backgroundColor: 'transparent',
    position: 'absolute',
    left: 0,
    top: 0,
    borderStyle: 'solid',
    borderRightWidth: 100,
    borderRightColor: 'transparent',
    borderBottomWidth: 70,
    borderBottomColor: 'red',
    borderLeftWidth: 100,
    borderLeftColor: 'transparent',
    transform: [
      { rotate: '35deg'}
    ]
  },
  starfiveAfter: {
    backgroundColor: 'transparent',
    position: 'absolute',
    top: 0,
    left: -25,
    width: 0,
    height: 0,
    borderStyle: 'solid',
    borderRightWidth: 100,
    borderRightColor: 'transparent',
    borderBottomWidth: 70,
    borderBottomColor: 'red',
    borderLeftWidth: 100,
    borderLeftColor: 'transparent',
    transform: [
      { rotate: '-35deg'}
    ],
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('StylePlayground', () => StylePlayground);

続きはまた自由研究が進んだら。

react-nativeで、ajax通信の結果を、setStateしたい時のthisのbindの仕方

reactと組み合わせてjqueryを使っている時に、ajaxでPOST通信を送ろうとした。この時のmthisのbindの位置が直感とはずれてたので、メモ。

thisにjqueryの関数自身が入る

export default class Example extends React.Component

  constructor() {
    this.example = this.example.bind(this);
  }

  example() {
    $.ajax({
      type: 'POST',
      url,
      data: JSON.stringify(data),
      success(res) {
        // ここのthisの話
        this.setState({ res });
      },
      error(error) {
        // ここのthisの話
        this.setState({ error });
      },
    });
  }
}

そりゃそうか。直さなきゃ。

thisにjqueryの関数自身が入る〜Part2〜

ajaxメソッドを読んだ時に、thisをbindしなきゃいけないから、きっと関数の閉じカッコの後ろにつけてあげればいいんだろうな。。。から。

export default class Example extends React.Component

  constructor() {
    this.example = this.example.bind(this);
  }

  example() {
    $.ajax({
      type: 'POST',
      url,
      data: JSON.stringify(data),
      success(res) {
        // ここのthisの話
        this.setState({ res });
      },
      error(error) {
        // ここのthisの話
        this.setState({ error });
      },
    }).bind(this);
  }
}

しかしこれもajax関数自身がthisに入ってくる。

そんで以下を試したら解決

解決策

結局綺麗に(?)bindするのが無理だったので、変数に格納して取って置くことにした。すっきりしないけど、困ったら愚直に

export default class Example extends React.Component

  constructor() {
    this.example = this.example.bind(this);
  }

  example() {
    const _this = this;
    $.ajax({
      type: 'POST',
      url,
      data: JSON.stringify(data),
      success(res) {
        // ここのthisの話
        this.setState({ res });
      }.bind(this),
      error(error) {
        // ここのthisの話
        this.setState({ error });
      },
    }).bind(this);
  }
}

あれ、no_underscore_dungleのlinter errorでてる。。。(気にしない)

店舗の会員権をすぐ無くす人に嬉しい、売買可能なオンライン取引所が誕生するらしい

面白いサービスを見つけたので、メモがてらに残しておく。

spotsaleというサービスで、店舗が発行する会員権のオンライン取引所らしい。

最近、Valuやtimebankなど従来取引できなかったものをプラットフォームを介して所有・売買が可能なサービスが流行ってる。多分そこから着想されたサービスだと思う。

何が嬉しいって、会員権をすぐに無くすし、管理する気すらさらさらない僕のような人は、アプリ1つでまとめていくことができるのではないでしょうか。

しかも、たまたま立ち寄った自分にとって価値の低い店舗の会員権を、相対的に価値を高く感じる人に対して販売できる。なんてお得な。

まだサービスの詳細はわからないけど、楽しみに待ちたいと思います。

ちなみに僕の招待コードは「uPuM」で、こちらから登録できますよ!

react-native, android, circleCI2.0, fastlaneでdeploy gateへのテスト版自動配信

情報が足りなくてとても辛かったので、情報共有に。

システムの作動順序

  1. githubのstagingにPR
  2. merge
  3. circleCIでbuild用に必要な環境構築
  4. fastlane経由ででdeploygateへのビルド&デプロイコマンド叩く

書くことが増えるのも嫌なので、ここでは3.のcircleCIの設定だけにフォーカスする。

設定ファイル

諸々の説明は公式ドキュメントを参考にした。

version: 2
jobs:
  build:
    docker:
      - image: circleci/android:api-25-alpha

    working_directory: ~/client

    environment:
      JVM_OPTS: -Xmx3200m
      ANDROID_HOME: /opt/android/sdk
    branches:
      only:
        - staging
    steps:
      - checkout
      - run:
          name: install android ndk
          command: |
            wget https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
            sudo apt-get install p7zip-full
            7z x android-ndk-r10e-linux-x86_64.zip
            rm android-ndk-r10e-linux-x86_64.zip
            sudo mv android-ndk-r10e /opt/android
            export ANDROID_NDK_HOME=/opt/android/android-ndk-r10e
            export PATH=${PATH}:$ANDROID_NDK_HOME
            curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
            sudo apt-get install -y nodejs
            npm install
            sudo apt-get install ruby2.3 ruby-dev make g++
            sudo gem install fastlane
            cd $HOME/client/android && fastlane android dg
      - store_artifacts:
          path: app/build/reports
          destination: reports

はまりポイントは、次の点くらいかな、、と思います。

  • nodejsをdebian環境にインストールする
  • android ndkのパッケージをどこからダウンロードするか

あれ、だいぶ薄い記事になったな。。

linuxに、root userでも使えるpyenv環境の構築

ほとんどPyenvによるPython3.x環境構築(CentOS, Ubuntu)のモノマネですので、環境構築をしたい人は上記を参考にしてください。メモです。

背景

  1. ansibleをpipでインストールしようとする
  2. pyenvで管理されてるpythonとシステム pre-install pythonが呼ばれたり呼ばれなかったりでうまくいかない。
  3. 全部pyenvでインストールしてるものを使ってしまいたい。

やったこ

ec2上にpythonのインストール。systemにもともとインストールされているpythonに何もかも突っ込んで言って辛い思いをした過去があるので、できればバージョン切り替え用のパッケージをかませたい。

結果pyenvを導入した。多くのプロジェクトが同一サーバー化で動くことは想定していないので、仮想環境構築はしない。(virtualenv等)

やらなかったこ

virtualenv/pyenvなど仮想環境構築ツールの導入。

コード

pyenvインストール

PyenvによるPython3.x環境構築(CentOS, Ubuntu)にある通りです。下記コマンドで起動スクリプトを作成。

export PYENV_ROOT="/home/ec2-user/.pyenv"
export PATH="${PYENV_ROOT}/shims:${PYENV_ROOT}/bin:${PATH}"

この後に設定を反映させるために下記コマンドを実行。

source /etc/profile.d/pyenv.sh

ログインし直した時も、この設定は反映されるようなので、一度実行すればきにする必要はなさそうです。

豆知識程度に補足をすると、/etc/profile.dにはアプリケーション毎の設定を記述していくそうです。ここに設定を書いておくと、ログインユーザに関係なく起動時に読み込んでくれます。(下記手順)

  1. /etc/profileを実行
  2. /etc/profileによって、/etc/profile.dディレクトリ配下のすべてのファイルを実行
  3. ログインユーザーのホームディレクトリにある~/.bash_profileを実行
  4. ~/.bash_profileによって、~/.bashrcを実行
  5. ~/.bashrcによって、/etc/bashrcを実行

sudoでもuserのパス設定を引き継げるように設定

あとは、ログインユーザからsudoで実行した時に、path情報とかを引き継げるように設定をする。

これをやらないと、なんかしらのscripthがpythonコマンドを叩こうとした時に、pyenvが呼ばれないことがある。visudoを利用して設定ファイルを書き換える。

$ visudo 

~~~  編集モードに入るので、下記のように変更  ~~~

# Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin  コメントアウト
Defaults    env_keep += "PATH"  # 追加
Defaults    env_keep += "PYENV_ROOT"  # 追加

fabricでデプロイ自動化

独学マンなので、書き方等ご指摘頂けますと幸いです。

背景

までできていた。デプロイ作業を失敗しそうなので、自動化した時の備忘録。自動デプロイツールとしてfabricを利用。

ちなみに現状、fabric本家はpython3系をサポートいません。参考: FabricのPython3への対応についてそのためforkされたprojectから、インストールする必要があります。

pip3 install fabric3

ディレクトリ構成はこんな感じ。シンプル。

...
├── fabfile
│   ├── __init__.py  # module読み込み
│   ├── deploy.py    # 基本処理の記述
│   ├── envs.py      # 基本設定を記述
...

基本処理の記述

from fabric.api import run, abort, env, cd, local
from fabric.decorators import task


class LocalHandler(object):
    """localでのデプロイ周りの処理"""

    def push(self):
        """localの変更をgithubへpushする
        commitまではされている想定
        """
        self.__check_has_commited()
        self.__check_on_master()
        self.__update_requirements()
        local('git push origin master')

    def __update_requirements(self):
        """requirements.txtをupdateするスクリプト"""
        req_txt = 'requirements.txt'
        local('pip freeze > {}'.format(req_txt))
        res = local('git ls-files -m', capture=True)
        if res == req_txt:
            local('git add {}'.format(req_txt))
            local('git commit -m "update requirements.txt"')

    def __check_has_commited(self):
        """変更がちゃんとcommitされているかを確認"""
        res = local('git ls-files -m', capture=True)
        if res:
            abort('there are some diff on git supervised files')

    def __check_on_master(self):
        """master branchにいるか確認"""
        branch = local('git rev-parse --abbrev-ref HEAD', capture=True)
        if branch != 'master':
            abort('you should call this on master branch')


class RemoteHandler(object):

    def pull(self):
        """master branchに移動してソースコードの更新"""
        with cd(env.app_path):
            self.__check_no_change()
            run('git checkout master')
            run("git pull origin master")

    def restart_daemon(self):
        """svcを使って、restartさせる"""
        self.__install_required_package()
        res = run('supervisorctl restart {}'.format(env.daemon_name))
        if res.failed:
            abort('REMOTE: fail restart daemon {}'.format(env.daemon_name))
        return res

    def __install_required_package(self):
        req_txt = 'requirements.txt'
        with cd(env.app_path):
            run('pip3 install -r {}'.format(req_txt))

    def __check_no_change(self):
        """変更がちゃんとcommitされているかを確認"""
        res = run('git ls-files -m')
        if res:
            abort('REMOTE: there are some diff on git supervised files')

@task
def deploy():
    LocalHandler().push()
    rh = RemoteHandler()
    rh.pull()
    res = rh.restart_daemon()
    if res.succeeded:
        print("SUCCESS!! WELL DONE")
    else:
        print("TRY ONcE MORE :D")

基本設定を記述

from fabric.api import env
from fabric.decorators import task

@task
def production():
    """ 本番 """
    env.environment = "production"
    env.app_path = '/path/to/project_root'
    env.daemon_name = 'supervisor daemon_name'
    env.user = 'ec2-user'
    env.hosts = ['host_name']
    env.key_filename = '~/.ssh/key.pem'

module読み込み

このままだと、プロジェクトルートからfabricのタスクを呼べない。init.pyにimport書いてモジュール化する。

from fabfile.envs import production
from fabfile.deploy import deploy

実際に自動デプロイをする

下記のコマンドをローカルで叩く。

$ fab production deploy

Facebookグロースハックチームが行った、グロースハックチームを会社全体で行うための試行錯誤

過去に作っていたメモをブログを作ったのでまとめてあげています。

QuoraにまとめられたFacebookのグロースチームに所属するAndy Johns人物が直接回答した有名なWhat are some decisions taken by the “Growth Team” at Facebook that helped Facebook reach 500 million users.というスレッドがある。

Andyの回答は日々の意思決定に焦点をあて、戦術・雇用・戦略・文化作りの4つの軸から、如何にしてたったの4年間でユーザー数を5億人にまで伸ばしたかについて具体例を交えながらまとめていた。

Andyのまとめだけもとても秀逸なのだが今回は、先輩からのリクエストを元にして以下の3つの異なる軸からこのスレッドを切り取ってみたい。若干、掲載するプラットフォームが変だっていうツッコミが入りそうな感じはするけど、こういうのはエンジニア主導で行われていくべきものだし、見ていて損は無い情報だろうから恐れずポストした。

  1. KPI策定に必要な要素とは
  2. PDCAサイクル高速化のためには
  3. 彼らの行った具体的な施作

運営するブログはこちら:努力1mm

本題に入る前に、、

本題に入る前には2010年時点でのFacebookのグロースチームに関する情報とFacebookの現状ユーザー数について少し触れておきたいと思う。

Facebookのグロースチームは当時、国籍のバラバラな30人ほどのメンバーが所属していた。このグロースチームが買収やパートナーシップ、FBページのマルチリンガル化も含めた、最適化の全責任を負っていた。その中には、なども含まれていた。残念ながら現在のグロースチームの情報は探せなかったので、知っている人がいたらどなたかFacebookかコメントにて教えて頂けたらとても嬉しいです。。

2006年に誕生したFacebookは2010年までにユーザー数5億人を達成。そこから2015年末までで、ユーザー数を3倍の15.9億人までに伸ばした。直近の2016年7月までに更に1億人を獲得し、17億人を超えた。これにはLGBTの人の要請を請けてFacebookが実名ポリシーを緩和したことなんかも影響しているのかもしれない。

01.jpg

出典:TechCrunch:facebook earnings Q4

それでは本題へ!

グロースチームのKPI 策定方法

実際に何かしらのサービスの最適化を行うとなっても、その第一段階である問題設定からKPI策定までの手順が分からなかったりすることが多い。中にはKPI策定した段階で「買収がベストプラクティスだ」という答えが出ることもあるが、ここではショートカット無しの自力での成長戦略に関するKPI策定手順をまとめてみる。

Facebookユーザー数増加のためのKPI策定の手順について

この場合のKPI策定の第一ステップは、課題をいくつかの小さな問いに分割していくことから始まる。今回はユーザー数増加を大きな課題としているので、KPIは少なくとも未登録・登録したて・利用中・利用停止の4段階のユーザーを対象に設定される必要がある。

対象が明確になれば、以下の4つをKPI策定への細分化された問いとして定義できる。 1. どうやって新規ユーザー獲得率を増やすか 1. 新規ユーザーをできるだけ早くアクティブユーザーにするための方法 1. アプリ使用時間を高めるためのトリガーはを引っ張る方法 1. 流出したユーザーを引き戻すためにどんなことができるのか

これはAndyが実際に作ってくれたカスタマージャーニーと、各段階のユーザーに対する課題に対する図で、なんとなくイメージをつけるのに最適かもしれない。

02.png

出典:Quora:What are some decisions taken by the "Growth team" at Facebook that helped Facebook reach 500 million users?

大抵の場合は、これらの問題の解を探しているうちに「新規ユーザー獲得にはFacebookの写真をアップロードできる機能を拡張すればいいんだ!」といった具体的な答えが見つかる。その答えが見つかったら、次の2つの手段からどちらが効率良いか考える必要がある。

  • 既存のユーザー接点の価値を高める
  • 新しいユーザー接点を0から作る

例えば写真の例でいくならば、既存の写真アップロード手順の簡易化と、Google Photoからのインポート機能を作成といった、2つのチャネルについて考えることができる。

ユーザーに登録を促すようなサービスを提供しているのであれば、Facebookのグロースチームが実際に見つけた次の例から、ユーザー接点最適化のヒントを得ることができるかもしれない。

  • 招待メールの絶対量を増やす
  • 既存ユーザーによる招待メールの送信を促す
  • 招待メールのCTRを高める
  • 既存ユーザーによる招待メールのCTRを高める
  • ログアウトページのデザイン性
  • 登録フォームの最適化
  • 登録前のアカウント認証メールの改善

ユーザー獲得のためのKPIの策定は積み上げ棒グラフのようなもので、それぞれのチャネルが少しでも改善すれば、全体としては莫大な効果を発揮することになる。チャネルの数を増やせば増やすほど、全体のユーザー数もどんどんと増えて行く。

もちろんただ単にチャネルを増やせば成功する訳ではない。増やしたならば最適化をしなくてはいけないし、そのチャネルの評判が悪ければ逆効果になるかもしれない。

チャネルを増やし、それぞれの質を高めるためには、失敗を恐れずにチャレンジを繰り返し、リスクを取りながらPDCAサイクルを高速で回していくことが必要とされる。もちろんFacebookのグロースチームもPDCAサイクル高速化のための工夫を行っていた。

PDCAサイクル高速化のための取り組み

僕は本田圭佑が好きで、彼に関する記事などは頻繁に目を通している。彼のまっすぐな姿勢というか折れない精神とかは天性のもので自分には無いものだし、何より有言実行だしかっこいい。サッカー選手でありながら自分で経営もしてるあたりもとても好き。そんなビジネスにも手を伸ばしている本田圭佑が、目標達成に関することでこんなことを述べていた。

挫折は過程、最後に成功すれば挫折は過程に変わる。

だから成功するまで諦めないだけ。

成功をいち早く獲得するためにはできるだけたくさんチャレンジすることが必要で、そのためにはリスクテーキングは欠かせない。誰しもリスクを取ることはなるべく避けたいと願うが、企業として成功するためにはリスクを取りチャレンジをたくさんして、PDCAをできるだけ高速で回すことが必要だ。

上記のことを叶えるためにAndyは、Facebookで実践していた3つのポイントを挙げている。それは小手先の短期的な努力で結果の見えることではなく、長期間継続的に続けていく必要のある組織作り的な面が多かったことが印象的だった。

  1. 経営陣とグロースチームの距離感
  2. カルチャー作り
  3. M & A

経営陣とグロースチームの距離感

memeboxのデータサイエンスによる業務改善例で述べたようにデータによるグロースチームは経営陣に近い距離にある必要がある。Andyもグロースチームと経営陣が近い関係にあったことに写真までつけて触れていた。これはグロースチームのリーダーであるChamathザッカーバーグを含めた経営陣が仲良く話している瞬間が切り取られている。

03.png

出典:Quora:What are some decisions taken by the "Growth team" at Facebook that helped Facebook reach 500 million users?

グロースチームが経営陣と近い距離を必要としている理由には、成長(グロース)の特性が影響している。成長は1部門として認識される実行されるべきことではなく、経営者のような視点から全ての部門に対して、企業ミッションを達成するために実施される必要がある。この写真からも分かるようにFacebookのグロースチームも、経営陣からの手厚いサポートを受け、部門横断的な視点から成長のための最適化を行っていたようだ。

最後にAndyは、企業の成長のために経営者がするべき意思決定で、何よりも大切なものがあることに触れた部分を引用したいと思う。それは壮大な企業ミッションの策定でもなく、グロースチームの会社内でのあるべき姿に触れる象徴的な内容だった。

グロースをエンジニア・オペレーション・企画チームの日々の活動において、最も根本的な要素と位置付ける。経営者がするべき最も大事な意思決定は、企業でのグロースのポジション設定にあるのかもしれない。

カルチャー作り

国際色豊かなグロースチームのオフィス天井には、メンバーの出身国の国旗が掲げられている。

04.png

出典:Quora:What are some decisions taken by the "Growth team" at Facebook that helped Facebook reach 500 million users?

グロースチームのオフィスの天井には、これらの国旗と並んで2つだけ、どこの国にも属さない旗が掛けられていたという。そこに掲げられていたのはグロースチームのメンバーが胸に刻むべき使命であり、リスクテーキングに恐怖を抱く人を奮い立たせるような内容になっていた。

GO BIG OR GO HOME

UP AND TO THE RIGHT

他にもありとあらゆる場所にメッセージは散りばめられていて、有名なDONE IS BETTER THAN PERFECTなんかは、オフィスの壁に書かれていたメッセージのうちの1つであった。

05.png

出典:Quora:What are some decisions taken by the "Growth team" at Facebook that helped Facebook reach 500 million users?

このような文化作りや経営陣によるサポートのような組織作りの取り組みが、高速でPDCAサイクルを回すためには役に立つのかもしれないなー、、と思ったりもした。続いて少し違ったPDCAサイクル高速化(というかショートカット?)の例を挙げてみる。

M&A

何かしらの目標があってなおかつ資金が潤沢にあるならば、企業を買収することもPDCAサイクル高速化の1つの手段かもしれない。Andyは実際にFacebookのグロースチームが主導で、企業の買収やパートナー提携が行われたと述べていた。

ここではFacebookが実際に行った、友人レコメンド機能拡張に向けた、企業買収によるサイクル高速化の具体例を挙げる。

Octazenの買収

Facebookのミッションは世界中の人を繋げることだ。そのためにはソーシャルグラフのような人間関係のリストを作成することは必須だった。そしてこのゴールに向かうためにFacebookが選んだのは、自前でのソーシャルグラフ作成ではなく、その情報を持っているOctazenというマレーシアのスタートアップを買収だった。

Octazenは、友人情報などを引き出せる権利をユーザーに認めてもらい、その情報を元にユーザーの連絡先リストやソーシャルグラフを抜き出して人間関係の繋がりのリストを作っていた。

Facebookは買収したOctazenの情報を元にして、自社で取り組んだら数年はかかるソーシャルグラフを数ヶ月で完成させた。拡充した友人レコメンド機能によって、継続的なユーザー数の増加を可能にしたのだ。

目的達成のためにPDCAの高速化を図ることは確かに大切なのかもしれない。でもサイクルを回し続けるのは工数もかかるし、精神的にも骨の折れる仕事だ。このような目標が現れた時、買収によるPDCAプロセスのショートカットも1つの手段として有効なのだろう。

具体的な施作

ユーザー5億人獲得のためにはいくつもの最適化を実践し、中には特別な施作もあったようだ。しかしAndyが強調して述べていたことはとてもシンプルなことで、memeboxのデータサイエンスによる業務改善例でも書いた、教科書通りのことを淡々とやり続けることだった。

僕たちが取り組んだ施作で、伝えていかなきゃいけないことは山ほどあるよ。でもね、そのほとんどは特別なものじゃなくて、マーケティングの教科書に載ってる内容なんだ。テストして、本番で試してみて、フィードバックを元に洗練する。これを繰り返していただけさ。

実際にAndyが上げていた具体例を挙げる前に、ここに書かれていることを愚直に実行してもうまくいく訳がないということには注意しておかなきゃいけない。プラットフォームだけでなく、時代によってもユーザーの行動は変化していく。これらを参考に、自社サービスで最も有効なように最適化を図っていくことが大事だと肝に銘じておきたい。

ここではその最適化の例を箇条書きでまとめていく。どのようなプロセスでこれらの具体例が導かれたかも丁寧に説明されているのだが、既に内容的には盛り沢山だし、また別の機会にそのプロセスについてはまとめたいと思う。以下が最適化の具体例だ。

  1. 文章よりもボタンを使おう
  2. ボタンの位置と言葉には気をつけよう
  3. 位置だけじゃなく、サイズ・色・影なども大切!
  4. 速さは正義。速度の遅いサイトはCV率を意識していないも同然
  5. 価値のあるコンテンツを作るために、見出しやサブ見出しを充実させよう
  6. 文字を使いすぎないこと。シンプルで簡潔なサイトを
  7. 色使いはグロースハッカーのたしなみ
  8. 画像はCV率を高める。効率良く使う方法をテストしよう
  9. ユーザーへの感謝を上手に述べれば、CV率もアップ
  10. 正しく使われれば、商品説明の動画はとても効果的だ
  11. ユーザー登録ホームはシンプルに。自動入力が可能な構成にしよう
  12. 所属欲求などの人間の欲求を上手に使おう(ex. 友達N人がサービスを使ってます!)
  13. メールの件名・送信元アドレス・日時のABテストは抜かりなく
  14. htmlメールよりもテキストメールの方がCV率が高かった!
  15. 広告を上手に使おう。更にランディングページを効率化すると3倍もCV率があがることもある
  16. 広告の画像やメッセージを数日間隔でローテーションさせると更に効果が上がる
  17. Facebook広告をクリックするための手段もシンプルに
  18. AdwordsのCTRを上げるには動的にキーワードを入れられるようにしよう
  19. SNSシェアボタンの位置もABテストを
  20. SEOメタデータの最適化も忘れずに
  21. 2万件以下のAdwordsキャンペーンしかもたないなら、1キーワード対して1つの広告グループを作成しよう。これによって狙いのキーワードに対して重複なく広告を打てるし、Adwords広告のCTRを上げるにはこれが一番手っ取り早かった

ソース

What awesome top strategies for conversion optimization

What are some decisions taken by the Growth team at Facebook that helped Facebook reach 500 million users