asoview! TECH BLOG

アソビュー株式会社のテックブログ

グローバル決済Night!~越境ビジネスやインバウンド対策を知る~ に登壇した話


こんにちは。アソビューの並木です。
マイクラと猫に癒される日々です。

先日、stripe 様からの招待で以下のイベントに登壇させていただきました
内容について少し触れたいと思います。 connpass.com

東京海上日動様とストライプジャパン様の合同イベントでして

グローバル決済Night!~越境ビジネスやインバウンド対策を知る~

と銘打って

まず

楠谷 勝氏(東京海上日動火災保険株式会社 デジタルイノベーション共創部長)

ダニエル へフェルナン氏 (ストライプジャパン株式会社 代表取締役)

のお二人による

・決済処理のグローバルの動き(中国の顔認証決済のお話など)

・現在の市場について

・これからの動き

・不正決済

についてを座談会形式で実施しました。

その後、私並木が

ユーザー事例の紹介

として、誠に恐縮ながらユーザー代表として登壇させていただきました。

※※※内容がセンシティブなこともあり※※※
※※※一部情報は伏せます。ご了承ください。※※※

当日の様子を一枚だけ。。
手前味噌ですが、弊社の事例について、紹介をさせていただきました。
f:id:yusuke-namiki:20200128203654j:plain

会場は wework 様 の アイスバーグ
www.wework.com

机を並べた前に立つわけではなく、みなさん自由なスタイルで聴いていただき
初めて経験する、良い意味でのラフなスタイルでの登壇でした。
途中まで、イベント参加者ではない一般の利用者の方もいらっしゃいました

当日のお品書き(アジェンダ)

f:id:yusuke-namiki:20200128204351p:plain

どうでもいいんですが、アジェンダという言葉が使い慣れなくて
「お題」とか「お品書き」っていう日本語を使いたくなります。

最初はアクセス制御の話から

f:id:yusuke-namiki:20200128204556p:plain
ここはAWSのサービスを紹介しているだけなのでそのまま載せる&説明は省きます。

次に、stripe様のサービスを使った不正ガードについて事例を交えて

設定できる項目は実際にはもっと多く、細かな設定ができます。

f:id:yusuke-namiki:20200128205240p:plain

次に、取引データから、チェックのためのリストを作成する仕組みについて紹介しました。

f:id:yusuke-namiki:20200128205551p:plain

簡単に説明すると

Jenkins

  • 特定のパスに格納されている、処理を実行する

  • 取得結果を特定のフォルダに格納

処理内容

  • 特定の期間を指定して、メタデータを取得して表示します。
    stripe の API リファレンス

stripe.com を参考に作成し、取得。

textファイル

  • 処理の実行結果がtext ファイルとして出力される

google drive

  • text ファイルを特定のフォルダにアップロード(これはどこでも大丈夫です)

まとめ

不正対策に終わりはないと思いますが、有効な対策は必ずその時その時で存在します。

一番の有効対策は諦めないこと

だと思っています。

イタチごっこだから。。といって対策をしないのは本当に勿体無いので
終わりのない戦いだとしても、皆さんで立ち向かいましょう。

最後におきまりのやつ。

アソビューでは、一緒に働いてもらえる仲間を募集中です! ゲストににワクワクを届けるため、最高のプロダクトを作っていこう!というマインドをもったメンバーがたくさん働いてます。 話を聞いてみたい!という方ははお気軽にご連絡ください。 最初は、会社の雰囲気知りたいーと言った気軽な感じでも大丈夫です。

www.wantedly.com

www.wantedly.com

CEC TOKYO イベントレポート

こんにちは。アソビュー!プロダクト開発チームでプロダクトマネジャーを担当している矢野です。

1月22日に開催されたCEC TOKYO(カスタマーエンゲージメントカンファレンス)に参加してきましたので、 社内共有も含めてテックブログにイベントレポートを書きたいと思います。

この記事は主に、マーケティングやカスタマーサクセスを業務にしている人向けの内容です。 多くのセッションに参加しましたが、個人的に学びの多かったセッションに絞ってレポートを書いています。

世界の“Customer Engagement”の潮流

Repro 平田氏 Amplitude 米田氏、Mr Jeferry Wang氏 Burger King Mr Preston氏

Amplitudeとは?

ネクストユニコーン。 エンジニア30人体制で、アプリ内のデータ、行動分析のしやすさを追求したプロダクト。 今回のバーガーキングのアプリに導入済みで、今回のセッションでAmplitudeの活用事例をお話いただきました

amplitude.com

バーガーキングのエンゲージメントの考え方

▼ ターゲットはどう定義してるのか?

バーガーキングのロイヤルカスタマーはいない。マックでもレストランでも行く人がターゲット。 オンラインで注文できることで、デジタルユーザー化していくことを目指してる。

▼ バーガーキングのマーケティング活動について

アプリで取得している情報は

  1. 行動ログ
  2. 位置情報
  3. 広告計測データ

⇒精度の高いターゲティングが可能になるため

▼ カンヌライオンズ2019を受賞したプロモーションの裏側

以前のアプリにはクーポン配布しかなかったが、新アプリからオンライン注文、決済ができるようになった。 オンライン注文、決済が新アプリでできることを認知してもらうためにプロモーションをおこなった。

▼プロモーションの内容は?

マクドナルドの店舗の半径600フィート内にはいると、ワッパーのクーポンがダウンロードできるという内容。 プロモーション詳しくは記事参照 bizseeds.net

▼ バーガーキングのマーケティング施策について

エンゲージメント評価(NSM ノーススター・メトリックス)を設定する

ユーザー毎のデジタルトランザクション数(つまりエンゲージメントも高いという裏付け)と設定

KPIの設定方法

ノーススター・メトリックスを最大化するためのKPIの落とし方はAmplitudeが定義したフレームワークを使って以下のように定義

  • 広がり(ユーザー数)
  • 深さ(エンゲージメントレベル )
  • 頻度(再訪頻度)
  • 効率(タスク完了までの速度)

それぞれのKPIに応じた施策

f:id:javeshi100:20200207172803j:plain

  • 広がり(ユーザー数)→ファネルコンバージョンの向上
  • 深さ(エンゲージメントレベル )→PUSH通知
  • 頻度(再訪頻度)→モバイルオーダーに特化したクーポン配布
  • 効率(タスク完了までの速度)→フリクションレスペイメントサービス導入

ノーススター・メトリックス 最大化のためのグロース運用

  • ノーススター・メトリックスを向上させているユーザー行動の特徴を分析する
  • ノーススター・メトリックスを向上させるマジックナンバーを求める
  • カスタマージャーニーから施策実行箇所を求める

いかに施策を早く回せるか?が重要。 バーガーキングでは学習回数を増やすことを意識して運用したそうです。

▼ マジックナンバーの見つけ方

マジックナンバーとは?

アプリ内で特定の行動をするとリテンションが上がる数値等

マジックナンバーの見つけ方?

性別、年齢などのデモグラデータだけではターゲティングが不完全だから、 アプリ内行動でセグメント化すると、良いインサイトが取れるとのこと。

たとえば、アプリ内の行動で成果のでている機能を特定する。 マジックナンバーが上がる機能を特定し、利用頻度が高くなるようにアプリを改善している

▼ 良いプロモーションは?

クラスターごとにプロモーションを分けて行っている。 たとえば、3ドルで反応するクラスター、2ドルで反応するクラスターなど、クラスターにあったキャンペーンを実施するなど パーソナリゼーションを活用し様々なタッチポイントをつくること。 コンテンツベースのパーソナリゼーションが有効

このセッションで学びまとめ

———

  • 囲い込みではなく、マインドシェア
  • マインドシェアを勝ち取るマーケティング施策の実行
  • 獲得からリテンション
  • 獲得はコストが高い
  • パーソナリゼーションによる、エンゲージメントの高いプロダクトを提供する

———

2020リテールビジネス最新予測!

株式会社ビジョナリーホールディングス 川添氏 コメ兵 藤原氏 FABRIC TOKYO 森氏 アスクル輿水氏

▼ リテールビジネスの課題

  • 商業集積地の家賃が上がる
  • 人件費の高等

→かわらない売上、コストの増加

いままで →いい場所にお店を出店する

これから →デジタル空間の中での集客と投資 ものを販売するお店はつくらない

▼ ケーススタディ

■コメ兵

店舗規模を縮小し、買取機能と、購入前の試着機能をリアル店舗で提供 オムニチャネルは物流の話 余剰在庫から物流への投資にシフト お客様がほしいときにお店に商品がある状態を作れれば良い 予約時点が一番欲しい状態、1週間後だといらないと感じちゃう。物流はUXで非常に大事

■ Fabric tokyo

リアル店舗のあり方がかわってきた。売り場ではなく、体験の場、サービスの価値提供の場。

Fabric tokyoでは全組織をデジタルネイティブにしていっている。 小売はサービス化していく(RaaS retail as a service) 弊社では、体型変動の保証を月額390円で何度でも対応するサブスクを開始した。

昨年やった施策としてリピート施策に振り切った カスタマーエンゲージメントを取り組んだ結果、新規会員も自然に増える。 なぜなら、リファラル、口コミから新規会員につながるから 今年もカスタマーエンゲージメント高める施策に振り切る。

今までの小売 →売ることがゴールだった

これからの小売 →関係性の構築

このセッションでの学び

購入から配送までもUXの一部 UXとして大事なことはお客様の時間を奪わないこと

まとめ

  • 新規獲得からリテンションに施策の優先度がシフトしてる
  • リテンションを上げるためにはエンゲージメント施策が大事
  • エンゲージメントを高める手段として、パーソナリゼーションを活用したターゲティングが必要
  • ターゲットのセグメントはデモグラ属性では不十分。アプリ内の行動をもとにセグメント化すると効果が高い
  • ユーザーから時間を奪わない。最短で目標を達成できることをUXとして盛り込む
最後に採用の宣伝をさせてください

アソビューでは、一緒に働いてもらえる仲間を募集中です! ゲストにワクワクを届けるため、最高のプロダクトを作っていこう!というマインドをもったメンバーがたくさん働いてます。 話を聞いてみたい!という方ははお気軽にご連絡ください。

www.wantedly.com

www.wantedly.com

canvasを使って動体感知。"あの"犯人(犬)を捕まえた!

犬アレルギーだけど犬が大好きな相原(@raihara3)です。

実家で定期的に起こる、ワンコのとある事件の犯人を捕まえるべく
canvasを活用して簡単につくった監視ツールの紹介です。

その名も「トイレ警察24時
名前の通り、トイレの話です。
汚い表現は極力控えていますが、苦手な方はご注意ください。


実家には現在3匹のワンコがいます。
そこで起こるワンコの事件というのは、
犬あるある「他の子の排泄物を口にしてしまう子」がうちにもいるんです...
母性本能で良い事なんですが、みんな成犬なのでやめて欲しい...

犯人はもう分かっています。(なぜ分かったかは省略)
でも、いつも人間の目を盗んで犯行を行う為証拠もなく、注意できずにいます。

そこで思いついたのが「トイレ警察24時」

概要

ざっくり。

f:id:raihara3a:20200113193334p:plain

(3)で差分がなければ(1)(2)を繰り返し、
差分がある場合は、被疑者が近づいては危ないので(4)(5)のような流れになります。
(差分 = 排泄物あり)

仕組み

※この先折り紙を丸めたものが排泄物として登場します。本物ではありません。

通常時の情報を取得

f:id:raihara3a:20200113193611p:plain
監視スタートして、15秒後に現在の状態を基準として取得します

setTimeout(() => {
  standardStatus = document.getElementById('toiret-canvas').getContext('2d').getImageData(0, 0, canvasSize.width, canvasSize.height).data;
}, 15000)

モーション検出

Diffy.jsというライブラリを使用しました。
フレームを少しずらしたWebカメラのスナップショットを取得し、
ハイコントラストの差分があれば"モーション検出"される仕組みです。

f:id:raihara3a:20200113193714p:plain
トイレに末っ子がやってきました。

import { create } from 'diffy.min.js';

const resolution = {
  x: 20,
  y: 15
}

let diffy = create({
  resolution: { x: resolution.x, y: resolution.y },
  sensitivity: 0.2,
  threshold: 21,
  debug: true,
  containerClassName: 'diffy-container',
  sourceDimensions: { w: 250, h: 200},
  onFrame:(matrix) => {
    matrix.forEach((row) => {
      const notWhiteCount = row.filter((color) => {return color !== 255}).length;
      if(notWhiteCount > resolution.x / 2.5) {
        // モーション検出
      }
    });
  }
});

window.diffy = diffy;

今回だとresolutionで指定している20 x 15に映像を分割し、
matrixに白〜黒のカラーコードが配列で渡されます。差分がなければ白が入ります。

モーション検出の条件に設定している割合は、
カメラからの距離や映り込む被写体の大きさによって前後すると思うので要調整です。

差分検出

動きがなくなった5秒後に最初の状態と変わりないか確認します。

const currentStatus = document.getElementById('toiret-canvas').getContext('2d').getImageData(0, 0, canvasSize.width, canvasSize.height).data;

for(let i = 0; i < currentStatus.length; i++) {
  if(standardStatus[i] - currentStatus[i] < -30 || 30 < standardStatus[i] - currentStatus[i]) {
    new Audio('chime.mp3').play();
    hasAbnormal = true;
    return;
  }
}

dataプロパティで取得したRGBAから+-30を許容値としました。
これは白のトイレシートと排泄物の明暗差からざっくり決めました。

f:id:raihara3a:20200113193824p:plain

差分が検出されると人間に向けた通知音を流します。

この段階で近くに人間がいて片付けができればいいのですが、必ずいるとも限らないので...

再度モーション検出

今が取り締まる瞬間です!
hasAbnormal = trueの状態でモーション検出すると、音と共にcanvasから写真のダウンロードリンクを生成します。
音といっても、あまりビックリさせても良くないので知らない犬の鳴き声にしてみました。
(これでもかなり反応する)

new Audio('dog.mp3').play();

const canvas = document.getElementById('toiret-canvas');
const aTag = document.createElement('a');
aTag.innerText = new Date();
aTag.download = new Date();
aTag.href = canvas.toDataURL("image/jpeg");
document.getElementById('evidence-box').appendChild(aTag);

この音に気を引かれてやめてくれるか、人間が駆けつけて止めるか、です。
被疑者の長女がやってきました。

f:id:raihara3a:20200113194043p:plain

しっかり音のする方を気にしてくれてます。

もし止めることができなくても、リンクから画像をダウンロードすればしっかり証拠が手に入ります。

f:id:raihara3a:20200113194216p:plain

排泄物がある状態ではモーションを検出している間、5秒おきに写真を撮り続けます。
その為ただトイレをしにきただけだった、という場合もちゃんと確認できます。冤罪防止。

しかし、いくら折り紙を丸めたと言ってもちょっとリアルだったかな...

さいごに

ワンコに後から注意することはできませんが、
これで「人間は見てないはずなのにバレる違和感」を感じてもらえればと思います...笑

毎回人間の目を盗んで犯行を行うなんて賢いけど、しっかり撮ってるからね...

f:id:raihara3a:20200113194311p:plain

これから取り締まっていこうと思います。

組織とアーキテクチャとマーケットの話

この記事はアソビュー! Advent Calendar 2019 - Qiitaの25日目、最後の記事になります。

メリー・クリスマス

CPOの江部です。アドベントカレンダーもいよいよ最終回となりました。僭越ながらトリを務めさせていただきます。

自己紹介ですが、総じて事業・プロダクト両面において道のないところに道をつくったり、空いてるスペースに走り込んだりする役割を担ってきました。目の前の扉は蹴り破るスタイルを得意としています。

今回のお題ですが、ゼロからの組織立ち上げを経てある程度の組織規模となった現状に至る中で感じたことを吟じてみたいと思います。相当な乱文かつ少し抽象度の高い話になりますが興味のある方はご一読ください。

生産性の高い開発組織とは

生産性の高い組織であるために必要なことはなにかを考えたときに、数年前くらいからマーケット、組織、アーキテクチャの3つが統合できている状態じゃないかなーと考えるようになりました。 ちょっとわかりにくいかもしれませんが簡易的な図で表すと下記のようなイメージです。

f:id:jjebejj:20191224014103p:plain
理想の状態

顧客に対して提供するサービスの境界が明確であり、それをどの組織がオーナーシップをもって作り上げるのか定義されている状態です。

  • 誰の何の課題を解決すべきか明確
  • チームがシステム(コード)に対してオーナーシップを持てている
  • 影響範囲が特定できる
  • スピーディーに変化できる

のではないかと考えています。

逆に、この状態が歪んでいると、様々な組織課題が現れます。 個人的な感覚ですが、これまで私達が直面した組織課題はほとんどこの歪みが原因で発生しているように思います。

例えば。

組織の歪み

f:id:jjebejj:20191224021802p:plain
組織の不和
「フロント」、「バックエンド」など技術レイヤで区切られた組織の場合に起こりやすいかとおもいます。 これにより起こる問題として

  • チームメンバーが向き合う先がバラバラ
  • 案件ごとのアサインで受託マインドになりがち
  • チーム間の足並みや認識が揃わずプロジェクトマネジメントに失敗する など。

アーキテクチャの歪み

f:id:jjebejj:20191224021235p:plain
アーキテクチャの不和
例えば組織はマーケットの境界で区切られているが、システムはモノリシックなケースなど。

  • あるシステムに対して異なる目的をもった複数チームがそれぞれ修正を加えることになり、システムとしての品質を維持するのが困難になる。
  • システムのコードに関する所有権・オーナーシップ・責任が曖昧になる。気づいたらデジタル九龍城がそこに生まれる。
  • 影響範囲がわからない。触りたくない。
  • 触った結果障害が起こる。

統合の順序

ではなにをガイドラインにしてマーケット、組織、アーキテクチャを統合していくのがよいのでしょうか。 答えはそのビジネスやフェーズによって違うのかもしれませんが、すくなくともいまのアソビューで言えば

マーケットに従った組織によってアーキテクチャを変化させる

のが良いのではと考えています。

例えばアソビューの場合、マーケットとしては大きく分けるとゲスト(消費者)、パートナー(レジャー事業者)、代理店(OTA・旅行代理店・媒体など)があり、さらにゲストはチャネル別、パートナーは商品別、代理店は業種別に分けられます。

これらは異なるKPIやビジネスライフサイクルでアプローチされるわけですが、それぞれに対して明確な責任をもったチームが組まれ、そのチームにおいて事業目標達成の手段としてアーキテクチャを最適化していくことにより、マーケット、組織、アーキテクチャが統合されていくというシナリオが理想かと思います。(まさにコンウェイの法則です)

分解ではなく凝集

得てしてスタートアップのサービス開始当初はエンジニア人数も時間も制約があるなかでモノリシックなアーキテクチャで構築されがちです。 それ自体はもちろんそのタイミングでは正解なのですが、事業成長し組織が複雑化するにつれアーキテクチャやコードが最後に変化に取り残されていきます。

そのアーキテクチャを最適化しようとする時、マイクロサービスの導入を検討するわけですが、往々にしていかに最適な要素に分解するか、という視点で議論しようとする向きがあります。誰かが横軸の技術視点で分解、整理することにより技術最適化を図ろうとしてしまいがちですが、そうではなくそれぞれのマーケットに従ったチームが自らのビジネスドメインに基づいて凝集させていくほうが、長期的に整理が付きやすく、実際にそれに向けてプロジェクトが動きだすことが多いです。

ここでいう凝集とは、ビジネスドメインを整理し、それを実現するシステムに必要なサブシステムや関連システムのデータ、機能などを取り込んでいくことにより最適なアーキテクチャをビルドアップしていこうというアプローチです。

一方の分解とは、様々なデータや機能が含まれるモノリスを、ある基準に従って境界線を引きながらマイクロサービスに切り分けることによってアーキテクチャを最適化しようという思考パターンです。

技術視点での分解は、機能的な重複がきれいに整理され一見効率のよいアーキテクチャのように見えますが、往々にして上述のような組織とマーケットとの乖離を起こします。 一方、ビジネスドメインに基づいて凝集させていく考え方であれば、ある程度のデータや機能の重複は随所に起こるものの、それぞれがマーケットや組織と連動した形となり、結果ビジネスやオペレーションを含めた全体視点でみると効率的となります。そして、それによって単なるアーキテクチャの最適化プロジェクトとしてぶち上げられるのではなく、具体的な業績向上、業務効率化を目的としたプロジェクトとして打ち出すことができ、エンジニア以外のチームやメンバーと目線を合わせて進めやすくなります。

(ちなみに、マーケットと表現するとSoE (System of Engagement)に限定された議論に捉えられやすいかもしれませんが、例えば基幹系システムなどSoR (System of Record)的なシステムでも、社内の運用部門をシステムやチームにとっての顧客ととらえれば同様の考え方が適用できます。)

あくまでマーケット起点での組織、アーキテクチャであるべきであって、既存の組織の形や技術的な効率だけを理由にしたアーキテクチャの設計、とくに異なるマーケットにまたがった低レイヤーの統合は、恒常的にアーキテクトチームなどが保守できる体制でなければなるべく避けたほうが懸命です。

さいごに

かなり纏まりのない文章になってしまいました。

まあこれは多分に理想論なので、人がたりないスタートアップだとどうやってもそんなきれいにはいかないよーとかあると思います。 我々も試行錯誤の連続のなかで振り子のように行ったり来たりしながら最適解を探し続けている状態です。

が、いろいろな組織課題に悩まれているマネジメントレイヤーの方々に、少しでも思考の整理や今後の方向性を考える上でなにがしかのヒントにつながればと思い書いてみました。

では、みなさん良いお年を。 来年もアソビューをよろしくお願いいたします!

Spockを使ったユニットテスト

アソビュー! Advent Calendar 2019 - Qiitaの23日目の記事になります。
はじめまして。
アソビュー!のサーバーサイドエンジニアの山野です。
弊社ではSpockを使用したユニットテストを行なっています。
今回はその実装方法について備忘録も兼ね、書きたいと思います。

Spockとは

SpockはGroovyで動作するJava・Groovyアプリケーション向けのテストフレームワークで、
以下のような特徴や機能があります。
・テストケース内がブロックで明確に分かれており、
 テストケース内にテーブル形式でテストデータを定義できるため、テストの内容や仕様が分かりやすい。
・テスト結果確認(アサーション)が簡潔に記述できる。
・Mockの仕組みが標準で用意されている。
http://spockframework.org/

基本のテストコード実装例

テストクラス内のメソッドとブロックは以下のような形で実装します。
・テストクラスはSpockのSpecificationクラスの継承が必要です。
・テストケース内のsetupgivenと宣言してもよく、
 expectブロックを使えばテスト対象の実行と結果確認一緒に行えます。
・またテーブル記述を使ったテストを行う場合は、whereブロックが必要です。(後述します)

class RecommendItemSpec extends Specification{

    def setupSpec() {
        //テストクラスで一回だけ実行される初期化処理
    }

    def setup() {
        //テストケースごとに実行される初期化処理
    }

    def "テストケース"() {
        setup:
        // テストケース内の初期化/前提条件の設定など

        when:
        // テスト対象の処理実行

        then:
        // テスト結果確認

        cleanup:
        // テストケース内の後処理

    }

    def cleanup(){
        // テストケース毎に実行される後処理
    }

    def cleanupSpec(){
        // テストクラスで一回だけ実行される後処理
    }
}

[実装の具体例]
商品の単価と個数を渡すとその合計金額を返却するメソッドのテストケースです。
テスト結果の確認をする際はassertXXXXのようなチェック用メソッドを呼ぶ必要はなく、
通常プログラムのように比較演算子で確認することができます。

def "合計金額計算_when_then版"() {
    setup:
        def charge = 1000
        def number = 5
        def amount = new Amount(charge, number)

    when:
        def result = amount.totalAmount()

    then:
        result == 5000
}
def "合計金額計算_expect版"() {
    setup:
    def charge = 1000
    def number = 5
    def amount = new Amount(charge, number)

    expect:
     // テスト対象実行とテスト結果確認を一緒に行う。
    amount.totalAmount() == 5000

}

テーブル記述(データテーブル)を使ったパラメタライズドテストの実装

・ユニットテストにおいて、一つのテスト対象メソッドに
 複数パターンのパラメータを指定して繰り返しテストを行うたいことはよくあることと思います。
・例えば年齢と性別の情報を受け取り、
 以下のように年代と性別の組み合わせでおすすめ商品を出力仕分ける処理を考えた場合、
 テストパターンとして年齢の境界値テストが必要のため、
 最低12パターンのテストケースが必要となります。

年代        性別     おすすめ商品
10・20代     男性     アイテムA
10・20代     女性     アイテムB
30・40代     男性     アイテムC
30・40代     女性     アイテムD
50・60代     男性     アイテムE
50・60代     女性     アイテムF

通常であれば12のテストケースを書く必要がありますが、
その場合テストコード行数が長くなり可読性が悪く、
また何度も同じようなテストケースを繰り返し書くと、
設定値や期待結果にミスが生まれやすくなると思います。

Spockではこのようなテストを実施する場合、
データテーブルと呼ばれるテーブル形式でテストデータを記述することで、
渡し繰り返しテストを実行できる機能があります。
このデータテーブルを使用する場合はwhereブロックが必要となります。

[実装の具体例]

@Unroll
def "おすすめ商品取得"() {
    setup:
    //テーブルの値(age, gender)をパラメータとしてセット
    def recommend = new RecommendItem(age, gender)

    when:
    def result = recommend.recommendItem()

    then:
    //テスト結果とテーブルの期待値(expected)を比較
    result == expected

    where:
    age | gender   || expected
    10  | 'male'   || 'item-A'
    29  | 'male'   || 'item-A'
    30  | 'male'   || 'item-C'
    49  | 'male'   || 'item-C'
    50  | 'male'   || 'item-E'
    69  | 'male'   || 'item-E'
    10  | 'female' || 'item-B'
    29  | 'female' || 'item-B'
    30  | 'female' || 'item-D'
    49  | 'female' || 'item-D'
    50  | 'female' || 'item-F'
    69  | 'female' || 'item-F'
}

テーブルの一行目(テーブルヘッダ)は各項目の変数名にあたり、
setupブロックやwhen/thenブロックで使用することができます。
テストを実行すると二行目以降のテストデータが順次この変数にセットされ、
テーブル行数分のテストを実行してくれます。
この実装例では、
agegenderの値がsetupブロックのRecommendItemクラスのパラメータとしてセットされ、
thenブロックでwhereブロックのexpected(期待値)の値とテストの結果を比較しています。

このデータテーブルを使用したテスト(パラメタライズドテスト)を行う際は
テストケースに@Unrollをつけるのを忘れないようにしてください。

@Unroll
def "おすすめ商品取得"() {

理由としては@Unrollをつけずにテストを実行すると
下記のように全パターンテストを行なった後にテスト合否結果は返してくれるものの、
何回目のテストで失敗したかは表示をしてくれません。
f:id:ys-yamano:20191220005146p:plain

@Unrollをつけてテストを実行すると、
下記のように各回別のテストの合否が確認できるようになります。

f:id:ys-yamano:20191220005354p:plain

Mockを使ったテストコード実装

DB接続などが伴うユニットテストでDBのデータに依存しないよう
Mockを使うケースがあると思いますが、
Spockでは標準でMockの機能が用意されています。

以下は商品IDを指定してDBから商品情報を取得する処理で、
DBから商品情報を取得するDaoクラスをMock化した実装例です。

[テスト対象クラス]

public class ItemService {
    ItemDao dao;

    public Item find(int itemId) {
        return dao.select(itemId);
    }
}
public interface ItemDao {
    Item select(int itemId);
}
public class Item {
    int id;
    String itemName;

    public Item(int id, String itemName) {
        this.id = id;
        this.itemName = itemName;
    }
    public int id() {
        return id;
    }
    public String itemName() {
        return itemName;
    }
}

[テストコード例]

    def "Mockを使ったテスト"() {
        setup:
        // ItemDaoをMock化
        ItemDao mockDao = Mock()
        def service = new ItemService()

        // Mockオブジェクトにテストデータをセット
        mockDao.select(1) >> new Item(1, "商品1")
        // ItemServicedaoプロパティをMockオブジェクトで書き換え
        ItemService.metaClass.setAttribute(service, "dao", mockDao)

        when:
        def result = service.find(1)

        then:
        result.id() == 1
        result.itemName() == "商品1"
    }

ポイントとしてはモック化したいクラスに
SpockのMockingApi.Mock()メソッドをセットしモックオブジェクトを生成する点と、

ItemDao mockDao = Mock()
//または以下でもOK
def mockDao = Mock(ItemDao)

{メソッド} >> {テストデータ}の記述で
指定したデータを返却するよう振る舞いを定義している点です。

以下の場合ではselectメソッドのパラメータ値が1の場合のみ、
商品ID:1 商品名:商品1のデータが返却されるよう定義しています。

mockDao.select(1) >> new Item(1, "商品1")

以上、簡単ですがSpockの実装方法について書かせていただきました。
内容がご参考になれば幸いです。

Google Optimizeで行うA/Bテストのすゝめ

こちらは、アソビュー!Advent Calendar 22日目の記事になります。

はじめまして!新卒2年目の山内と申します。

特徴的な髪型から、社内ではキノコとしばしば呼ばれています。
現在は、社内基幹システムの運用保守とバックエンド開発をしているのですが、1年目のときにアソビュー!のグロースを目的としたA/Bテストを行っていたので、その時のことを書いていこうと思います。

A/Bテストとは

A/Bテストとは、オリジナルページに変化を加え、オリジナルパターンとテストパターンを同条件で比べて効果をはかるテスト手法になります。

様々なツールがありますが、弊社では、VWO(visual website optimizer)やGoogle Optimizeを利用していました。 今回は、Google Optimizeでの手法を紹介していこうと思います。

テスト準備

さて、いざテスト実装と言いたいところですが、施策を考えなければ実装ができません。
施策を考えるときに重要になるのが、施策の目的・課題・仮説を明確にすることです。

▼ページを特定

まず、テストを行うページを特定します。

▼課題の抽出・仮説出し

次に仮説を考え、そのページにはどのような課題があるかを洗い出します。

私の場合、色々な場面で実際にアソビュー!を使って仮説を出していきました。
外にいるときはこのボタンは目立たないかもしれない。であったり、
お出かけ先で、明日の朝体験したいとなったらどのような情報があったら良いだろうか。 等々あらゆる場面や時間、状況を想定しました。
また、実際に知人にアソビューを使ってもらい、ページの課題のインタビューを行いました。

▼施策出し

最後に、課題を解決する施策を出します。

このように施策を考え出すまでの目的・課題・仮説を明確にすることでテスト後の考察が的確にできるようになります。

いざテスト実装

いざ、テストを実装していきます。
今回は、アソビューのチケット枚数選択画面の「購入する」というボタンに対して実際に行ったテスト例を紹介します。

f:id:mauchi0106:20191221105230p:plain
こちらの「購入する」というボタンの文言を変更していきます。
チケット枚数選択の文言が、「購入する」だといきなり購入がはしるのでは、という不安感があるため、文言を変更することで遷移率が向上するのではないか。という仮説のもと、他に適切な文言がないかを検証するため、下記の文言でテストを行いました。

  • 購入に進む
  • 上記で購入に進む
  • 次へ
  • 枚数を確定する

こちらの4つの文言をOptimizeに反映させ、テストを作成します。
OptimizeではGUIで簡単にボタンの大きさや色、文言を変更することができます。
ただ、この方法だと実際のページ上にDOMが追加されたり、複数ボタンがある際に思わぬ挙動を起こす可能性があるため、 直接Optimize上でCSSを上書きする方法がおすすめです。

Optimize上で実際にCSSを書いてみるとこのような感じで文言の変更ができます。

.button {
  font-size : 0px;
}

.button::after {
  font-size : 14px;
  content : "購入にすすむ";
}

※クラス名は仮です。

f:id:mauchi0106:20191220134705p:plain
「購入に進む」に変更できました。

このようにCSSを上書きすることで、文言の変更を行うことができます。

テスト開始

テストパターンの作成が完了したら、目標の設定や、テスト対象ページの設定、対象デバイス(PC/SP/タブレット)等の選択を行い開始ボタンを押すと実際に開始されます。
ページにもよりますが、2週間ほどテストを行うと十分なOptimizeが十分な結果が取れたと認識し、レポートが作成されます。

f:id:mauchi0106:20191221111900p:plain
今回の結果がこちらになります。 Optimizeによると、「購入にすすむ」が目標に対して、最適である可能性が高いことがわかりました。

効果検証

最も重要なのは効果検証です。

今回の結果から、「購入に進む」というすぐに購入するわけではないという文言を示したことにより、ゲストの不安が払拭され、次ページへの遷移率が向上したと考察する事ができます。

このように結果を元に、なんでうまくいったのだろう、なんでうまくいかなかったのだろうと考察すると思います。
その際に活きるのが、テスト前に明確にした施策を行った目的・課題・仮説です。
うまくいった際は、この仮説のようなゲストが多かった、なので同じ仮説を元に考えると次はこの施策をやってみよう、であったり、
うまくいかなかった際にも、この仮説のようなゲストは少なかった、なので次の施策はこうすれば良いのではないかと考えることができます。

施策の目的・課題・仮説を明確にすることで格段に素早くPDCAを回すことができます。

効果まとめ

1つのテストが終了し、結果を集約することも重要です。

私達は、A/Bテストの成功事例を青シート、失敗事例を赤シートというようにまとめました。

今回の結果をまとめたシートがこちらです。

f:id:mauchi0106:20191221115743p:plain

失敗した際も同じように考察し、シートにまとめます。

f:id:mauchi0106:20191221120443p:plain

変更点は何なのか、目標や仮説は何なのか、結果はどうなったのか、どう考察できるのか、をすべての施策に対してまとめました。
このようにシートにまとめることで、ひと目で過去の具体的なA/Bテストの振り返りや、社内でA/Bテストの知見を広めることができます。

また、A/Bテストのタスク管理チケットや実際の結果、青/赤シートがわかるようにスプレッドシートに集約し、管理していました。
このように一括して管理することで、テスト後のソースコード改修や、変化率、テストの消化状況をモニタリングすることができます。

f:id:mauchi0106:20191221122142p:plain

まとめ

実際に僕は紹介した手法で、2ヶ月間で15個ほどのA/Bテストを実行しました。

うまくいったもの、いかなかったものとそれぞれありましたが、どちらのパターンもシートにまとめ考察することで、アソビュー!を利用するゲストがより使いやすくなるにはどう改修すればいいのか、という知見を貯めることができたと思います。
これからA/Bテストを行おうと思っている方は参考にしていただけると幸いです!

最後に

アソビューでは、一緒にワクワクしながら課題解決の遊びしたい方大募集中です!

hrmos.co

スケールできなかったオフショア開発拠点をなんとかした話

この記事は アソビュー! Advent Calendar 2019 - Qiita 21日目の記事です。
アソビューにてバックエンドエンジニアの服部と申します。
今年の夏よりベトナム開発組織 ASOVIEW VIETNAMのCTOに任命され、組織のスケールアップをメインミッションとして日々奮闘してます。
最近はベトナムにくると「帰ってきた...」というホーム感がでるようになってきました、不思議です。

f:id:takayasu-hattori:20191221102109j:plain:h250
無秩序なバイクのイメージが強いホーチミンですが、テト明けは人も少なく装飾が綺麗です。

はじめに

オフショア開発を初めてみたはいいが、どこかうまくいってない。
開発は進んでるしリリースもできてるんだけど、人の入れ替わりは多いし
どこか日本と現地で噛み合っていないような。。。

アソビューでは2018年にベトナムにオフショア開発拠点を自社で立ち上げおります。
ベトナム開発拠点の立ち上げから1年。
新規開発系のプロジェクトはうまく回っていたが、エンハンスメント案件系の対応チームがこの問題に直面しており、スケールしづらくなってました。
それを少しだけ改善した話。

ここで説明しないもの

スクラムガイドに記載されている事柄について

なんかうまくいってない

ベトナム開発拠点が立ち上がってちょうど1年たった頃くらいから、
日本のメンバーから下記のような声がよくあがるようになってきました。
もちろん自分も同じように言っていた1人です。

  • 依頼通りのものが出てこない
  • 各タスクの進捗がわからない
  • コード品質が悪い
  • プロダクトバックログに積まれたタスクが山ほどあるのにタスクがないと言われる
  • PO/決裁権のある人が知らないないものが開発されている
  • 数ヶ月で人がすぐ辞めていく
    etc...

(これってベトナム組織の問題なの...?というものが多数混ざってますが一旦。)
離職率も上がっているように見え、ベトナム開発組織でなにが起きているのかみんないまいちちゃんと把握できてない状態でした。
ということで、ベトナムに参りました。

何が起きていたか

ベトナムに来て最初やっていたことは、とにかく毎日観察/調査です。

  • 日本側と「どのような」やりとりを「どこで」やってるのか
  • どんなイベント(朝会/定例等)をやっているのか
  • だれがリーダー的なポジションで、だれがコミュニケーションの中心にいるのか
    etc...

訪越して2-3週目くらいたってようやく、どうやらベトナム組織単体の課題ではなく、
日本組織にも大いに課題がありそうということが見えてきました。

下記一例ですが、

  • 複雑なレポートライン
    • 事業をまたがり自由に依頼
    • ブリッジエンジニアを通さない依頼
  • 不確実性の高いタスク
    • 要件定義がきちんとされていないタスク
    • POを通さずに要件定義されたタスク
    • いろんな人が自由に作ったtodoレベルのタスク
    • 何に貢献しているのか説明されていないタスク
  • 柔軟すぎる開発体制
    • 開発計画のない作業体制
    • 作業の振り返りをしていない
    • チーム間のメンバー移動がフレキシブルに行われている

f:id:takayasu-hattori:20191220234144p:plain
レポートラインが多すぎて誰も管理できてない状況に
なかなか渋い状況ですね。。。日本で管理できていると思っていましたが、見えないところでいろんな作業が発生していました。
もちろんベトナム組織からも下記の不満が出てきていました。

  • 案件依頼にルールがない
  • タスクをやってもリリース直前で止められる
  • タスク完了直前で仕様変更がある
  • 事業貢献度がわからない
  • 開発の進め方にルールがない

これではベトナム組織も疲弊するのは当然です。

これ、ほとんどスクラム開発で解決できそう

課題としては、日本組織は開発依頼体制が、ベトナム組織は開発受け入れ体制が整備されていないこと。
スクラムガイドラインに則った開発ができるように整備すれば、両方とも解決できそう。

日本組織では2年ほど前よりスクラム開発を導入し、知見も溜まってきております。
採用/教育含めスクラムマスターをベトナムで急遽起用するのは現状難しそうだと判断し、
CTO兼スクラムマスターとしてスクラム開発を数チームに導入し、改善を図ってみることにしました。

スクラム導入の下準備

とはいえ、「スクラム開発やろうぜ!」といって始められるわけではないので下準備(スプリント0の実施)をしていきます。
巻き込まないといけな人が多いので、実際にスプリントを回し始めるより大変です。

ベトナム開発チーム内での準備

ベトナム開発組織のメンバーは全員スクラム開発をやったことがないとのこと。
スクラムガイド にベトナム語訳があったので、1週間以内に読んでくることを依頼。
また、下記を並行で実施しました。

  • 日報の提出依頼
  • 毎朝、同じ時間に朝会の実施(のちにデイリースクラムに移行)
  • スクラムガイドの読み合わせ
  • スクラムイベントの簡単なシミュレーション
  • タスク管理ツールのルール化

POとの調整

チームの整理および当時のプロダクトバックログや今後のタスクに関してはPOと整理しました。

  • チームの整理
    • ベトナム開発チームの数/メンバー構成は変えない
    • 事業ごとにベトナム開発チームをつけ管理することを合意
  • プロダクトバックログの整理
    • 不要なもの(個人のtodoや決済権を通ってないタスク)は削除
    • 要件定義できていないものはICEBOXへ
    • 必要なタスクの作成
  • タスクの依頼ルートの整理
    • 基本的にPOがタスクを作成する
    • POがOKを出した場合のみ、そのタスクに関してのみ代理でスクラムマスターがタスクを作成することができる(これはスクラムガイドに反してますが今回OKとしてます)
    • メンバー/サポートからの依頼の吸い上げはPOが参加するミーティングでやるやらを判断する

f:id:takayasu-hattori:20191220234026p:plain
メッシュになってたのが嘘のよう。レポートラインが明確化されました

そしてスクラムへ…

初めてのスクラムイベント

スクラムイベントの初回実施時は、どのチームでも質問責めにあいます。
1つ1つの行動や発言に対して

  • なんでやるのか
  • 今のはどう言う意味か
  • こうした方が効率がいいと思う

いろんな意見が出てきますが、ちゃんと意味を説明し、また初回なので教科書通りにやっていくことを伝えます。
とはいえみんなポジティブで、「スクラム開発やってみたい!だから教えて欲しい!」という姿勢なので、
やりにくさや進めづらさはなかったです。

導入してどうなったか...

課題としてあげた部分に関してはほぼ解消。
また以前は個人プレーが割と多いと感じてましたが、イベントをこなすにつれてチームでゴールを達成するにはどうすればいいか。という良い空気も出てきました。

ここ、さらっと書いてしまっていますがスクラム導入までの整理や調整が本当に大変で、導入後は日々みんなで改善を繰り返していくだけです。

f:id:takayasu-hattori:20191220234732j:plain:h250
レトロスペクティブ後のようす。最初は恥ずかしがってましたが最近は楽しそう!

とはいえ、課題はまだまだ

以前よりよくなった!とはいえ開発チームが自走し、かつ組織がスケールしていくには課題がまだまだあります。
日本の遊びをもっと世界に広めたい!、オフショア開発や海外経験してみたい。もしそういうことに興味がある方はぜひ一緒に働いてみませんか?

というわけで、アソビューでは一緒に働ける、世界をワクワクさせたい仲間を募集してます。
hrmos.co