一休.com Developers Blog

一休のエンジニア、デザイナー、ディレクターが情報を発信していきます

一休における開発組織の変遷(目的型組織への移行)

メリークリスマスイブ! 皆様クリスマスイブはいかがお過ごしですか。

データサイエンス部所属のエンジニア 笹島 id:sisijumi です。

年末ということもあり、今日は一休という会社のエンジニア組織の変遷を振り返るとともに、現状に関してもお話させていただきます。 (現状はデータサイエンス部ですが、今年の10月までは宿泊事業部のエンジニアのマネージャーをやっておりました。)

一休における収益の変遷

宿泊事業に関してですが、過去発表した資料を元に説明させていただきます。 (近年非上場になった為、直近の収益に関しては資料はありません。)

f:id:sisijumi:20171224173112p:plain

創業期、成長期を経て、一時停滞したものの、きちんと再度成長していることがわかりますね。

事業のステージに合わせて変化してきました

創業期、成長期を支えてきた組織構造

各部署内に宿泊・レストランチームが存在しています

f:id:sisijumi:20171224172217p:plain

各部署が都度エンジニアのマネージャーとコミュニケーションと会話して開発を進める、というスタイルになっています。

f:id:sisijumi:20171224172210p:plain

この頃、中心となるマネージャーがボトルネックになってしまうと開発の全体のスピードが遅れるという課題がありました。 また、宿泊予約やレストラン予約等それぞれのサービスがより成長し、上記の開発スタイルがそのスピード感にマッチしなくなってきました。

再成長を支えた事業部の設立

それぞれのサービスをより成長させることへコミットできるように、一休という会社としても事業部制への移行を実施しました。 それとともに事業部毎にエンジニアを配置する形に変更になっています。

f:id:sisijumi:20171224172429p:plain

事業責任者とエンジニアのマネージャーでプロダクト開発優先順位を意思決定し、開発メンバーを調整するという流れでした。 以前の形と異なる点は、エンジニアのリソースをどう活用すれば事業の成長に最も寄与できるかをきちんと意思決定しながら開発を進めている点です。 (以前の形では依頼ベースで開発を進めてしまうところがあり、事業の成長に最も寄与するためのエンジニアのリソースの配置をどうするか、という視点が欠けていたと思っています。)

f:id:sisijumi:20171224172347p:plain

目的型組織への移行

ミッション毎にプロダクト開発チームの枠組みを作りました

事業を成長させるためのミッションを作成し、ミッション毎にチームを配置しました。この形にすることで、それぞれのチームごとにミッションに集中できる構造になっています。
以前の形ですとビジネスオーナーとエンジニアの間にエンジニアのマネージャーが間に挟まってしまい、不要なコミュニケーションが発生してしまっていました。ビジネスオーナーとエンジニアの距離をより近づけることで、開発時のコラボレーションがより活発になることを目指しました。

f:id:sisijumi:20171224172750p:plain

事業部長が作成したミッションごとのチームそれぞれで、プロダクト開発が進む体制になっています。 この構造においては、マネージャーはエンジニアを後方支援する形になっています。また事業部長とのコミュニケーションも何をやるかではなく、事業の成長に各ミッションへのエンジニアのリソースは最適化できているか、という視点が主になっています。

f:id:sisijumi:20171224172757p:plain

最後に

エンジニアの枠組みとしてこうあるべき、というビジョンも必要ですが、それに囚われずチーム構造を柔軟に変化させる必要があります。 自分がマネージャーだと、現状のチーム構成から変化させることに心理的負荷はやはりあります。(チームでやってもらっているメンバーにきちんと説明し、合意をした上できちんと進めないといけないということが頭をよぎり、反射的に反対することもある)
しかし、今の立場に縛られず現状の事業をどうやって伸ばすか、成果を最大化させるためにエンジニアチームでやるべきことはなんなのか、など事業にきちんと貢献できているかを客観的に継続的に判断する必要があります。事業的な成長をプロダクト開発において牽引していきたいと思います。

明日は id:ninjinkuniOSとAndroidの段階的リリース機能を比較する です

一休.comスマホ版予約入力画面リニューアルの舞台裏

本記事は、一休.com Advent Calendarの23日目です。

宿泊事業本部フロントエンドエンジニアの宇都宮です。

先日(12/19)、一休の宿泊予約サービス(以下、一休.com)のスマホ版の予約入力画面リニューアルをリニューアルしました。本記事では、

  • どのような方針で
  • どのような技術を使って
  • どのような設計で
  • どのように実装していったか

を紹介します。

Before/After

リニューアル前

ファーストビュー

f:id:ryo-utsunomiya:20171220082307p:plain:w320

エラー通知

次の画面へ進む前に、alert()

f:id:ryo-utsunomiya:20171220082328p:plain:w320

リニューアル後

ファーストビュー

f:id:ryo-utsunomiya:20171220083829j:plain:w320

エラー通知

項目の上に表示&自動フォーカス

f:id:ryo-utsunomiya:20171220083838j:plain:w320

主な変更点

  • デザインの刷新
  • エラー通知の改善
  • クレジットカード入力画面、オプション注文画面の統合

実装面では、ASP.Net Web Formsのユーザーコントロールを大幅に削り、Vue.jsのコンポーネントに置き換えました。

以前の画面の課題

リニューアル前の予約入力画面には、いくつかの課題がありました。

  • UIが使いにくい 特にエラー通知が不親切
  • 画面の表示が遅い(サーバサイドで大量のデータを読み込んでいるため)
  • ユーザーコントロールの変更が難しく、生産性が低い

エンジニアの観点からすると、特に生産性の低さが気になっていました。ユーザーコントロールとはASP.Net Web Formsフレームワークの機能で、再利用可能なコンポーネントを定義する機能です。

ユーザーコントロールは、上手に活用すれば生産性を向上させる機能だと思いますが、残念ながら一休の予約入力画面では、生産性を低下させる原因となっていました。というのも、数千行規模のユーザーコントロールが複数存在し、それぞれが密結合していたのです。

4月に、フォームの入力欄の順番を入れ替える作業を行いました。表面上は簡単な調整で、普通のHTMLなら30分でできそうな作業でしたが、実装1日+テスト0.5日という工数を費やす必要がありました。

あるユーザーコントロールから別のユーザーコントロールに入力欄を移動するには、その入力欄のために定義されているフィールド/プロパティやメソッドを、まるごと別のユーザーコントロールに移す必要があったためです。

↓は当時のプルリクのスクリーンショットですが、3項目の移動で+3,571 −166という大きなdiffが生じています。

f:id:ryo-utsunomiya:20171220090931p:plain:w320

また、予約入力画面のaspxファイルは単体で5,000行(※)ほどあり、ファイル内で行きつ戻りつするのも非常に大変でした。

※:JSが3,000行、残りはHTMLとASP.Netのコードナゲット

このような理由から、予約入力画面のリニューアルを検討しました。

リニューアルの方針と実装

スコープ

予約の画面は膨大な業務ロジックに支えられているため、実装工数が膨らむことは避けられません。そこで、以下のようにスコープを限定して進めました。

  • 対象はスマホ版の予約入力画面のみ
    • PC版や、スマホ版でも予約確認画面や完了画面には手をつけない
  • サーバサイドの業務ロジックにはできるだけ手をつけない。レスポンス速度の改善はスコープ外

技術選定

予約入力画面のリニューアルでは、EFO(Entry Form Optimization)の観点から、JavaScriptでリッチなエラー通知を行うことを目指しました。あわせて、開発者の生産性を向上させる必要性も感じていました。そこで、JavaScriptフレームワークのVue.jsを導入しました。

既存の画面で使用しているjQueryではなく、Vue.jsをメインに据えた理由は、生産性が大きな要因です。Vue.jsの単一ファイルコンポーネントを使用すると、画面上の要素(コンポーネント)ごとに、関連するロジック(js)とスタイル(css)をひとまとめにすることができます。コンポーネントを小さく保ちながら実装を行えば、コードのメンテナンス性を大きく向上させることができると考えました。

実装

Vue.js + Vuex + VeeValidateというスタックで実装を行いました。

Vuexを導入するかは迷いましたが、予約入力画面で使用する膨大なデータを一元管理できるのは大きなメリットでした。VeeValidateはpluggableな構成になっていて、独自のバリデーションを簡単に定義できるようになっているのが良かったです。

テストには vue-test-utils を使用しています。Vueコンポーネントのテストは一部のメソッドと算出プロパティだけで、レンダリングのテストは行っていません。

現在のところ、コンポーネントの構成は以下のようになっています。

f:id:ryo-utsunomiya:20171220094116p:plain:w320

大枠から作っていって、徐々にコンポーネントを分割していくという方針を採ったため、コンポーネントの粒度は大きめです。また、本来はコンポーネントにすべきなのに、コンポーネント化できていない部分もあったりします(たとえば、お知らせメール受信設定)。

目安として、.vueファイルが100行を超えたらコンポーネントを分割できないか検討するようにしていました。最も大きなコンポーネントで.vue 200行 + .js 300行ほど、平均的なコンポーネントのサイズは100行程度に抑えることができたため、コードの見通しはかなり良くなりました。

また、以前は5,000行ほどあった予約入力画面のaspxファイルは、リニューアルによって200行にまでスリム化しました。

残る課題

本ページのリニューアルに関しては一区切りというステータスですが、以下のような課題が残っていると認識しています。

  • 一休.com全体でのデザイントーンがバラバラ
  • 画面の表示は相変わらず遅い
  • コンポーネントの粒度はもう少し細かい方がよさそう
  • JavaScriptの単体テストが不足している
  • CSS Modulesがまだ使いこなせてない

デザイン面の課題はデザイナーと協力して、プログラムの課題は他のエンジニアとも認識を合わせながら、引き続き改善していきたいと考えています。

明日はzimathonさんの「開発組織の目的型組織への移行」です。

KMLを元にしたSolrの空間検索に挑戦

この記事は一休.comアドベントカレンダー2017の22日目です。

いよいよ今年も終わりですね。 みなさん クリスマスの、忘年会のご予約はすみましたか?

というわけでアドベントカレンダー2打席目、一休.comレストラン 検索 & 集客担当のにがうりです。

一休の本社は赤坂見附の駅からほど近くにあり、お昼ごはんの選択肢が非常にバラエティに富んでいるのが嬉しいところです。 もちろん、その中には一休.comレストランにご加入いただいている店舗様もたくさんあります。

本エントリでは

  • 筆者のお昼休み中に通える範囲内にあり
  • 一休.comレストランでランチが予約できる

レストランがどのくらいあるのか、Solrの空間検索( Spatial Search )を利用して調べてみました。

なお、前回のエントリ同様、Solrのバージョンは7.1.0を前提としています。

事前準備

Solrのスキーマ構成

ひとまず、以下の項目を用意します (restaurant_idがuniqueKey)

  <field name="restaurant_id" type="string" indexed="true" required="true" stored="true"/> <!-- レストランのID -->
  <field name="restaurant_name" type="string" indexed="true" stored="true"/>  <!-- レストランの名称 --> 
  <field name="lat_lon"           type="location"   indexed="true" stored="true" /> <!-- 緯度、経度 -->
  <dynamicField name="*_coordinate" type="pdouble" indexed="true" stored="false"/>

対象となるデータ

「赤坂・永田町・虎ノ門」エリアで12時に予約できるお店が120件ほど見つかりました。 これらの店舗のID, 名前, 緯度経度をSolrに登録しておきます。

Solr検索のテスト

試しに3件取得

URL

http://localhost:8888/solr/ikyu-advent-2017-spatial/select?wt=json&echoParams=none&fl=restaurant_id,restaurant_name,lat_lon&q=*:*&rows=3

取得結果

無事に登録されていました

{
    responseHeader: {
        status: 0,
        QTime: 5
    },
    response: {
        numFound: 120,
        start: 0,
        docs: [{
            restaurant_id: "100152",
            restaurant_name: "春秋 溜池山王店",
            lat_lon: "35.6736091,139.740132"
        }, {
            restaurant_id: "100195",
            restaurant_name: "沖縄懐石 赤坂潭亭",
            lat_lon: "35.6685154,139.732890"
        }, {
            restaurant_id: "100197",
            restaurant_name: "赤坂浅田",
            lat_lon: "35.6738200,139.738229"
        }]
    }
}

オフィスの位置を中心に範囲検索

Spatial Searchのgeofilt を使い、

  • オフィスの場所を中心に
  • 半径400メートル以内の店舗を
  • 近い順に

探してみます

URL

http://localhost:8888/solr/ikyu-advent-2017-spatial/select?wt=json&echoParams=none&fl=restaurant_id,restaurant_name,lat_lon,geodist:geodist()&spatial=true&sfield=lat_lon_rpt&q={!geofilt}&pt=35.675471,139.737937&d=0.4&sort=geodist()%20asc&rows=3

pt=35.675471,139.737937 が一休本社オフィスの緯度経度、d=0.4 が400メートル以内という指定です

取得結果

{
  "responseHeader": {
    "status": 0,
    "QTime": 0
  },
  "response": {
    "numFound": 36,
    "start": 0,
    "docs": [{
      "restaurant_id": "100729",
      "restaurant_name": "個室会席 北大路 赤坂茶寮",
      "lat_lon": "35.6752587,139.738609",
      "geodist": 0.06510001  /* geodistは中心地からの距離 (km) */
    }, {
      "restaurant_id": "106301",
      "restaurant_name": "土佐料理 祢保希 赤坂店",
      "lat_lon": "35.6751768,139.737050",
      "geodist": 0.086561576
    }, {
      "restaurant_id": "107172",
      "restaurant_name": "赤坂 金舌",
      "lat_lon": "35.6746016,139.737555",
      "geodist": 0.10269355
    }]
  }
}

無事取得できました。 一見、後は半径を調整すればいけそうに思われます・・・が、一休本社の周辺マップはこのようになっています。

一休周辺地図 ※ 赤いピンが株式会社一休本社

ご覧の通り、

  1. 東側には大通り
  2. 大通りを抜けても日枝神社や大使館
  3. 北側もガーデンテラスの方向に抜けるためには何回か信号を渡る必要がある

となっているため、単純な半径の調整で良い具合に、というのは中々厳しいものがあります。 ここは、円形ではなく任意の範囲を指定して検索したいところです

任意の範囲を指定して検索

任意の範囲を検索するためには、

  1. どうやって任意の範囲を指定するのか
  2. 任意の範囲を使った検索をどのように行うか

という2つの課題をクリアする必要があります。 幸い、1はGoogleマイマップ、 2はJTSを利用したSpatial Search を使うことで対応できました。

Googleマイマップで範囲データを作成

Googleマイマップでは地図上に自由にラインを引くことでき、さらにそれをKML形式のデータとして出力することが可能です。 ※ Googleマイマップの使い方については本稿の主旨と異なるため、割愛します

ランチタイムの徒歩行動圏をこのような枠線で表現しました。 お昼の行動半径

こちらをKML形式でエクスポートした結果が以下です。

ikyu-advent-2017-spatial.kml

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>ikyu-advent-2017</name>
    <Style id="poly-000000-1200-77-nodesc-normal">
      <LineStyle><color>ff000000</color><width>1.2</width></LineStyle>
      <PolyStyle><color>4d000000</color><fill>1</fill><outline>1</outline></PolyStyle>
      <BalloonStyle><text><![CDATA[<h3>$[name]</h3>]]></text></BalloonStyle>
    </Style>
    <Style id="poly-000000-1200-77-nodesc-highlight">
      <LineStyle><color>ff000000</color><width>1.8</width></LineStyle>
      <PolyStyle><color>4d000000</color><fill>1</fill><outline>1</outline></PolyStyle>
      <BalloonStyle><text><![CDATA[<h3>$[name]</h3>]]></text></BalloonStyle>
    </Style>
    <StyleMap id="poly-000000-1200-77-nodesc">
      <Pair><key>normal</key><styleUrl>#poly-000000-1200-77-nodesc-normal</styleUrl></Pair>
      <Pair><key>highlight</key><styleUrl>#poly-000000-1200-77-nodesc-highlight</styleUrl></Pair>
    </StyleMap>
    <Placemark>
      <name>ランチエリア</name>
      <styleUrl>#poly-000000-1200-77-nodesc</styleUrl>
      <Polygon>
        <outerBoundaryIs>
          <LinearRing>
            <tessellate>1</tessellate>
            <coordinates>
              139.7344959,35.6802109,0
              139.7360516,35.6778317,0
              139.7344422,35.6766465,0
              139.7351772,35.6739926,0
              139.7361803,35.6719924,0
              139.7388196,35.67116,0
              139.7404772,35.672341,0
              139.7392058,35.673352,0
              139.7372103,35.680516,0
              139.7344959,35.6802109,0
            </coordinates>
          </LinearRing>
        </outerBoundaryIs>
      </Polygon>
    </Placemark>
  </Document>
</kml>

「coordinates」の中に経度、緯度の羅列が入っています。こちらが枠線の情報のようです。

ちなみに、この地図に対して前述の120件のレストランをプロットするとこのようになりました

f:id:ikyu_com:20171222135449p:plain

範囲広すぎましたね・・・ 気を取り直して続けます。

検索の下準備

インストール直後のSolrでは空間検索ができない状態でした。 空間検索を行うためには、JTSを入手する必要があります。

JTSの入手 & 設定

  1. https://repo1.maven.org/maven2/com/vividsolutions/jts-core/ より jts-core-{バージョン}.jar をダウンロード (本稿作成時点では1.14.0)
  2. SOLRインストールディレクトリ/server/solr-webapp/webapp/WEB-INF/lib/ にコピー ※ SOLRインストールディレクトリ/server/lib と間違えないように

SolrにJTSを反映

1. 定義済みの型「location_rpt」に対しspatialContextFactory="JTS" を追記 (これをやらないとエラーになります)
<!-- 変更前 -->
<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" geo="true" maxDistErr="0.001" distErrPct="0.025" distanceUnits="kilometers" />
<!-- 変更後 -->
<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" geo="true" maxDistErr="0.001" distErrPct="0.025" distanceUnits="kilometers" spatialContextFactory="JTS"/>
2. location_rpt型の列を追加

lat_lon_rptという列を追加しました。 データを作り直すのは面倒なので、copyFieldでlat_lonの値をコピーさせています

  <copyField source="lat_lon" dest="lat_lon_rpt"/>
  <field name="lat_lon_rpt"  type="location_rpt"   indexed="true" stored="true" />
3. Solr再起動

この後データを再度登録し、lat_lon_rptにデータが入っていることを確認して下準備は完了 

任意の範囲を検索

説明 を読むと、

&q=:&fq={!field f=geo}Intersects(POLYGON((-10 30, -40 40, -10 -20, 40 20, 0 0, -10 30)))

という指定で範囲の指定ができるようです。 つまり、Intersects(POLYGON((...))) の中に、先のkmlのcoordinatesの内容を使えばいけそうです。 手で加工するのも面倒なので、Pythonの力を借りて検索しちゃいましょう。

ikyu-advent-2017-spatial.py (Python3.6.xで実行)

import re
import urllib.request
import urllib.error
import xml.etree.ElementTree as et

def main():
    root = et.parse('ikyu-advent-2017-spatial.kml').getroot()  # 先程のkmlファイルを読み込み
    polygon = root.findtext(".//{http://www.opengis.net/kml/2.2}coordinates")  # coordinates内の文字列を取得

    polygon = re.sub(r'\s+', '\n', polygon)  # スペースをトリミング
    polygon = re.sub(r',0$', '', polygon, flags=re.MULTILINE)  # 行末の.0を削除
    lon_lats = [_.split(",") for _ in polygon.split('\n')]
    lon_lat_str = ",".join([f'{_[0]} {_[1]}'  for _ in lon_lats if len(_) == 2])  # lon1 lat1,lon2 lat2,lon3 lat3... の組み合わせの文字列を生成

    query = (
        ('wt', 'json'),
        ('echoParams', 'none'),
        ('rows', '120'),
        ('fl', 'restaurant_id, restaurant_name,lat_lon'),
        ('q', '*:*'),
        ('fq', f'{{!field f=lat_lon_rpt}}Intersects(POLYGON(({lon_lat_str})))'),
    )

    url = "http://localhost:8888/solr/ikyu-advent-2017-spatial/select?" + urllib.parse.urlencode(query)
    print(url)
    print("-------------------")

    with urllib.request.urlopen(url) as req:
        try:
            response = req.read().decode('utf-8')
            print(response)
        except urllib.error.HTTPError as e:
            print("HTTPError")
            print(e.reason)
        except Exception as e:
            print(e)

if __name__ == '__main__':
    main()

出力されたURL

http://localhost:8888/solr/ikyu-advent-2017-spatial/select?wt=json&echoParams=none&rows=120&fl=restaurant_id, restaurant_name,lat_lon&q=*:*&fq={!field f=lat_lon_rpt}Intersects(POLYGON((139.7344959 35.6802109,139.7360516 35.6778317,139.7344422 35.6766465,139.7351772 35.6739926,139.7361803 35.6719924,139.7388196 35.67116,139.7404772 35.672341,139.7392058 35.673352,139.7372103 35.680516,139.7344959 35.6802109)))

取得結果

45件取得されました

{
  "responseHeader": {
    "status": 0,
    "QTime": 1
  },
  "response": {
    "numFound": 45,
    "start": 0,
    "docs": [{
      "restaurant_id": "100197",
      "restaurant_name": "赤坂浅田",
      "lat_lon": "35.6738200,139.738229"
    }, {
      "restaurant_id": "100729",
      "restaurant_name": "個室会席 北大路 赤坂茶寮",
      "lat_lon": "35.6752587,139.738609"
    }, 
    /* ---- 略 ---- */
    {
      "restaurant_id": "107172",
      "restaurant_name": "赤坂 金舌",
      "lat_lon": "35.6746016,139.737555"
    }, {
      "restaurant_id": "107347",
      "restaurant_name": "ビストロMATSU",
      "lat_lon": "35.6760843,139.735606"
    }, 
    /* ---- 略 ---- */
    {
      "restaurant_id": "108549",
      "restaurant_name": "鉄板焼877",
      "lat_lon": "35.676792,139.73558"
    }, {
      "restaurant_id": "108862",
      "restaurant_name": "焼肉しゃぶしゃぶシャンボール",
      "lat_lon": "35.6745112,139.736611"
    }]
  }
}

この45件のみで地図にプロットしなおすとこの通り。

f:id:ikyu_com:20171222135928p:plain

見事、徒歩圏内のレストランのみに絞り込むことに成功しました。 45件、コンプリートの道は遠そうです。

まとめ

  • 範囲指定の情報の作成はGoogleのマイマップを使うと楽
  • 任意の範囲で検索するためにはJTSが必要
  • KMLデータをSolrの検索条件に変換する処理はPythonなりで自動化可能

この結果を活かして、検索をもっと使いやすく、便利にしていきたいと思います。

明日は id:ryo-utsunomiya さんの「一休.comスマホ版予約入力画面リニューアルの舞台裏」です。

Dev旅のススメ。オフサイト・開発合宿におすすめな宿3選

この記事は一休.comアドベントカレンダー2017の 21日目です。

qiita.com

一休コンシェルジュ(https://www.ikyu.com/concierge/)のディレクターをしているid:aitamxです。2017年1月に一休にJoinし、もうすぐ1年となります。
アドベントカレンダーの一枠をいただいたので、『月1でのリリースサイクルの回し方』的な投稿も考えましたが、 それは別の機会とし、メディアっぽい内容のエントリーにしました。
箸休め的な形で愉しんでいただければ幸いです。

今回は一休.com掲載施設で、オフサイトや開発合宿、ハッカソンといったエンジニアが使うシーンに合いそうな宿をご紹介します。

\オフサイト・開発合宿で必要な設備/

◆ 何はなくともWi-Fi環境
◆ プロジェクタ・モニター・ホワイトボード
◆ 非日常な滞在

オフサイト・開発合宿におすすめな宿3選

オフサイトミーティングや開発合宿をするにあたり、自分たちで機材を持ち込む方もいらっしゃると思いますが、とはいえホワイトボードを持ってくのは何かとツラいですよね。
それに何かとかさばる荷物は少しでも減らしたいところ。
また、「非日常な滞在」=絶景やオシャレなインテリア、美味しい空気の中で得られるフレッシュな感覚は、クリエイティビティを必要とされる開発者の皆さんにこそ体験していただきたいので、必要項目に入れました。

しかし、条件をクリアした施設は、探してみると意外にありません。

\ マ ジ か マ ジ で か /

焦りながら結構な時間を費やし、営業の方にもアドバイスをいただき、バケーションレンタル(貸別荘)・リゾートホテル・旅館の3施設を厳選しました。

団体旅行にも。千葉の「バケーションレンタル(高級貸別荘・コンドミニアム)」

Cairns House (ケアンズハウス) オフサイト・開発合宿におすすめな宿3選

【おすすめポイント】
・とっても豪邸
・「ホワイトボード」「スクリーン」の貸出付きの宿泊プランがある
・一棟貸し切りなので、会議室代がかからずに作業に没頭できる

こちらは千葉の館山に位置する施設で、一棟貸し切りなので自分たちが自由にアレンジできます。
とてもゴージャス&オシャレな空間で、柔軟なアイデアが生まれそう。
別館を含めて利用すると、最大15名利用ができます。

人数少なめなスタートアップ企業の合宿にいかがでしょうか。
▼Cairns Houseについての詳しい紹介記事
こんなお家に住みたい!が叶う、憧れの豪邸 | 一休コンシェルジュ

箱根のリゾートホテルでオン&オフの滞在

ハイアット リージェンシー 箱根 リゾート&スパ オフサイト・開発合宿におすすめな宿3選

【おすすめポイント】
・広くて居心地のよい客室で、プライバシーが保たれる
・「ホワイトボード」「スクリーン」のある会議室では、窓越しに明星ヶ岳の大文字を見渡せる
・付属のレストランが美味しい

オフサイト・開発合宿におすすめな宿3選 強羅の自然を感じられるスパリゾート。上質な空間で、さすがハイアットといった安心感があります。
56平米という広々とした開放的な客室は、ゆったりくつろげる雰囲気。雑魚寝苦手な方が多いときには、ホテルは気楽です。
ホテル内のレストラン&バーが複数あるので、チームメンバーの好みに合わせたランチやディナーが楽しめるのもいいですね。
エグゼクティブな雰囲気に包まれ、濃縮したパワーミーティングをしたい方に。

友ヶ島、淡路島、四国を望む絶景宿でパワーチャージ

休暇村 紀州加太 オフサイト・開発合宿におすすめな宿3選 【おすすめポイント】
・紀淡海峡を望む絶景で湯ったりできる
・最大100名利用可能な和室の会議室
(ワイヤレスマイク・ホワイトボード・プロジェクター・スクリーン完備)
・紀州の旬の食材を使った豪快な海の幸を満喫

オフサイト・開発合宿におすすめな宿3選 高級宿泊施設のイメージがある一休.comですが、実は休暇村の予約もできることはご存知でしょうか。
こちらの「休暇村 紀州加太」は開放感のある眺望と、四季を感じる海の幸を堪能できる施設です。
露天風呂は最近はやりの「インフィニティ風呂」!
雄大な自然の中で煮詰まった頭をリフレッシュしたら、インスピレーションがわきそうですね。

まとめ

「オフサイト・ミーティング」といっても、各企業で解釈がさまざま。
「組織力向上」や「目標・ミッション理解」、「事業部毎の課題解決」を目的とし、チームビルディングに活用されている企業も多いと聞きます。(一休でも実施しています)
業務中の会話はSlackやhipchatだけということもあるかもですが、たまに場所を変えてミーティングをすると思わぬ発見があるかもしれません。

また、今回改めて施設を探しましたが、国内にはまだまだ開発合宿向きの施設が多くないと感じました。(特に、プロジェクター+ホワイトボード+Wi-Fiの貸し出しに対応している施設が少ない)
貸別荘等を利用するケースも多いかと思いますが、自分たちで何でもしないといけないのって結構大変だったり…。
新サービスのアイデアブレストや、プロトタイプ作成、興味のある技術やトレンドをみんなで味見…など、開発合宿といっても目的はひとそれぞれ。
ニーズに合わせた宿選びができるよう、おすすめ施設を見つけたら、定期的にご紹介していこうと思います。

明日はnigauryyyさんの「KMLを元にしたSolrの空間検索に挑戦」です。

データ分析基盤、その後

この記事は一休.comアドベントカレンダー2017の20日目です。

データサイエンス部所属のエンジニア 笹島 id:sisijumi です。

今日はクラウド環境へのデータ分析基盤構築にまつわるお話をさせていただこうと思っています。

データ分析基盤の構築に関して

夏にデータ分析基盤を Azure SQL Data Warehouse を中心にした構成で構築

構築はしましたが、残念ながらこの構成での運用には至りませんでした。

一休では元々社内にデータ分析基盤を構築し運用していましたが、運用負荷の増大に伴いその基盤のクラウド環境への移行を進めました。
下記は今年の8月のあるイベントでの発表資料ですが、イベントではデータ分析基盤は Azure SQL Data Warehouse を中心としたものに と話しさせていただきましたが、現状そうはなっていません。

二度の作り直し

実は上記は二度目のチャレンジでした。
一度目は Redshift(AWS), 二度目が Azure SQL Data Warehouse(Azure) へとそれぞれの環境に構築しました。
その環境を利用して実際に分析業務を行っているメンバーに検証してもらい、その結果を受けてさまざまなディスカッションをした結果、作り直し、という意思決定を行っています。

なぜ作り直しという意思決定に至ったのか

いろいろな理由はありますが、最大の要因は実際に分析業務を行うメンバーとのコミュニケーション不足だと考えています。
他社の事例などを参考に構築に関してはエンジニア側で主導しました。自分たちとしてはこうあるべきという絵図を描き、その形をきちんと実現しました。
もちろん構築の途中で分析業務を行っているメンバーにも、こういう構成で作りますという説明は行っていますが、その時点の相互理解が不足していたと考えています。結果として、運用不可を下げるというエンジニアの課題を解決することがメインになってしまい、分析業務を行っているメンバーの求めている形にはなっていませんでした。

それでも二度の構築に価値がなかったとは思っていません。実際にその基盤上で分析業務用のSQLを試してみてもらい、求めているものはこうじゃない、遅い、等フィードバックを通じて相互理解が深められました。

最終的な構成

AWS上にRDSを利用して構築しました。DBのエンジンはSQLServerを利用しています。

f:id:sisijumi:20171220223443p:plain

ついに完成しました。三度目の正直です。やったー

どうしてこの構成になったのか

分析業務に関わるメンバー全員にとってはこの構成が最適だと判断しました。
データ分析を行っているメンバーは今まで通り分析業務が行えます。社内にあったデータ分析基盤はSQLServerがメインになっているものであり、さまざまな業務がSQLServerに最適化されている為です。既存の資産(分析用のクエリ等)が再利用できたり、データ分析業務を行っているメンバーの道具を変える必要が無かったり(例えばSQLServerManagementStudio 等がそのまま流用可能)、さまざまな利点がありました。
また、マネジードな製品を利用することでエンジニアの運用負荷も下げられます。課題としてパフォーマンスに対する懸念はありましたが、列ストアインデックスなどを利用してさまざまなパフォーマンスチューニングを行った結果、現行と同等の性能は出ています。

運用開始

社内のデータ分析基盤を利用していた業務は徐々にクラウドデータ分析基盤に移行していっています。 また、日々のETLの結果も下記のような形でSlackに投稿されています。 今後は安定的にこの基盤を運用していければ良いと考えています。

f:id:sisijumi:20171221011437p:plain

明日は id:aitam による オフサイト・開発合宿に。プロジェクター+ Wi-Fi環境のある宿3選 です。

一休.comレストラン アプリのローンチと2度のメジャーアップデートを通して、デザイナーとして学んだこと

この記事は 一休.com アドベントカレンダー 2017 の 18日目です。

一休.comレストランでアプリのUI/UXデザイナーをしているid:vivashion です。

私は一休.comレストランのアプリを開発し始めた時からデザイナーとして関わっています。 当アプリはファーストローンチから4度のメジャーアップデートをし、現在(Ver.5.3.2)に至ります。 ファーストローンチのVer.1.0.0、Ver.2.0.0、Ver.3.0.0のメジャーアップデートは大きくデザインが変わり、アプリ開発体制や環境も大きく変化してきました。

これまでのアプリ開発において紆余曲折ありましたが、その中で私がデザイナーとして何を考え、何を学んだか、ご紹介すると共に、この記事を読んでくださる皆様にとって良いプロダクト開発のための気づきやヒントになればと思います。

開発しているアプリはこちら。



一休.comレストランアプリ ローンチ Ver.1.0.0

f:id:vivashion:20171218140501j:plain

アプリ開発プロジェクトの立ち上げ

一休.comレストランは、元々Webしかない状態でしたが、社内のエンジニアからの発案で、アプリを開発しようという意見が寄せられたことがアプリ開発プロジェクトが立ち上がったきっかけでした。しかし、「アプリ開発をしよう」と動き始めたはいいものの、社内にアプリ開発のノウハウが無かったのです。

そのような状況の中、たまたま弊社のエンジニアが、UIデザインを強みとしているGoodpatchの代表である土屋さんと知り合いで、アプリ開発におけるノウハウも多く持っていることから、アプリの共同開発をお願いしました。共同開発を行った理由としては、一緒にプロジェクトを進めながら自社内にもアプリ開発のノウハウを取り入れたいと考えていたためです。

Goodpatchからは、ディレクター1名、iOSデザイナー1名、
一休からは、iOSエンジニア1名、Androidエンジニア1名、Androidデザイナー1名(私)の
計5名でプロジェクトメンバーが構成されました。
また、GoodpatchからiOSエンジニア、Androidエンジニアの方が実装のアドバイザーとして協力してくださいました。

アプリ開発における全てのことが初めての経験で、全てのことが学びとなったプロジェクトでした。

Ver.1.0.0の開発の仕方

まずGoodpatchが一休.comレストランのサービスについて知ることから始まりました。どんなユーザー層がサービスを利用していて、どんなシーンで利用しているか。そして、どんなステップを踏んでレストランを予約しているか。それらの情報からペルソナを複数人作成し、ペルソナ毎のカスタマージャーニーを書き、リーン・キャンバスを埋め、ユーザーにどんな価値を提供していくかを詰めていきました。

この時、提供しようとした価値とは、「Webよりも簡単に予約できること」。例えば、Webよりも予約するまでの画面遷移のステップを減らすこと、予約するプランの見比べをしやすくすること等です。

このように開発するアプリの根底部分を固めた後、画面遷移図を作成し、プロトタイピングを行っていきました。まずは「色」の概念が入らないようにグレースケールで画面をデザインしていき、作って捨ててを何度も繰り返し、全画面を作成し、その後、サービスやペルソナに合わせたカラーを入れ込みアプリ全体のデザインを完成させました。

完成したデザインを基にエンジニアが実装していき、都度実装された画面を確認し、細かいデザインの調整も行いアプリをローンチすることができました。

Ver.1.0.0でデザイナーとして学んだこと

全てが学び

アプリ開発に関するノウハウが皆無だったので、プロジェクトで発生するすべての事を学ぼうと必死でした。

プロジェクト発足当時、私はAndroidのデザイナーとしてジョインしましたが、それまでAndroidのデザインガイドラインも、iOSのHuman Interface Guidelines( HIG)もちゃんと見たことがない状態でした。アプリのデザインに関する知識や経験も皆無だったので、Prottを使ったプロトタイピング、デザインのフェーズにおいては、まずGoodpatchのiOSデザイナーの方が出してきた案に対し、それをAndroidのMaterial Designに落とし込むということを行い、アプリのデザインの仕方とMaterial Designについて学んでいました。


Android 4系とMaterial Designの壁

当時はMaterial Designのガイドラインもそれほど充実していなかったので、概念の理解を深めそれをデザインに落とし込み、ユーザーが意識しなくても使えるUIを心がけました。しかし、その当時はMaterial Designを実装するのに大きな壁がありました。Android 4系OSのシェアがまだ大きく、アプリのサポート対象としていたことです。Material Designを実装ベースで比較的簡単に表現できるのは5系のOSからでした。

Material Designの大きな特徴の1つとして、画面遷移のインタラクションになめらかなアニメーションを取り入れるというものがあります。いくらUIをMaterial Designにしても4系をサポートするとなるとこのなめらかなアニメーションを表現できないという制限があり、デザイナーとしては非常にもどかしい思いをしました。

今、Androidアプリをリデザインするとしたら、とてもスムーズでなめらかな画面遷移を表現でき、よりMaterial Designらしいアプリを開発できることでしょう。
(現在、Androidアプリの開発はほぼストップしていますが...)

1度目のメジャーアップデート Ver.2.0.0

f:id:vivashion:20171218140537j:plain

Ver.2.0.0 開発プロジェクトの立ち上げ

iOS、Android両アプリをファーストローンチして間もなく、会社の上層部からアプリのコンセプト変更とともに機能追加の要請が降りてきました。

降りてきた要望としては、「もっと簡単に操作できるデザインに」、「当日行けるレストランを見つけやすく」というものでした。この1度目のメジャーアップデートプロジェクトが発足した時点で、Goodpatchとの契約は終了し、新たに社内からディレクター1名、iOSデザイナー1名がジョインし、完全内製 のチーム体制になりました。

多くの問題を抱えながらのプロジェクト立ち上げ、iOS Ver.2.0.0のリリースでしたが、非常に多くのことを学べたプロジェクトでした。
(プロジェクトの途中にAndroidの開発は一旦中止となりました)

Ver.2.0.0の開発の仕方

改めて新しいコンセプトのアプリのリリースに向けて、どんなアプリにするかミーティングを重ねました。

「もっと簡単に操作できるデザインに」のコンセプトに対しては、デザイン的なアプローチで改善する。「当日行けるレストランを見つけやすく」のコンセプトに対しては、地図検索で現在地付近のレストランを探せる機能を提供することに決定しました。さらに、新しくジョインしたiOSデザイナーからの提案として「ラグジュアリー感の演出」を追加のコンセプトとしました。

この時のプロジェクトでは、iOSデザイナーが画面遷移やデザインをガリガリ作って、それをエンジニアが実装していくスタイルでした。私が担当していたAndroidのデザインは、この時もiOSデザイナーが出してきたデザインをMaterial Designに落とし込むようにしていました。

Ver.2.0.0でデザイナーとして学んだこと

チーム内コミュニケーションの大切さ

上述しましたが、私は、Ver.2.0.0の開発においてもiOSデザイナーが出してきたデザインをMaterial Designに落とし込むというアプローチを取っていました。

iOSデザイナーがデザインをガリガリ作って、どんどん作業を先行してしまったので、デザインの意図がどのようなものかというコミュニケーションが欠落しがちで、デザインの意図をよく理解しないまま、iOSからAndroidへのデザインの移植をしていました。

iOSデザイナーが1人で完璧なデザインを作れるということではないので、コミュニケーション不足によって改善できるポイントを見逃していた可能性もあります。より良いユーザー体験を提供するにはまずチーム内のコミュニケーションを透明にすることが大切だと改めて感じました。

自分の意見をはっきりと伝えることの大切さ

Ver.2.0.0からジョインしてきたiOSデザイナーはそれまでアプリのデザインをしたことはありませんでしたが、優れたWebデザイナーでした。その彼がデザインした画面はHIGに沿っておらず、初めて目にするようなUIや、ユーザーが予期しないであろうインタラクションが散見されました。当然、ユーザーにとって使いやすいだろうとデザインされたものだったのですが、馴染みのないUIだったためにユーザーを迷わせていた箇所も多かったことでしょう。

私は、そのようなUIの欠点を認識していたにも関わらず、修正・改善すべきという発言を控えてしまっていました。当の彼が優れたWebデザイナーで、彼にデザインを任せておけば大丈夫というフィルターがかかってしまっていたためです。

結局、そのデザインのままアプリをリリースすることになり、ユーザーに提供してしまったことを後悔しています。自分が気づいていたUIの欠点、修正したほうが良いという意見をはっきりとチームメンバーに伝えていればそのような結果にはならなかったことでしょう。

アプリは会社の事業としての1つのプロダクトであり、収益を上げていかなければならない

至極当然のことなのですが、事業会社において、アプリはビジネスをしていく上でのひとつの武器です。つまり、アプリを開発したということはそれ相応の結果を求められますし、収益を上げられないアプリに価値はありません。

プロジェクトが立ち上げ時に会社上層部から提案された「当日行けるレストランを見つけやすく」というコンセプトには、それをコンセプトに掲げるだけの数字的根拠があり、ユーザーにアプリならではの機能を提供し、それの収益化が見込めるという判断によるものでした。

2度目のメジャーアップデート Ver.3.0.0

f:id:vivashion:20171218140557j:plain

社内の組織体制の変更によりVer.2.0.0のiOSデザイナーが部署異動になり、晴れて私がアプリのメインデザイナーになりました。

CTO伊藤直也さんが一休にジョインし、アプリチームの開発体制、デザインの改善に乗り出しました。まず、直也さんからアプリのUIがどんなものであるべきか勉強会が開かれました。

f:id:vivashion:20171218142739p:plain

この勉強会が大きなきっかけとなり、まずはHIGにできるだけ沿った形のUIにし、使い勝手を向上させることが大きな目的となりました。

Ver.3.0.0の開発の仕方

まずHIGからかけ離れている画面、UIを洗い出しました。例えば、アプリ立ち上げ後のトップ画面はドロワーメニューを採用していたり、検索のパーツが独自UIになっていたり、ナビゲーションバーが無い画面があったり、プッシュで画面遷移するべきところがモーダルで表現されていたり。それらの画面をそれぞれHIGに沿った形のUIにするためにプロトタイピングを繰り返し、開発メンバー全員で議論しながらアプリ全体を再設計しました。

Ver.3.0.0でデザイナーとして学んだこと

HIGの大切さ

HIGはiOSアプリをユーザーが触る上で非常に重要なものです。HIGに記載されているUIは、iPhoneユーザーが日常で頻繁に触れ、自然と使い方を学んでいて、意識せずとも操作できるUIであるからです。

そもそも一休.comレストランのアプリは、簡単に、スムーズにレストランを予約できるということが大前提です。なので、ユーザーがこの目的を達成するために、如何に意識させないUX、UIにするかを考えなければなりません。

プロトタイピングの大切さ

プロトタイピングすることで、実装する前にアプリを疑似体験することができます。デザインモックをベースにユーザー体験を向上させるために議論し、プロトタイピングすることが実際のユーザー体験の向上につながります。ビジュアル化されたものはチームメンバーのさらなる改善案を引き出し、より良いプロダクトをデザインできます。

おわりに

今回は、ファーストローンチから、デザイン的に大きく変更がなされた2度のメジャーアップデートを通して学んだことを紹介しました。これらはデザイナーとして多くの学びを得た体験でした。

まだまだ一休.comレストラン アプリは、ユーザーにより良い体験を提供するために改善しなければならないことがたくさんあります。一休として「こころに贅沢を」を提供できるアプリになるよう、さらなる改善をしていきます。




明日はhayatoiseさんの
「Google Analytics APIでWordPressのタグとカテゴリのPV数を取得する方法」です。

qiita.com

あえてテクニカルなコーディングをしないという選択肢

この記事は一休.comアドベントカレンダー2017の17日目です。 残すところ一週間とすこしですね

一休.com スパ を運用・開発しているid:kichion0526です。

テクニカルな話や一休の苦労話etcは諸先輩方がたくさん書いてくれているので

最近、何を意識して実装しているかを書き残したいと…

ギークなテッキーになりたかった故に小難しいことをしていた

前職からC#で書くことも多く、リフレクションなどを使い倒しメタプログラミングがすらすらできるようになるのが当時の目標でした

一例として
~ゲームにおける様々なアイテムを生成するファクトリ~

public abstract class ItemBase
{
    protected ItemBase(int id) {
        Id = id
    }

    /// アイテムカテゴリを取得します。
    public abstract ItemCategory Category { get; }

    /// アイテムIDを取得します。
    public int Id { get; }

    /// アイテム名を取得します。
    public abstract string Name { get; }
}

public enum ItemCategory
{
    Weapon = 0, // みんな大好き武器
    Armor = 1, // みんな大好き防具 
    GachaTicket = 2, // みんな大好(ry
    ...(などなどいっぱい)...
}

public class Weapon : ItemBase
{
    public override ItemCategory Category => ItemCategory.Weapon;
    ....(コンストラクタ等でid指定etc)...
}

public class Armor : ItemBase
{
    public override ItemCategory Category => ItemCategory.Armor ;
    ....(コンストラクタ等でid指定etc)...
}

public class GachaTicket : ItemBase
{
    public override ItemCategory Category => ItemCategory.GachaTicket;
    ....(コンストラクタ等でid指定etc)...
}

アイテムクラスを定義したら端的にswitch文でファクトリ作ると下記のイメージ

public static class ItemFactory
{
    public static T Create<T>(int id, ItemCategory category) where T : ItemBase
    {
         switch (category)
         {
             case ItemCategory.Weapon:
                 return new Weapon(id);
             case ItemCategory.Armor:
                 return new Armor(id);
             case ItemCategory.GachaTicket:
                 return new GachaTicket(id);
                ...(その他もろもろ)...
         }
    }
}

「新しいアイテムが追加されるたびにここをいじるのはめんどくさいなぁ」
と思うとリフレクションの出番です

public static class ItemFactory
{
    private static readonly Dictionary<ItemCategory, Func<int, ItemBase>> Items;

    static ItemFactory()
    {
        var constructors = typeof (ItemBase).Assembly.GetTypes()
            .Where(x => x.IsSubclassOf(typeof (ItemBase)))
            .Where(x => !x.IsAbstract)
            .Select(x =>
            {
                // コンストラクタの引数の型
                var argumentType = typeof (int);

                // コンストラクタ
                var constructor = x.GetConstructor(
                    BindingFlags.Instance | BindingFlags.Public,
                    null,
                    CallingConventions.HasThis,
                    new[] {argumentType},
                    new ParameterModifier[0]
                );

                if (constructor == null)
                    return null;

                // コンストラクタの引数
                var id = Expression.Parameter(argumentType, "id");

                // コンストラクタをデリゲート化
                return Expression.Lambda<Func<int, ItemBase>>(
                    Expression.New(constructor, id), id
                    ).Compile();
            })
            .Where(x => x != null)
            .ToArray();

            Items = constructors.ToDictionary(x => x(0).Category);  // Keyを生成するためなので引数は何でもいい
    }

    public static ItemBase Create(ItemCategory category, int id)
    {
        return Items[category](id);
    }
}

これでItemBaseを継承するクラスのコンストラクタを辞書化しておけるのでCreateメソッドがこんなシンプルに!
しかも、辞書はstatic変数で保持しているのでアイテム生成のコストも気にしなくていい!
(こんな書き方できるのかっこいい!)

何が得られたの?

メリット

  • 新しいカテゴリのアイテムが追加されるたびにファクトリを修正しなくて良くなった
  • 生成メソッドの行数の少なさ
  • (かっこいいという満足感)

デメリット

  • switch文の実装より初期実装に時間がかかる
  • リフレクション知っているとしても初見は黒魔術
  • Dictionaryなので2つ以上のクラスで同じカテゴリを使うと一律エラーで死ぬ
  • アイテム種類数が少ないと単純に行数が増えてる
  • 後任の人が困る材料になりかねない

もともとは
「新しいアイテムが追加されるたびにここをいじるのはめんどくさいなぁ」
というモチベーションから始まった改修でした

今でこそ振り返ると複雑な概念を導入した割には解決したことが薄すぎると思っています

こうなるとswitch文を書き換えないという選択肢の方が良さそうです

結局何が言いたいの?

ホントの目的がないとテクニカルなコーディングはただの自己満足になると感じています

私自身、本当にテクニカルな手法が必要なのかを意識するきっかけになったのは一休に転職するようになってからです
極端な例では「この改修でどれくらい儲かるか」「今のチームの人が全員入れ替わったら扱えるか」なんてことを考えたりします

一休はエンジニアでもビジネス視点が求められているのでより「事業目標」に向かってコーディングできていると思います

より目的を意識してプログラミングと付き合うと
どうコーディングしたらいいか?の問いに答えが出しやすいのかなと実感してます

まとめ

目的を見失ったプログラミングは自己満足になりがち という話でした

この考えに至ったのも一休にいる優秀な方々のおかげだと思っています これからも社内のエンジニアの方から学びを得て全能感を高めていけたらと…

つらつらと書きましたが先人たちがいい言葉を残してくれていますので、それで締めたいと思います

プログラマが学ぶべき最も大切な技能というのは、コードを書かないときを知ることなのかもしれない。


最も読みやすいコードは、何も書かれていないコードだ。

(「リーダブルコード」和訳版 第13章「短いコードを書く」P.168 より)


明日は sagisakat さんの
アプリのローンチと2度のメジャーアップデート、何を考えてデザインしたか」 です

レストランアプリのアイコンをクリスマス仕様にした話

この記事は 一休.com アドベントカレンダー 2017 の 15日目です。

一休レストランiOSアプリの開発ディレクターをしています、id:tsuchidah です。

クリスマスまであと10日となりました。 今回は、ふとした思いつきでアプリのアイコンをクリスマス仕様に変更して、どんなことが起きたのか、数字的な面や得られた知見などをご紹介したいと思います。

きっかけ

一休.comレストランにとってクリスマスはとても大事な時期です。クリスマスに大事な人と素敵なひとときを過ごすため、レストラン選びは外せない…。多くのユーザーさんが一休.comレストランに訪れます。ですが、他の予約サービスなどを使うユーザーさんもたくさんいるはず。そこで、少しでも弊社サービスを候補に入れてもらおう、と考えた時にアプリをクリスマスアイコンにすることを決めました。

狙う効果は2点

  • Storeで目を引くこと → 新規インストール数の増加
  • ホーム画面で目を引くこと → 起動率の増加、そこからのコンバージョン

クリスマスを意識していない人に早いうちからアイコンを目にしてもらい、「あ、そういえばそろそろか」「今年は一休で探してみようかな」と意識してもらえればいいな、という割りと軽い気持ちでスタートしました。

リリース

11月初旬に次のようなかたちでリリース。

f:id:tsuchidah:20171215120437p:plain

背景画像に雪を降らせる…といった案もありましたが、11月上旬という時期は逃したくなく、このかたちに落ち着きました。クリスマスまで一月半ほど期間がありますが、実はクリスマス予約は10月初頭から始まっています。ハロウィンが終わり、11月は一般的にもクリスマスに向かって盛り上がっていく時期。いいタイミングでリリースできたのではないかと思っています。

結果

数字的な変化

新規インストール数

特に伸びませんでした。

これはサービス内容によりけりで結果は異なると思います。弊社サービスはレストラン予約サービス。レストランを予約する時、「まずStoreへ…」となる方は少ないと思います。とは言え、インストール数が増える時期なので多少拍車を掛けられるかもと期待をしたのですが、残念な結果となりました。

起動率

起動セッション数、DAUが伸びました!

セッション数、DAUどちらもおよそ+20%ほど。アプリをフォルダーに入れる方も多く、なかなか目に触れないのでは…と懸念していたので、予想以上の数字でした。 こういった数字の成果が出たことは大変よい知見になりました。

予約数

明確な成果はわかっていません…。

予約数は上昇したものの、この時期は予約数が増える時期なので、クリスマス仕様にした成果とは断言できません。ただしセッションが増えた分CVRが下がらなかったので、悪い結果ではないと判断しています。

その他

別チームへの影響

効果を社内に共有したところ、PCブラウザ、スマホブラウザでも取り入れようという話になり、間もなくリリースされました。

f:id:tsuchidah:20171215123328p:plain

クリスマスの日付をクリックすることで、すぐに検索結果に遷移することが出来ます。そのアイデアは思いつかなかったので、やられた!という気持ちになりました(笑)

社内からの声

とてもポジティブな意見をたくさんいただきました。また、「友達からいいね!と言われたよ」など、間接的にユーザーさんのフィードバックを受け取ることができました。アイコンやUIを季節に合わせて変えるという一見効果がわかりにくい施策で、ユーザーさんにポジティブな印象をもっていただけると思うと、やってよかったと心底思います。

まとめ

ふとした思いつきだった割には、得られたものが多かったため、とても満足しています(笑)また、他社のレストラン予約アプリがクリスマスアイコンに変わっているのを見つけては、「弊社サービスが影響しているんだったらいいなぁ」と妄想をしています。 次もアイコンを変えるチャンスがあれば試してみよう、とチーム内で話していますので、次回乞うご期待です。

一点、iTunesConnectが12/22〜12/27までお休みのため、良いタイミングでアイコンを通常仕様に戻せるかが気がかりです…。

最後に、

クリスマスまであと少し、クリスマス仕様なのは今だけ!(笑)是非アプリやサイトに足を運んでみてください。 まだクリスマスのご予約がお済みでない方は、クリスマス直前割という企画も行っていますので、是非。

みなさま、素敵なクリスマスをお過ごしください。

明日は @hirosawak さんの「一休.com iOSアプリでのfastlane使用例」です。お楽しみに。