200万品のレシピデータから感謝を伝えるインフォグラフィックを制作した話

f:id:transit_kix:20150421180149p:plain

ユーザーファースト推進室のデザイナー倉光です。

クックパッドではユーザーさんから寄せられた多くのレシピが公開されていますが、先日レシピ数が200万品に到達しました! 特設ページ「ありがとう!みんなのレシピが200万品♪ 」 では、レシピが200万品に至るまでの歩みを紹介するインフォグラフィックを公開しています。

今回はこのインフォグラフィックの制作事例を紹介したいとします。

インフォグラフィックとは?

f:id:transit_kix:20150421134809p:plain インフォグラフィックとは、情報、データ、知識を視覚的に表現したものです。図やグラフなどを用いることで、データを目で見てわかりやすく伝える際などに用いられます。

クックパッドが持つデータといいますと、一例ですが「今日このレシピが何回表示されました」「今月はこのキーワードの検索回数が急上昇中です」といったものが挙げられます。(ちなみに一部のデータは研究者の方向けに公開も行っています)。膨大な数のデータは、それ自体は事象や数値の集合体に過ぎません。これらに対して視点を与え、情報を再構成し、受け手にメッセージを伝えるのがインフォグラフィックの役目です。

視点を与える

今回のインフォグラフィックの制作時のエピソードに移りましょう。

今回の企画は、投稿推進部とユーザーファースト推進室のメンバーで協力して制作を行いました。まずは200万品レシピにまつわる面白そうなデータを選定します。

f:id:transit_kix:20150421134816p:plain

まずはこのようなデータが集まりました。さて、これから何を伝えるか?どう伝えるか?その視点を見つけるのがデザイナーの仕事です。

今回、私たちが一番伝えたかったことは「レシピ作者さんへの感謝の気持ち」でした。そこで各データをただ羅列するのではなく、クックパッドにおけるユーザーコミュニケーションの仕組みも紹介し、レシピ作者さんへの感謝を伝えて、未来のレシピ作者さんへ投稿のきっかけを与えられるようなストーリーにしようと考えました。

ストーリーを具現化する

ストーリーが決定すると、次のフェーズでは「データをどう構成するか」のアイデアを考えます。

f:id:transit_kix:20150421134826p:plain

私はふせんと太めのペンを使用して、手書きで思考を整理することが多いです。今回は一枚のイメージとして表現することが決まっていたので、情報を1ブロックごとに1枚のふせんに記入して、ふせん同士を貼り付けて構成を検討しました。この手法の利点は、気軽に持ち歩けるのでいつでもどこでも情報設計のプロトタイピングが開始できること、ふせんを動かすだけで情報の順序の入れ替えや修正が容易にできること、デザイナー以外のスタッフと議論する際も気軽にデザインに参加してもらいやすくなること… などが挙げられます。また、簡易的ではありますがPC/スマートフォンで情報の構成をどう変化させるかもこの時点で検証しておきます。

この時点でそれぞれのデータに対し、どのようなデザインパターンを適用するかも考えます。今回は、クックパッドにおけるユーザーコミュニケーションの説明にはサイクル型を採用。

f:id:transit_kix:20150421135600p:plain

レシピを書くと、そのレシピを見た他のユーザーさんが料理を作り、感謝の気持ちがつくれぽとして作者さんに届く仕組みを目で見てわかるように図解することにしました。

楽しく、わかりやすく、一目瞭然に

このあと、実際にグラフィックデザインの作業に移行します。 f:id:transit_kix:20150421135300p:plain

この時に気をつけるのは「楽しく、わかりやすく、一目瞭然であること」。大きさ、色、形状などの対比で情報に強弱をつけることで、全体を見たときのまとまりも意識します。また料理に関するデータなので、食や料理にまつわるモチーフを使用することで、情報に対して親しみやすさを感じられるように心がけました。

f:id:transit_kix:20150421135323p:plain

ちなみに今回のインフォグラフィックでも使用しているクックパッドオリジナルのピクトグラム。こちらは社内でオリジナルフォントとして管理されており、サービス全体で統一感を保ちつつも効率的にデザインすることが可能になっています。

またクックパッドの文化として「デザインレビュー」と呼ばれる仕組みがあります。デザイナーはGitHubのIssue機能を利用し、担当する仕事について仮説設計にはじまり最終的な表層部分のスタイリングに至るまでの過程を言語化し、それを社内のデザイナー全員でレビューし合います。Issue上では「このビジュアル表現では言いたいことが伝わってこない」「そもそもの仮説設計がおかしいのでは?」といった議論が日々行われています。

今回もデザインレビューの結果、情報が伝わりづらかった一部のモチーフの選定を変更したり、順序の入れ替えが行われた後に最終版が完成しました。

1つのレシピデータの向こうには1人のユーザーがいる

公開後から2週間で、インフォグラフィックのページには約30万PVがありました。更に嬉しい反応として、レシピ作者さんがいちおしレシピを投稿できるコーナーを用意していたのですが、約1700件ものいちおしレシピが寄せられています。

レシピ作者さんのいちおしレシピが大集合!

それぞれのレシピに添えられているエピソードも「母が作ってくれた思い出の味です」「このレシピがきっかけで沢山の友達が出来ました」といった素敵なものばかり。まさに珠玉のレシピ集となりました。1品のレシピデータの向こう側には、それぞれの想いを持って投稿してくれた1人のレシピ作者さんがいます。今回の企画を通して、改めて200万品という数字の重みを実感しました。

今回の企画に限らずクックパッドではユーザーの本質的欲求に応えるべく、データに対し視点を与え、問いを立てることで日々サービスの改善・運用活動を行なっています。

クックパッドでは、日々の料理を楽しみにするためのデザインを本気で考えたい仲間を募集中です!

クックパッド株式会社 採用情報

Docker を利用した Web アプリケーションのデプロイ

技術部の鈴木 (id:eagletmt) です。

クックパッドでは一部の Web アプリケーションサーバで Docker が使われており、今回はそのデプロイ方法について紹介します。

Docker で Web アプリケーションをデプロイするときには、まだまだベストプラクティスがある状況ではありません。 たとえば、どのように無停止でデプロイするか、どのようにコンテナと通信するかといった問題があります。 最初に Apache Mesos と Marathon などのツールを検証しましたが、クックパッドの環境において使いやすそうなものはなく、最終的に自前でデプロイのしくみを作ることにしました。 しかし Docker 周辺のツールは様々な新しいものが出てきている最中です。 今はまだベストなものが無いけれども、近いうちによりよいものが出てくるかもしれません。 そのため、できるだけ単純なしくみにしておくことで、後から別のしくみに移行しやすいようなものにしようと意識しました。

デプロイの流れ

Docker 単体では無停止でのデプロイを実現するのは難しいため、各ホストに nginx を立てて、デプロイ時に nginx の設定を更新する形にしています。 全体の構成は下図のようになっています。

デプロイは以下の流れで進みます。 既に v1 が稼動しており、新しく v2 をデプロイするとします。

  1. docker pull v2-image
  2. docker run --detach v2-image
  3. v2-container のヘルスチェックが通るまで待つ
  4. nginx の設定を v1-container から v2-container へ書き換えて nginx を reload
  5. コネクションのタイムアウト分だけ待つ
  6. docker stop v1-container

Docker を利用した Web アプリケーションのデプロイ方法としてはわりと一般的な方法かと思います。 とはいえ、この中でいくつか考えなければならない問題があります。具体的には、通信方法、設定値、権限の問題です。

通信方法

一つのホストに複数のコンテナを立てられるようになっており、リクエストをどのコンテナにプロキシするかはバーチャルホストで行っています。 nginx とコンテナ間の通信方法として、2つの方法を用意しています。

UNIX ドメインソケットを利用する

docker run のときに適当なディレクトリを --volume でマウントし、コンテナ内の Web アプリケーションはそのディレクトリに UNIX ドメインソケットを作成して listen します。 するとホスト側にもその UNIX ドメインソケットが見えるため、そこへプロキシする方法です。 コンテナのネットワークを分離でき、オーバーヘッドが小さく、ポートが被る問題に悩まなくてよいため、基本的にはこの方法を使うようにしてもらっています。

Docker の bridge networking を利用する

docker run--publish オプションを渡し、そこで指定したポートへとプロキシする方法です。 この方法は Docker イメージが公開されているソフトウェアをそのま利用するとき等に使います。 ホスト側のポート番号は、現在のホスト内で動いている Docker コンテナが使っているポート番号のうち最も大きなものに 1 を足したものを使うようにしています。 雑な割り当て方法ですが、これでもほとんどの場合でうまく動きます。

設定値

Docker コンテナに設定値を外から与えたい場合、環境変数が最も楽な方法です。 問題はその環境変数をどのように管理し、どのように与えるかです。

環境変数の管理には etcd を利用しています。 etcd にアプリケーション毎に環境変数の値を保存し、etcenv を使って環境変数を一時ファイルへ出力し、それを docker run --env-file で読み込ませる、というようにしています。

権限

Docker は root ユーザで動いており、Docker コンテナを操作するためには root 権限が必要です。 Docker デーモンの UNIX ドメインソケットを root 以外も書き込めるようにしたり、あるいは tcp で bind すれば root 権限がなくてもコンテナを操作できますが、 docker run --volume オプションを自由に使われてしまうとホスト側で root しか読み書きできないファイルもコンテナ内から閲覧できてしまう等の問題があります。

権限の問題を解決するため、デプロイに必要な操作をすべて行うデプロイスクリプトをホスト側に用意し、そのデプロイスクリプトに対してのみ全ての開発者に sudo を許可する、という形にしています。 またデプロイがホスト側で完結するので、何かのオペレーションでデプロイが必要になったときに、コマンド一つで再デプロイできるのも利点です。 開発者がデプロイするときは、Capistrano 経由でこのデプロイスクリプトを実行しています。

今後

このしくみでデプロイ自体はうまくできているものの、全体で見ると完全には自動化できていない部分があります。 たとえば現状では、デプロイ先のホストを決定するために EC2 のタグを使っており、このタグは運用者が設定しています。 つまり、どのアプリケーションをどのホストにデプロイするかを運用者が指定している状況です。 Docker によって得られたポータビリティを活かして、必要なメモリ量やコンテナ数などを設定すれば適当なホストにデプロイされるようになるとよさそうです。 さらに動的にコンテナ数を変えると自動的にスケールイン・スケールアウトしてほしいです。 その実現のために、Amazon EC2 Container Service がいいのか、swarm がいいのか、あるいは自前で何か作ったほうがいいのか、色々と検討中です。

まとめ

できるだけ単純なしくみにして後からもっとよいツールが出てきたときに対応しやすいようにしつつ、現状ではどのようにして Docker を利用した Web アプリケーションをデプロイしているか紹介しました。 Docker を利用した大まかなインフラ構成や利用しているミドルウェアの話はよく見ますが、Web アプリケーションのデプロイ方法についての具体的な話はあまり見ないので、一つの参考になればと思います。

Android開発でRxJavaをチームに導入した話

買物情報事業部の八木(@sys1yagi)です。

Android界隈でRxJavaが話題になっていますね。クックパッドアプリ(以後、「本体」と表現します)でも先日ついにRxJavaの導入を果たしました。本エントリではRxJavaをチームに導入する為に行ったいくつかの取り組みを紹介します。

目次

  • RxJava導入の失敗
  • どのような課題を解決するのか
  • 導入の為に機能を分解し、学習コストを考える
  • ブログを書く
  • 低コスト、低リスクに導入する
  • 勉強会を開く

RxJava導入の失敗

2014年11月にRxJavaの1.0.0がリリースされました。遂に実用段階かという事で個人的にあれこれ触り、本体に導入する機会を伺っていました。ある日、bug fixの為にRxJavaを使うと簡潔になるのではないかと思い気軽にPull Request(以後、PRとします)を送った所、「このタイミングで急に導入する意図はなにか?」「RxJavaでなければいけない理由は何か?」「きちんと学習しているメンバーが少ない中で導入してメンテは大丈夫か?」といった突っ込みが入り、PRからおよそ2時間ほどでRxJavaとお別れとなりました。

f:id:sys1yagi:20150415155611p:plain

レビューでの突っ込みはもっともで、明確な理由もないままチーム全体として学習が不十分なライブラリを持ち込むのはまずいですよね。メリットよりリスクの方が高そうです。

どのような課題を解決するのか

導入に失敗した私は反省し、まずはRxJavaによって本体開発のどのような課題を解決できそうなのかを洗い出しました。

No 課題 どのように解決できるか
1 非同期処理系の書き方に統一性がない。複数の非同期処理を合成する為のうまいやり方が定まっていない RxJavaをPromiseとして使う事で単一の機能を持つ非同期処理群を構成できる。それをドメインに合わせて合成するスタイルにすれば、AsyncTask内でCountDownLatchで待ち合わせるようなスタイルを駆逐できる
2 FragmentでgetActivity() == nullなどの状態チェック漏れによってクラッシュする問題 Fragmentのライフサイクルに合わせてunsbscribeする機構を作るかRxAndroid(RxJavaを使ってAndroid用に書かれたコンポーネント群です)等を使う事で状態チェックが不要になる

また、導入によって期待できる効果についても検討しました。

No 期待できる効果 理由
1 複雑なロジックを統一的な記述にできる Observable化した後の記述はRxJavaに沿うので統一的になる。また不自然な書き方や無理やり書いた場合などの検出がしやすい
2 メンテナビリティの向上 各処理をOperator単位で記述するので処理の意図が理解しやすくなる
3 アプリケーションアーキテクチャの改善 現在のアーキテクチャを改善していく際にRxJavaの思想が応用できるのではないか

「期待できる効果」の方はともかく、課題の解決に有効という事であれば学習コストを支払う価値は十分あるでしょう。なぜなら上記の2つの課題は本体開発においてずっと有効な手段が得られないままでいたからです。すぐにでも導入したい所ですが本当に課題の解決に有効かどうかについてどのように判断すればよいでしょうか。それはRxJavaを学習し適用しながら検証するしかないですね。この辺りが学習コストの大きいRxJavaのジレンマとなります。気軽に始められてやめたければ引き返せるような入り口を探す必要があります。

導入の為に機能を分解し、学習コストを考える

そこで、機能を分解して学習コストを抑えられるアプローチがないか探す事にしました。

RxJavaの機能の活用方法は概ね以下の様に分解できます。

  • List処理の抽象化・ストリーム化
  • Optional
  • Promise
  • Data Binding
  • Event Bus

これらのうち、学習コストを抑えられそうなのはList処理ではないかなと思います。Optionalはアプリケーションの設計に大きく関わってくるのでOptionalそのものの議論が必要になりますし、Promiseはスレッド操作周りを意識する必要がありいきなり取り組むにはコストが高そうです。Data BindingやEvent BusはSubjectを利用するので、Observableを知る前に触れるのはオススメできません。コードを書きながらObservableとOperatorの関係に触れつつ、低コスト・低リスクに学ぶにはList処理が一番よさそうです。もし「RxJavaはダメかもね」という判断になった時も、List操作での適用だけなら比較的簡単に戻せるでしょう。

ブログを書く

RxJava導入によって解決出来そうな課題と、低コストで学習できそうな方法が定まったので次にこれらをまとめたエントリを社内ブログに書きました。社内のブログなのでここには載せられませんが、概ね同じような内容を個人ブログ(RxJavaをコレクション操作ライブラリとして捉えれば学習コストと導入リスクを低減できるのではないか)に書いています。

社内ブログのエントリでは、本体開発においてRxJavaを導入する事で期待できそうな点について言及しつつ、Observableの概要と基本的な使い方をList操作をベースに解説しました。またエントリの末尾でRxJavaを導入するPRを送るという宣言もしました。

f:id:sys1yagi:20150415155644p:plain

低コスト、低リスクに導入する

社内ブログを周知したあと、ブログでの宣言通りList操作周りの実装を置き換えたPRを送りました。変更対象はListUtilsというリスト操作周りのユーティリティクラスで、T head(List<T>), List<T> tail(List<T>)などの実装をRxJavaを使って置き換えました。

これらの実装は元々あったものなので戻そうと思えばいつでも戻す事ができ、低リスクです。同時に特にメリットが無いのですが、「RxJavaを適用したコードをレビューする」という機会を作るのには最適です。

f:id:sys1yagi:20150415155714p:plain

このPRをマージした後、ビジネスロジック部分のList操作周りを中心にRxJavaを適用を進めていきました。その中で冗長な部分やわかりにくい部分をレビューで指摘してもらい改善していきました。

勉強会を開く

ある程度皆がRxJavaコードのレビューに慣れて来た段階で勉強会を開く事にしました。発表内容は初級から応用まで様々で、発表者の多くが発表の為にRxJavaの調査に力を入れていました。いわゆる勉強会駆動ってやつですね。当初は関係者6,7人で静かにやろうと考えていましたが、どんどん参加者が膨らみ最終的には20名くらいまでになりました。

f:id:sys1yagi:20150415160210p:plain

この勉強会によって、発表者はよりRxJavaの学習がより進み、参加者も基本的な部分の理解や学習のきっかけをつかめたように思います。

以下は勉強会の際に発表した資料です。公開されているものについて列挙します。

おわりに

勉強会後、本体開発にRxJavaが用いられる機会が増えました。現在ではAPI call周りのObservableを使ったPromise化が進んでいます。API callの並列化、直列化の書き方や、実行スレッドの設定をどこで書くかといった話や、可読性などについて日々レビューの中で議論しています。ゆくゆくはData BindingやEvent Bus等の適用についても検討されていくでしょう。また、解決できる課題として挙がった非同期処理やライフサイクルの問題に対しても徐々にRxJavaを適用しつつあります。今のところ大きな問題はでておらず期待通りの効果を得られています。

以上がAndroid開発でRxJavaをチームに導入する為に行った取り組みです。本エントリがRxJava導入の検討の役に立てば幸いです。

そんなクックパッドではユーザーの課題をどういう風にモバイルで解決するかを考え、実現するために情熱を注げる方を募集しています!興味がある方はぜひご応募ください。