一休.com Developers Blog

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

imgix導入で画像最適化とサイトスピードを改善した話

こんにちは。 一休.comの開発基盤を担当しています、id:akasakasです。

今回は、画像最適化配信サービスであるimgix宿泊レストランサイトに導入して、 画像最適化・サイトスピード改善につなげたお話をしたいと思います。

ここでお話しする内容

  • サイトスピードという観点での一休が抱えていた課題(の一部)
  • imgixの特徴とそこでできる解決策
  • imgix導入の効果
  • imgix導入をする上で大変だったこと
  • これから画像最適化を考える人たちへ
  • まとめと感想
  • おまけ(与太話)

諸注意

imgixを導入して、画像最適化という面でサイトスピード改善につながりましたが、 サイトスピードという観点で一休が抱えている課題はまだまだあります。 imgixを導入すれば、サイトスピードは万事解決!!!という話ではありませんので、悪しからず。

サイトスピードという観点での一休が抱えていた課題(の一部)

画像最適化ができてなかったため、サイトスピードが遅かった

画像の最適化ができてなかったことが大きな原因の一つでした。

一休というサイトの特性上、ホテル・旅館・レストランの画像を綺麗に見せたいというのが前提としてあります。 ホテル・旅館・レストランの高級感をユーザーに伝えたいため、画像サイズが大きい&高品質な画像を取り扱っています。

サイズ・転送量の大きい画像を扱っていることがサイトスピード低下の大きな原因となっていました。 無駄なリソースの削除をして、画像最適化・サイトスピード改善につなげたかったというのが背景としてあります。

今まで画像最適化ができてなかった結果、サイトスピードが遅くなり、ユーザーにご不便をかけていたのを改善して、より快適に使ってもらいたいという思いから画像最適化プロジェクトが始まりました。

imgix導入前のPageSpeed Insightsのスコア

imgix導入前のPageSpeed Insightsのスコアが以下になります。

宿泊

f:id:akasakas:20180624205650p:plain

[補足] 宿泊のキャプチャについては2018/01/19時点のもので、厳密にいうと画像最適化だけでなく、他にもいろんなボトルネックがありましたが、スコアが低い大きな原因は画像最適化の部分でした。

レストラン

f:id:akasakas:20180624205710p:plain

imgixの特徴とそこでできる解決策

上記の課題を解決するためにimgixを導入しました。 imgixは画像最適化に特化したCDNサービスというイメージです。 国内でimgixの導入企業として有名なのが、日本経済新聞社さんですね。

個人的に思うimgixの特徴としては以下が大きいと思います。

  • 拡張子自動判別
  • 自動圧縮設定
  • ロスレス圧縮
  • ストレージサービス連携(Amazon S3/Google Cloud Storage)

imgixのAPI Referenceなどに詳細が記載されていますが、こちらでも簡単に触れてみます。

拡張子自動判別

imgixの画像URLパラメータに auto=format でjpegの画像もブラウザによってwebpなどに変換してくれます

自動圧縮設定

imgixの画像URLパラメータに auto=compress で自動で圧縮してくれます

ロスレス圧縮

ロスレス圧縮が可能な画像に対して、 lossless=0 とすれば、ロスレス圧縮されます lossless=1 ではないというのが地味に罠でした。

ストレージサービス連携(Amazon S3/Google Cloud Storage)

画像の移行をする上で、これが大変ありがたかったです。 imgixではストレージサービスを指定するだけで画像のの最適化・配信ができます。

一休ではAmazon S3に画像コンテンツを格納しているので、 S3バケットの指定と、Access Key ID/Secret Access Keyを設定すれば、 imgixから画像が参照できるというところまでできました。

既存資産(一休でいうS3)をそのまま使えたので画像移行がスムーズに行うことができました。

before:S3バケットをみるCDNサービスを

f:id:akasakas:20180624210008p:plain

after:imgixに切り替えたというイメージです

f:id:akasakas:20180624210107p:plain

URLの変更はありましたが、そこは気合いと根性でアプリケーション側を直しました。

ストレージサービス連携はS3だけでなく、Google Cloud Storageにも対応しています。

imgix導入の効果

PageSpeed Insightsのスコアは宿泊・レストラン共に大きく改善されたのがわかります。

宿泊

before(再掲)

f:id:akasakas:20180624205650p:plain

after

f:id:akasakas:20180624210352p:plain

beforeのキャプチャについては2018/01/19時点のもので、厳密にいうと画像最適化だけでなく、他にもいろんなボトルネックがありましたが、スコアが低い大きな原因は画像最適化の部分でした。imgixの導入で20〜30点ほどスコアが改善されました。

レストラン

before(再掲)

f:id:akasakas:20180624205710p:plain

after

f:id:akasakas:20180624210737p:plain

画像転送量

ある画面でimgix導入前後の画像転送量比較が以下になります。

before(imgix導入前)

f:id:akasakas:20180625101726p:plain

after(imgix導入後)

f:id:akasakas:20180625101748p:plain

10MB近く削減されています。 imgix導入前は全く最適化できてませんでした。 そもそも10MB越えの画面はどうなんだ?という疑問もありますが。

上記の例が最も画像転送量を削減できましたが、他の画面でも軒並み画像転送量を抑えることができたので、導入の効果は非常に高かったと感じます。

imgix導入をする上で大変だったこと

技術面

  • 既存の資材(S3)を使えたので、切り替えが楽だった
  • URLの変更はあったけど、そこは気合いと根性で直した

ので、正直なところ技術面で大変だったことはなかったです。

技術面以外

実はこっちの方が大変でした

英語でサポートと契約などのやり取り

imgixは日本の代理店がありません。 最初にサポートに確認したら「ない!!!」って言われて、そのメールをそっ閉じしたのは今思えば、懐かしいです。 英語が堪能な帰国子女さんのお力を借りて、なんとかVolume Pricingの交渉ができました。

料金見積もり

見積もりが難しかったです。 実際に1ヶ月フリーで使わせてくれて、そこで正確な見積もりをとりました。 imgixのサポートは丁寧で優しかったです。

導入の承認

CDN費用のコストカット+技術的な改善ができることを説明して、導入の承認を得ることができました。

これから画像最適化を考える人たちへ

実際にimgixを導入しましたが、自分の進め方がよくなかったと思う部分が多々ありました。 反省/振り返りを含めて、どうやって導入を進めればよかったかというのを考えてみました。 あまりテッキーな話ではないです。仕事の進め方的な話がメインです。

ポイント

imgixに限らず、導入効果とコストカットの説明ができれば、導入できると思いました。

技術選定

候補としては以下の3つがあげられるのかなと思います

  1. imgix
  2. Cloudinary
  3. Fastly Image Optimizer

技術比較

一休の場合、画像最適化をポイントとしました。 imgix や Cloudinary の方が画像最適化を主眼に置いたサービスであるため使いやすい印象を覚えました。

移行難度

既存資産(S3や一休の画像配信構成)をそのまま使えるという点でimgixの方が移行をスムーズに進めることができるという印象がありました。 なので、この時点でほぼほぼimgixに決めてました

移行コスト

正直、imgixの見積もりしかとってなかったです。 理想は3つそれぞれ見積もりを出して、比較するべきだったというのが反省点です。

imgixの導入コストが既存より安いor同等であればOKかなと思ってました。 技術的改善が見込めれば、導入前後のコストが同等でも問題ないだろうという考えがあったからです。

その他細かい話

あと細かいところで 特定画面だけimgixを部分導入して、詳細なBefore/Afterの効果比較を実施し CDN費用のコストカット+技術的な改善(画像最適化)ができることを説明して、導入の承認を得ることができました。

まとめと感想

imgixを導入して、画像最適化&サイトスピードを改善ができました。

モダンなインフラに乗っかると、割と簡単にベストプラクティスを実践できるのだなというのが正直な感想です。 既存の環境でチューニングをがんばっていた諸氏には感謝をしつつ、未来のためにはどんどん新システムに塗り替えていきたいですね。

これから画像最適化を考えているエンジニアの方々にとって、この記事が少しでも参考になれば幸いです。

諸注意をもう一度

imgixを導入して、画像最適化という面でサイトスピード改善につながりましたが、 サイトスピードという観点で一休が抱えている課題はまだまだあります。 imgixを導入すれば、サイトスピードは万事解決!!!という話ではありませんので、悪しからず。

おまけ

レストランの導入が想定よりも遥かに速かった

当初は宿泊のみ先行して対応するという話で進めてて、レストランの対応は冬ぐらいになる

はずでしたが、

やっぱりモダンな技術とその効果が気になって、飛びついたご様子です。

技術調査・契約・見積もりなどのめんどくさいことをやって、慎重に事を進めてきた自分としては おいしいところだけをかっさらったレストランのエンジニアをみて、イラッときたのは内緒です。

でも、レストランのサービスが改善できたのでOKです

最後に

imgix移行に協力して頂いた方々に感謝

  • インフラ面で協力してくれたエンジニア
  • 英語のやりとりが発生した中で、サポートして頂いた帰国子女の営業さん
  • 画像の変更で作業ワークフロー上、大きな影響を受けたにも関わらず、ご理解とご協力を頂いたデザインさん

この場を借りて、御礼申し上げます。

参考

一休のETL処理をAirflowで再構築しました

一休のデータサイエンス部に所属しています小島です。

以前データ分析基盤の構築で記事を上げていましたが、今回はETL*1周りの話をしようと思います。 user-first.ikyu.co.jp

今回ETLのツールとして導入したのはAirflowというツールです。 2017年のアドベントカレンダーでも紹介させていただきました。 一休のデータフローをAirflowを使って実行してみる

一休のETLの現状について

一休のETL周りは以下の画像のようになっていました。 f:id:kojimakohei2:20180607154849p:plain

課題

  • ETLの処理時間が伸びた(出社後も処理が続いていた)
  • エラーのリカバリ作業に時間がかかる(ログが確認しにくい, サーバーに入って作業しなければいけない)
  • 複雑な依存関係の定義がしにくい(どれとどれが依存しているかわからない)
  • リソース負荷(全て並列で実行していた)
  • 処理毎のボトルネックが把握できない

ツールの問題というよりは正しくツールを使えていないことが原因でしたが、上記の理由でDigdagをAirflowに移行することを決定しました。

なぜAirflowにしたか

  • 処理が複雑にかける
  • 管理画面の依存関係のグラフと、tree形式での表示がわかりやすい
  • Pythonで記述できるので、機械学習などと相性がいい
  • 管理画面から簡単に再実行できる

Airflowの導入した結果できるようになったこと

  • ETLの時間が把握できるので、ボトルネックとなる処理に関して対処が可能になった
  • 再実行が管理画面から簡単にできるので、リカバリーが簡単になり運用に時間がかからなくなった
  • 並列数などを簡単に設定できるので、負荷が少なくなった
  • 依存関係がとても簡単に定義できる

ボトルネックとなる処理の検知

f:id:kojimakohei2:20180601122438p:plain めちゃめちゃ簡単にわかるようになりました。

対象日付のリカバリーが管理画面から簡単にできる

f:id:kojimakohei2:20180601122649p:plain

導入に当たって苦労したこと

  • ETL処理は意外と追加や変更が多いので開発しやすい環境を作ること
  • Airflowの制約があったこと

ETL処理は意外と追加や変更が多いので開発しやすい環境を作ること

ETLで肝なのはデータが正確にDWHに格納できるかというところです。 なので開発環境を作る必要があります。 ただ、開発環境を作成する際に考えなければいけないのがアウトプットするDBについてです。 インプットに関しては参照するだけなので、あまり考慮しなくていいのですが アウトプット用のDBをどのように用意するかを考え、結果的に開発環境ではdockerコンテナを立て件数を自動的に絞ることで開発ができるようにしています。 BigQueryや、S3などについてはコンテナを作れないので環境ごとにbucketを変更することで開発できるようにしています。

Airflowの制約があったこと

Airflowは1.9.0を使用していますが、内部的に全てUTCで動いています。 利用としてはこちら記事にわかりやすく書いてありましたが、タイムゾーンに依存しないように内部のシステムは全てUTCを使用するようにしているらしいです。 [AIRFLOW-289] Use datetime.utcnow() to keep airflow system independent - ASF JIRA

なので一休では表示側はUTCで処理部分は明示的にJST => UTCに変換しています。

今後ETLツールの利用方法

レコメンドなどに使用しているモデルなどについて機械学習の学習部分を自動で行なっていくことと、 ボトルネックになっている処理を改善し、ETLの最適化を図るようにしていこうと思います。

*1:Extract、Transform、Loadの略で、企業内に存在する複数のシステムからデータを抽出し、抽出したデータを変換/加工した上でデータウェアハウス等へ渡す処理

Send With Confidence Tour で一休.com の事例を話してきました

こんにちは。一休.com の宿泊開発基盤のお手伝いをしている id:shiba-yanです。

先週の話になるのですが、SendGrid の日本国内代理店の方から「一休.com での SendGrid を利用した運用について事例を話してほしい」とお願いされたので、簡単に出したが話してきました。

当日は SendGrid 本社から技術系の偉い人が来日して、世界初の話を含めていろいろと話してくれました。

様々な話がありましたが、中でも Geopod*1 が東京にもあることは知りませんでした。昔はシンガポールを使っていたみたいですが、今は日本からでも低レイテンシーで送信できるようになっているようでした。

おそらく開催ブログは公開されると思いますが、先にスライドを公開しておきます。メインは 2017 年に発生した、割と大規模な SendGrid 障害時の話です。

一休.com ではメールが非常に重要な役割を果たしているので、多少の送信エラーが発生したとしても、出来るだけリカバリーが行えるように実装しています。

しかし、障害発生時にはいろいろな問題が発覚したため、反省しつつ対応を行っていきました。

割と当たり前のことが出来てなかったと今では思いますが、当たり前を当たり前に行うのは非常に難しいことだと再認識し、今後の開発にも繋げていきたいと考えています。

参考

*1:SendGrid が世界中に用意しているエンドポイントの一つ

Renovateによるnpmパッケージ定期更新

一休.com・フロントエンドエンジニアの宇都宮です。

JavaScriptを使ったWeb開発では、様々なライブラリを使います。開発の活発なライブラリであれば、毎週のようにバージョンアップが行われます。ライブラリのバージョン更新は、それを行ったからといって価値に直結するわけではありません。しかし、以下のような理由から、一定の頻度での定期更新が必要です。

  1. バージョンアップに追従しないと、古いバージョンにロックインされる
  2. 差分が大きいバージョンアップはリスクが高い
  3. ライブラリに脆弱性が見つかった際は速やかにバージョンアップが必要

本記事では、JavaScriptライブラリ管理の標準的ツールであるnpmと、GitHub AppのRenovateを使用した、ライブラリを定期的に更新する仕組みの作り方について解説します。

npmによるパッケージ管理

npmは、JavaScriptライブラリの管理ツールです。

npmでは、使用するライブラリの名前とバージョンをpackage.jsonというファイルで管理します。

以下のように、dependencies(本番環境で使用するライブラリ)と、devDependencies(開発環境でのみ使用するライブラリ)を分け、それぞれのライブラリ名と、バージョン番号を指定します。

{
  "dependencies": {
    "vue": "2.5.16"
  },
  "devDependencies": {
    "vue-loader": "14.2.2"
  }
}

npmパッケージのバージョン番号は原則としてsemverという仕様に基づいており、x.y.zのxがメジャー、yがマイナー、zがパッチバージョンを意味します。メジャーバージョンアップは後方互換性のない変更、マイナーは後方互換性のある新機能追加、パッチは後方互換性のあるバグフィックスです。

依存ライブラリのバージョンには、幅を持たせることが可能です。たとえば「^2.5.16」は、「2.5.16以上のバージョンで、3.0未満」という意味になります。この指定方法はライブラリ向きです。他からライブラリとして参照されることのないアプリケーションでは、バージョン番号を固定しても良いでしょう。

実は、package.jsonだけでは、インストールされるライブラリのバージョンが毎回同じになることは保証されません。vue 2.5.16をインストールしたとしても、vueが依存しているライブラリのバージョンまで、全て同じになるとは限らないからです。

そのため、全ての依存ライブラリのバージョンを記録したファイルを別途用意する必要があります。npm 5以上では、npm installの実行時にpackage-lock.jsonというファイルが生成されます。このファイルには、全ての依存ライブラリのバージョンが記録されます。package.jsonpackage-lock.jsonが存在する状態でnpm installを実行すれば、正確に同じバージョンのライブラリがインストールされます。

これはnpmコマンドの代替であるyarnでも同様で、yarn.lockに全ての依存ライブラリのバージョンが記録されています。

npmパッケージの更新手順

更新の必要なnpmパッケージを探すには、npm outdatedまたはyarn outdatedコマンドを使用します。

yarn outdatedは、semverに基づいてメジャー/マイナー/パッチの分類を行う等、よりリッチな機能を備えています。個人的にはyarn outdatedを好んで使っています。

バージョンアップの必要なライブラリを見つけたら、yarn upgrade vue 等で、ライブラリのバージョンを更新します。あとは動作確認して、リリースするだけです。

簡単そうに書きましたが、実際のところ、npmパッケージの更新は面倒です。バージョンアップしても問題ないか確認するには、CHANGELOGの確認が欠かせません。

一休の宿泊予約サービス開発チームでは、週に1回程度の頻度で、以下のようなプルリクエストを作成して、npmパッケージの定期更新を行っていました。

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

この作業は地味に面倒で、毎週1時間程度の時間をアップデートに割いていました。そこで、パッケージ更新作業の省力化を図るための手段をいくつか検討した結果、Renovateの導入を決めました。

Renovateとは

Renovateは、GitHub上で動作するアプリ(bot)です。Renovateを導入したリポジトリでは、以下のようなプルリクエストが自動的に作成されます。

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

github.com

↑のスクリーンショットは、yarnのリポジトリから取得しています。Renovateは、yarnをはじめとした様々なオープンソースプロジェクトや、Uber等の企業まで、幅広く利用されています。

publicリポジトリでは無料、organization + privateリポジトリでは$15/month~で使えます。更新の必要なライブラリの確認 => CHANGELOGまとめ => プルリク作成という定型作業を代わりに行ってくれるので、十分に元が取れる価格だと思います。

また、Renovate本体はOSSであり、自前でホスティングすることも可能です。

Renovateを導入する

RenovateはGitHub Marketplaceから導入できます。Marketplaceからrenovateを探したら、まずはプランを選びましょう。ここでは、私が個人で開発しているオープンソースプロジェクトにRenovateを導入するため、「Open Source」を選んでいます。

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

次の画面では、有料プランの場合、支払い情報の入力が必要になります。会社のGitHubリポジトリに導入する際は、個人アカウントの請求情報を入力しないよう注意しましょう。

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

最後に、インストールするリポジトリを選択します。ここでは、「ryo-utsunomiya/amazon-block」を選択しています。

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

1時間ほど待つと、以下のようなOnboading Pull Requestが作成されます。

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

github.com

このプルリクでは、renovate.jsonというRenovateの設定ファイルをリポジトリに追加します。デフォルトでは以下のような動作をします。

  • メジャーバージョンアップは別々のプルリクに分割する
  • パッチ・マイナーアップデートを区別しない
  • アップデート用ブランチの名前には renovate/ プレフィックスをつける
  • マージは自動では行わない(人間が手動でマージする)
  • package.json が更新された場合にのみlockファイルを更新する
  • プルリクの作成は2時間毎に1回を上限とする
  • Renovateが作成したプルリクで、マージ/クローズされていないものの数が20を超えないようにする

このプルリクエストをマージすると、Renovate Botが動き始めます。

Pin Dependencies

パッケージのバージョンが固定されていない場合、以下のようなプルリクエストが作成されます。

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

github.com

ここでは、各パッケージのバージョン番号を固定(Pin)します。Pin Dependenciesプルリクをマージすると、パッケージの更新用のプルリク作成が始まります。

なお、依存ライブラリのバージョンをに幅をもたせたい場合は、以下のようにrenovate.jsonを変更すればOKです。

{
  "extends": [
    "config:base",
    ":preserveSemverRanges"
  ]
}

renovatebot.com

パッケージ更新の運用

Renovateによって、プルリクを作成するところまでは自動化されました。残りの作業は、安全を期して人間が行うようにしています。

具体的な手順は以下の通りです。毎週月曜の当番制にしています。

  1. 毎週月曜に、担当者がパッケージ更新の有無を確認する
  2. 更新があれば、パッケージ更新用ブランチを手元にpullして動作確認する
  3. 問題なければ、プルリクをマージする

マイナー/パッチアップデートはすぐに適用しています。一方、メジャーアップデートはソースコードの改修が必要なことが多いので、担当者をアサインして、一定の時間を確保してアップデートしています。

導入の効果

Renovateによる自動化によって、プルリク作成までの手順が標準化されたため、誰でもパッケージ更新作業が行えるようになったのが大きいです(従来は、有識者が気づいたときに更新するという体制でした)。

副次的な効果として、どのようなライブラリを使用しているか棚卸しできる、ライブラリの導入時に運用コストまで考慮できる、といったものもあります。

今後の課題

更新時の動作確認に属人性があるため、バグのある状態でリリースしてしまう危険性があります。

  • 自動テストの拡充
  • 動作確認手順の標準化

などによって、バグのある状態でのリリースを防ぐ対策を強化していきたいと考えています。

おまけ:マイナー/パッチアップデートをまとめる

デフォルト設定の場合、各パッケージ毎に更新プルリクが作成されます。しかし、週1回程度の更新頻度だと、マイナー/パッチアップデートは複数存在する状態になります。そこで、宿泊予約サービス開発チームでは、以下のようなrenovate.jsonの設定を行って、マイナー/パッチアップデートを1つのプルリクエストにまとめています。

{
  "extends": [
    "config:base"
  ],
  "minor": {
    "groupName": "all dependencies"
  }
}

↑のrenovate.jsonを使用すると、以下のようなプルリクエストが作成されます。

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

github.com

一休レストランの事業領域を理解するのに役だった本3冊

一休レストランiOSアプリチームのマネージャーをしているninjinkunです。自分は半年前に一休に入社したのですが、元々外食は好きだったものの、飲食に関わるサービスに関わるのは初めてでした。

そこで自分でサービスを使ったり、営業同行をしてみたりしながら、自分なりに事業領域を理解しようと試行錯誤をしてきました。このエントリではその活動の一環として読んだ本の中から、参考になったものを紹介します。

Hot Pepperミラクル・ストーリー

Hot Pepperミラクル・ストーリー―リクルート式「楽しい事業」のつくり方

Hot Pepperミラクル・ストーリー―リクルート式「楽しい事業」のつくり方

いきなり競合他社の本なのですが、まだ紙媒体だった頃のHot Pepperの話です。立ち上げから、全国展開してアクセルを踏んでいく経緯が事業責任者の視点で書かれています。

自分にとってこの本は、一休レストランの「営業」を理解するという意味で有益でした。飲食店の集客ニーズをくみ取って、そこにHot Pepperという広告媒体を提案しに行く話が具体的に書かれているのですが、ビジネスモデルは違えど一休の営業とも共通点は多いように思います。

他にも、できる営業を分析して、全員に展開していく様は勉強になりました。営業のトークを撮影してみんなで共有したり、社内でロールプレイングをやって営業力を他の社員に展開したり、営業ツールを作ったりと、全く門外漢で知らない手法だらけだったので面白く読めました。営業の活動を知りたい開発者にはおすすめだと思います。

ミシュラン

ミシュランガイド東京 2018

ミシュランガイド東京 2018

一休レストランはお値打ちなお店から高級なお店まで幅広いラインナップがありますが、やはり名店と言われるお店は社内での会話に良く出てきます(商品の文脈でも、プライベートで行ってみたいという文脈でも)。自分はそういった名店の知識がなかったので、定番のミシュランを買って勉強していました。掲載店が近所にあったりするのに気づくと、開拓する楽しみが増えます。

こちらはちょうど2018年版が出たところのようです。

東京カレンダー

東京カレンダー2018年1月号

東京カレンダー2018年1月号

雑誌です。最近は割とゲスめのコピーを採用していることで一部で話題ですが、自分がこの雑誌が優れていると思う点は「お店の利用シーン」を明確に提案している点にあります。

実際のお店を取り上げたデートや女性同士の飲み会、謎の男女の「お食事会」などが(相当誇張されている表現ではありますが)毎号たくさん載っており、飲食店を利用するイメージが湧きます。そして何より、この雑誌のターゲット層は一休レストランのユーザー層と丸かぶりしているのです。ネタだと思われそうですが、自分は最近東京カレンダーを参考にしてユーザーストーリーを考えました。

また、社内にもファンが多いようで、最近作られた社内Slackの #東京カレンダー はやたらと盛り上がっており、同僚とのコミュニケーションにも一役買っています。

おわりに

以上、一休レストランの事業領域を理解するのに役だった本たちでした。飲食店に関する本でおすすめがあれば教えていただけると嬉しいです。

iOSとAndroidの段階的リリース機能を比較する

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

一休レストランiOSアプリを開発しているid:ninjinkunです。

iOSでは今年の夏からiTunesConnectにて段階的リリース(Phased Releases)が導入されています。段階的リリースとは、アプリの新バージョンを提供する際に一部のユーザーから順にアップデートを適用していくリリース方法です。不具合があった際に全ユーザーに影響が及ぶことを防ぐことができ、リスクを低減することができます。

この機能にの詳細については、 iOSの段階的リリースについての注意点 - inFablic | Fablic, inc. Developer's Blog. が詳しく、本エントリは追試 + 以前に使用した経験があるAndroidの段階的リリース(Staged Rollout)との比較になります。

結論を先に書くと、この機能は段階的自動アップデートと呼ぶ方が適切だと思いました。そして使いどころが難しかったので、今後はあまり使わないでしょう。当初はAndroidと同じ機能を期待していたのですが、だいぶ違うものでした。

この情報は2017年12月時点のものなので、来年には状況が変わっている可能性がある点にご留意ください。

日数と公開割合が固定 📅

この機能を使う際には、iTunesConnectから段階的リリースを選択します。

f:id:ninjinkun:20171225130423p:plain

まず気になるのが、段階的リリースの日数が固定されている点です。7日間に渡って1日ずつ固定の割合で公開範囲が広がります。Androidの場合は公開の段階を引き上げる日と、公開する割合を自分たちで決めることができます(ただし手動)。

手動アップデートと新規ユーザーは全員新バージョン 🆕

もう一つ注目したいのが、段階的リリースの最中であっても、ユーザーが手動でAppStoreから新バージョンをダウンロードできるという点です。さらに、新規ユーザーは全員に新しいバージョンがインストールされます

Androidの場合は、公開範囲に含まれなかったユーザーは新バージョンに手動でアップデートすることができません。また、ここはきちんと検証できていないのですが、新規ユーザーについても新バージョンが適用される比率は同じであると思われます。

制御されているのは自動アップデートのみ 🆙

iOSの段階的リリースとAndroidの段階的リリースはどうやら根本的に実装方法が異なるようです。具体的には、iOSは新バージョンの自動アップデートを制御しているだけであり、自動アップデートではない手動アップデートと新規ユーザーの場合には新バージョンが適用されます。そしてどうやら、AppStore上に置かれるバイナリは新バージョンのみのようです。

他方Androidは新バージョンのバイナリを適用するユーザーを制御しており、自動/手動アップデート、新規/既存にかかわらず、公開範囲に含まれたユーザーしか新バージョンをインストールできません。そしてGoogle Play Store上には旧バージョンと新バージョンの両方のバイナリが配置されます。

ロールバック不可 🙅

この違いから、iOSの段階的リリースでは新バージョンに問題が発生しても、ストアが旧バージョンを保持していないためロールバックができません。できるのは自動アップデートの適用範囲を固定の状態で止めることだけです。その間にも新規ユーザーは問題がある新バージョンをダウンロードし続けることになります😓

修正リリースは再審査 👨‍⚖️

修正したバージョンをリリースする際は再度AppStoreの審査があります。このため、一般的に段階的リリースの目的の一つであると思われる、不具合のリスクを抑えながら頻繁にリリースを行ってスピード感を出す目的には使えません。

また、修正したバージョンを提出する際に段階的リリースを選択すると、また1%からやり直しです。今回の使用では、前回の段階的リリース当選ユーザーに新バージョンが配布されているかは確認できませんでしたが、おそらく実装されていないのではないかと睨んでいます。

Androidはこの部分でも先行しており、段階的リリースの対象ユーザーのみに修正バージョンを配布することができます。

一休レストランでの利用事例

一休レストランでは、予約情報を入力する画面のリニューアルを行う際に、リスク低減の目的で段階的リリースを使用してみました。そして危惧していたとおり、リリース後に一部のレアなケースで予約ができないという不具合が見つかりました(事前に可能な限り確認はしていたので、発見できなかったのは仕方なかったと考えています。損害も軽微でした)。

ここで段階的リリースやってて良かった…となると良いのですが、実際は不具合が新規ユーザーのみに起こるものだったため、段階的リリースは全く意味が無かったという結果になりました 😭

おわりに

以上、Androidとの比較を交えながらiOSの段階的リリースについて解説しました。

自分の意見としては、

  • 新規ユーザーには新バージョンが渡ってしまう
  • ロールバックができない
  • 修正リリースがやりづらい

という点から、積極的に使うことはないと思います。今後のアップデートに期待しています。

最後にiOSとAndroidの比較について表にまとめておきます。

  iOS Android
呼称 Phased Releases Staged Rollout
公開日程 7日間で固定 手動で随時変更
公開範囲 1%, 2%, 5%, 10%, 20%, 50%固定 0.5%, 1%, 5%, 10%, 20%, 50% から選択任意
段階的リリース中の全公開 できる できる
段階的リリースのサスペンド できる(ただし手動アップデートは可能 & 新規ユーザーには新バージョンが出続ける) できる
制御できるユーザー 自動アップデートユーザー 全ユーザー
ロールバック できない できる(ただし新バージョンをインストールしてしまったユーザーは戻せない。審査がないので上げ直しで対応)
対象ユーザーへの修正版配布 不明(おそらくできない) できる
ストアに置かれるバイナリ 最新版のみ 新旧複数バージョン

それでは、今年も一年おつかれさまでした。新年会のご予約に、一休.comレストランをぜひご利用ください。

レストランを簡単予約!一休.comレストラン

レストランを簡単予約!一休.comレストラン

  • IKYU Corporation
  • フード/ドリンク
  • 無料

2018/1/11 追記

Androidの段階的リリースの公開範囲は、現在は任意の%で指定できるようになっているとのことです。ご指摘感謝。

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

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

データサイエンス部所属のエンジニア 笹島 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さんの「開発組織の目的型組織への移行」です。