Quantcast
Channel: クックパッド開発者ブログ
Viewing all 802 articles
Browse latest View live

仮説検証とサンプルサイズの基礎

$
0
0

パートナーアライアンス部 森田です。有料会員の獲得施策や、それに関わるサービス内動線の最適化を担当しています。

記事の対象

  • 仮説検証を通じて何かを改善をしたいと思っている人
  • 仮説検証の際に「どれくらいのデータをを集めたら良いか」分からない人

はじめに

仮説検証とは「仮説を立て、それを証明するためのデータを集め、真偽を確かめること」です。今回は仮説検証を行う際の手順と、その検証に必要なサンプルサイズの考え方を説明します。サンプルサイズの話のみ関心があるかたは、前半を飛ばし「サンプルサイズの決め方」を読んでください。

目次


仮説検証のつくりかた

1. 仮説をたてる

仮説をたてます。知りたいことを言い切る形にすれば仮説になります。

例えば「クックパッドの利用者が有料会員になるきっかけは文字ではなく美味しそうな料理の写真である」や「クックパッド利用者は自分の閲覧したレシピを後から見返したい」などです。ただし、後者は仮説検証のための仮説としては少し不十分です。その理由は次に説明します。

2. 施策/KPIを考える

仮説は根拠の程度はあれど真偽は不明です。その真偽を確かめるために何のデータを集める必要があるか考えます。ここが一番の難所です。

「ユーザは自分の閲覧したレシピを後から見返したい」という仮説の真偽はどのようにすれば分かるのでしょうか。施策としては単純に閲覧履歴の提供が思いつきます。しかしKPIの設定で詰まります。

このようなときは「知りたいことを」をもう少し具体的にします。たとえば割合を決めたり、何かと比較したりします。例えば「全ユーザの5%がレシピを後から見直したいと思っている」や「閲覧履歴を利用したユーザは利用しないユーザに比べて利用時間が20%伸びる」などです。悩んだ場合は次に述べる「仮説検証後のアクションを決める」と合わせて考えることをおすすめします。

3. 仮説検証後のアクションを決める

実際にデータを集める「前」に、データを集めた後のアクションを決めます。このステップにより先の試作/KPIが十分かわかります。「こういう結果ならばこうする」というのが決められないようであれば、施策/KPIは不十分なのでやりなおしです。

サービスの改善のように、指標となるKPIの意味に主観が含まれる場合はとくに意識します。事後の解釈ではサンクコストによるバイアスがかかり、冷静な判断が難しくなります。たとえば「閲覧履歴の利用者は全体の5%」という結果がどのような意味を持つか、検証後に考えるようではいけません。

4. 対象を決める

施策もKPIも決まりました。次にその対象者を決めます。

最終的なサービスへの全体影響を知りたい場合は細かいことを考えず全体を分割してABテストなどでデータを集めます。

一方で、何かしらの価値の有無を確認したい場合、まずは「このユーザの行動が変わらないのであれば、価値自体が無いのだろう」と考える理想的なユーザを施策の対象とすることをおすすめします。なぜならば全体で価値の有無を検証をしてしまうと影響が希釈されて計測が難しくなることが多いためです。

先の「2. 施策/KPIを考える」と、この「4.対象を決める」では、相関関係と因果関係の問題に十分注意してください。たとえば先の「閲覧履歴を利用したユーザは利用しないユーザに比べて利用時間が20%伸びる」という仮説の検証は実はなかなか難しく、もしそのようなデータが取れたとしても「利用したユーザはもともと利用時間が長いユーザ」である可能性は非常に高いです。この問題に関しては今回の説明の範疇外のためこれ以上触れませんが、自明な答えがないためサンプルサイズを計算するよりも厄介です。大変ですが誰もが苦労していると思いあきらめましょう。

5. サンプルサイズを計算する

施策もKPIも対象も決まりました。あとは必要なサンプルサイズを計算するだけです。タイムイズマネー、時間は有限で貴重です。PDCAを回すためにも行き当たりばったりの計測ではいけません。


サンプルサイズの決め方

ここからはサンプルサイズの決め方を説明します。

私が業務で行う仮説検証はほぼ全て、ABテストとこの先で述べる仮説検定*1という考えのもとで行っています*2。そして、その際のKPIとしては「入会する/しない」や「利用する/しない」のような、単純な1,0を好みます。そのためここからは

  • A「入会率10%の現ランディングページ」
  • B「入会率(以下CVR)15%を期待する新ランディングページ(以下LP)」

という2つのページを「BはAよりもCVRが5%高いだろう」という仮説のもとABテストし、その結果を判断するのに必要なサンプルサイズnを、信頼区間の考えをもとに説明したいと思います。

検証としてABテストをするつもりがない場合でも、この先の説明を理解することで適切なサンプルサイズを決める事ができるようになると思います。

答えを先に

ここから先の説明は少し長くなります。少しでも理解しやすいよう、これから求めようとするサンプルサイズnとは何かを、さきに文章で表現します。注意すべき点として、求めるサンプルサイズnは「差があること」を確認するためのサンプルサイズであり、「Xの差があること」を確認するためのサンプルサイズではないという点です*3

確率p1と確率p2にX以上の差があるならばn個のサンプルサイズで明確な差がでるだろう。もし差が出ないのであればX以上の差は無いのだろう*4

サンプルサイズを決める二つの要素

今回のABテストに必要なサンプルサイズは「二つの平均値*5」と「必要な確度」できまります。そのためまずは「二つの平均値」と「必要な確度」がそれぞれ何を意味するかを説明します。続いて予備知識として「二項分布」の概念を説明をした後、「必要な確度」を分解して現れる「αとβ」を説明し、最後にサンプルサイズを計算します。

「二つの平均値」とは何か

今回で言えば10%と15%です。サンプルサイズの計算にはこの二つの値が必要です。理由については少し先の「β」で説明します。

「必要な確度」とは何か

確度とは下記の二つの確からしさです。

(1) AとBに差がない

(2) AとBに差がある

その上で、(1)であるにもかかわらず「差がある」とすることを「第1種の誤り」といい、その確率を「α」といいます*6

そして(2)であるにもかかわらず「差がない」とすることを「第2種の誤り」といい、その確率を「β」といいます。言い換えるとαは偽陽性の確率であり、βは偽陰性の確率です。

つまりサンプルサイズを決める要素の一つの「確度」とはαとβの二つであり、「必要な確度」とはαとβをそれぞれどの程度まで許容するかを意味します。

「AとBに差がない」とは何か

実のところ「AとBに差がない」とは「BはAで無いとは言えない」を意味します。消極的かつ回りくどい表現ですが、これには意味があります。実際のところいくらデータを集めても「完全に差がない」と言い切れません。そのため何かの差を判断する際にはまず、「BはAと等しいだろう」という前提に立ち、データを集め、「Bのデータを見る限り、確率的にAと等しい可能性が十分にある」状態ならば、それをもって「差がない」と結論します。いいかえると「Bの結果がAであるにしては逸脱している」かどうかで差の有無を結論するわけです。そして「Bのデータを見る限り、確率的にAと等しい可能性が少ない」場合には「差がある」と結論します。何を持って「逸脱」とするかはこのあとの「α」で説明します。

このような手法を「仮説検定」と言います。ここより先に続く説明は、この「仮説検定」という考えに基づき差の有無を結論します。「仮説検定」についてより詳しく知りたい方は「仮説検定 - Wikipedia」を読んでください。

二項分布

この後に続く「αとβ」の説明のために、予備知識として二項分布という概念を説明をします。

堅苦しい説明をすると、二項分布とは

確率pで成功する行為をn回施行した場合の成功数の確率分布

です。

たとえば、裏表がともに50%の確率で出るコインを100回投げた場合、表が出る数はぴったり50になるとは限りません。むしろ出るほうが稀です。では何回が何%の確率で出現するのでしょうか。それを表したものが下の図であり、二項分布です。

f:id:idwtstwof:20160923221813p:plain

縦軸が確率、横軸が表の数になり、面積を合計すると1(=100%)になります。これをみると平均を50として、だいたいが40−60の範囲に収まることがわかります。

同じ考えで、「入会する/しない」 をそれぞれ1と0で表し、入会する確率をpとした場合も、二項分布で表すことができます。下の図は200人(n)がCVR10%(p)のLPに接触したさいに、最終的な入会数がどのようにばらつくかを示しています。実装の都合上曲線になっていますが、実際は上の図のように階段状に分布しています。また、スライドを移動することでnを変更できます。nと分布の関係を確認してみてください。なお、赤く塗りつぶされた部分の説明は後ほどするため、気にする必要はありません。

n:

αとβ

予備知識の勉強を終え、ここからはαとβについて説明します。復習として、ぞれぞれの意味はこのようなものでした。

  • αとは「真実として差がないにも関わらず、差があると結論する」偽陽性の確率
  • βとは「真実として差があるにも関わらず、差がないと結論する」偽陰性の確率
α(偽陽性の確率)

αを入会数に当てはめて考えます。

200人がCVR10%の現LPに接触した場合の入会数は20人を中心にばらつきます。そして面積の(だいたい)95%が12人から28人の間におさまることが二項分布により示されています。この12−28の領域を「95%信頼区間」と呼びます。これは二つ目の図の赤で塗りつぶされていない部分に相当します。それはつまり赤い部分の面積は5%ということになります。

Bの入会数を計測し、その結果がこの赤い部分に該当する場合は「差がある」と結論すると決めたならば、それは「αを5%に設定した」事を意味します。

もしもBの真実のCVRが期待してたような15%でなくAと同様の10%である場合、Bの計測後の入会数は5%の確率で赤い部分になるため、その時は「真実として差がないにも関わらず、差があると結論する」ことになり、偽陽性の確率=α=5%となります。

なお、決めたと表現したように、αを何%するかは恣意的です。赤い面積が小さくなるようにαを設定すれば偽陽性の確率は減りますが、その分、本当に差がある場合も「差がない」と結論しやすくなります。一般的にはαを1%、5%、10%とすることが多いです。

β(偽陰性の確率)

続いてβの説明をします

αは一つの分布で完結する話でした。というのはαは比較して考える問題ではないからです。それに対してβは本当は存在する差を見逃してしまう確率であり、二つの分布が必要です。今回でいえばCVR10%(A)とCVR15%(B)の二つです。

まず先程の図に、CVR15%の分布をオレンジの線で加えます。また、入会数を入会率に置き換えた分布も合わせて示します*7。灰色の部分についてはすぐに説明するので気にしないでください。とはいえ察しの良い方のために言いますと、この灰色の部分がβです。

n:

復習になりますがβとは「真実として差があるにもかかわらず、差がないと結論する」偽陰性の確率です。今回の例にあてはめると「Bの真実のCVRは15%にも関わらず、n人が接触した結果の入会数をみて、Aと差がないと誤って結論する」確率です。この時に、何をもって「Aと差がない」とするかは先に述べたαで決まります。つまりβはαによって変わります*8

入会件数分布の灰色の部分は「CVR10%時に95%の確率で発生しうる人数の、CVR15%時の発生割合」を示しています。その割合(=確率)は右の山全体を100%とした時に39%となり、この39%がβです。βが39%とはつまり「Bの真実のCVRが15%の場合、200人のサンプルサイズでは39%の確率で『BはA(10%)と差がない』と結論する」事を意味します*9

しかしながら本当に差があるにも関わらず、39%の確率で差がないとしてしまうのであれば「(2) AとBに差がある」を十分な確度で結論出来ているとは言えません。ではこのβを5%にまで減らすにはどのようにしたらいいでしょうか。もしまだ試していなければ最後の図のスライドを動かしてn(サンプルサイズ)の値を増やしてみてください。するとnの増加に伴い、入会確率分布の山が鋭角になり、結果として灰色の面積が減っていくことがわかります。これはつまりβを小さくするにはnを増やせば良いということです*10

また、図を見るとわかるように、βは二つの分布の山の距離(平均値の差)により変化します。もしもB(オレンジの線)が15%でなく30%であれば山の距離はもっと離れ、それにともないβの値も変化します。このことから「適切なサンプルサイズ」を求めるには「二つの平均値」を事前に決める必要があることがわかります。

ここまでの話を整理するとこのようになります。

  • α = 恣意的な確率
  • β = f(α, 二つの平均値, サンプルサイズ)

このことからも、適切なβにするには、サンプルサイズを変化させれば良いことがわかります。

サンプルサイズの計算

今までの話を元に、αとβがともに5%となるようなnを計算します。考え方としては、Aの右側2.5%とBの左側5%の数値が一致するようなnを求めます。そうすれば過不足のない、必要十分なサンプルサイズを求めることができます*11

計算には二項分布の正規分布への近似を利用しています*12。計算のためのPythonコードは以下のようになります。

import sympy as sp
p = 0.1
diff = 0.5
n_s, p_s, diff_s = sp.symbols("n_s p_s diff_s")
a_mean = n_s * p_s
a_var = (a_mean * (1 - p_s))
a_sd = sp.sqrt(a_var)
b_p = p_ + p_s * diff
b_mean = n_s * b_p
b_var = (b_mean*(1 - b_p))
b_sd = sp.sqrt(b_var)
a_right = (a_mean + 1.96 * a_sd)
b_left = (b_mean - 1.65 * b_sd)
param = [(p_s, p), (diff_s, diff)]
print(sp.solve((sp.Eq(a_right.subs(param), b_left.subs(param))), n_s)[1])
=> 554.289654454896

これにより、555人という結果が得られました。この人数を先程の図に設定し、いい塩梅であることを確認してみてください。

まとめ

前半は仮説検証のつくりかたを、後半はその際に必要なサンプルサイズの考えかたを説明しました。「何のために何を計測するか」は最も大切な、かつ難しい問題です。しかし、もしそれを決めることが出来たならば、その時は十分なサンプルサイズを伴い正しく判断しなければいけません。また途中で述べたとおり、相関関係と因果関係にも十分注意してください。

サンプルサイズの話に関しては出来る限り誤った事を書かないように注意したつもりですが、私自身の統計や検定に対する理解不足から、説明や表現に間違いがあるかもしれません。その際は指摘していただけると助かります。

参考書籍

私が仮説検証に取り組み始めた当初、確率と統計に関して全くの無知でした。数学の知識も乏しく、参考書を読んでもなかなか理解できません。そんな時に出会った「入門統計学」と「伝えるための心理統計」の二冊は、混乱しがちな所を一から丁寧に書かれており、非常に助かりました。もし統計を一から学ぼうという方におすすめします。また、今回の説明にない「平均値の検定」などを行う際のサンプルサイズに関しては、永田靖さんの「サンプルサイズの決め方」で学ぶ事ができます。

入門 統計学 −検定から多変量解析・実験計画法まで− - 栗原 伸一

https://www.amazon.co.jp/dp/4274068552/

伝えるための心理統計: 効果量・信頼区間・検定力 - 大久保街亜, 岡田謙介

https://www.amazon.co.jp/dp/4326250720/

サンプルサイズの決め方 - 永田 靖

https://www.amazon.co.jp/dp/4254126654/

便利サイト

Optimizely社がサンプルサイズを計算するページを無償で提供しています*13www.optimizely.com

*1:紛らわしくて申し訳ありませんが、仮説検証と仮説検定は別です

*2:少し嘘です。実のところは私がABテストを行う理由は検定のためというよりも既存の情報(今回で言えばAのCVR)の季節変動や他の施策の影響を観測することが主目的です。Aが今まで10%だからといって、Bの計測中も10%であり続けるとは限りません。そしてまた、解釈や判断は検定よりもその背景にある信頼区間をもとに行っています。しかしながら適切なサインプルサイズを説明するには仮説検定の考えに沿う方が容易なため、今回は「ABテストの結果を仮説検定する」文脈で説明します

*3:差の大きさが重要な場合は、差の有無に加えて信頼区間による解釈が必要となり、場合によってはより多くのサンプルを必要とします

*4:ただしX未満の差の有無については依然として情報不足です

*5:一般的には平均値の差を以て「効果量(Effect size)」として説明されます

*6:今回の説明では触れませんが、p値を利用した帰無仮説の棄却については基準率の誤りに注意してください。Alex Reinhartさんの「The p value and the base rate fallacy — Statistics Done Wrong」(西原史暁さんが日本語に翻訳されています「【翻訳】ダメな統計学 (5) p値と基準率の誤り|Colorless Green Ideas」)が非常にわかりやすく、混乱するたびに読み返させていただいています

*7:二項分布における確率の信頼区間は正規分布で近似して計算しています。ただし、サンプルサイズが少ない場合及び確率が極端に低い(高い)場合の信頼区間には注意が必要です。気になる方はWikipediaの「 Binomial proportion confidence interval - Wikipedia, the free encyclopedia」や、ynomuraさんの「 統計学復習メモ18: 少数のベルヌーイ試行による発生確率の区間推定 (Weblog on mebius.tokaichiba.jp)を読んでください。実務ではRなどを使うことをおすすめします

*8:αが小さいほどβは大きくなります

*9:1-β(1-0.39)=61%を検出力(power)といいます

*10:鋭角になるということは、サンプルサイズの増加により、得られる結果が本来の値に近づく確率が増す事を意味しており、これを「大数の法則 - Wikipedia」と言います

*11:「なぜAは2.5%でBは5%なのか。Aも5%じゃないのか」と不思議に思うかもしれませんが、これは両側検定という考えをもとにαを設定している事に関係します。今回の説明の範囲外のため、気になる方は「両側検定」や「片側検定」といったキーワードで調べてみてください

*12:二項分布の正規分布への近似には条件があります。詳しくは「 二項分布 - Wikipedia」「 ポアソン分布 - Wikipedia」を読んでください

*13:今回の計算結果とOptimizely社の計算結果は異なります。どのような式を利用しているかは分かりませんでした。もしどこかに書いてあるならば教えてください


管理下にないウェブサイトを適切に改修するために

$
0
0

管理下にないウェブサイトを適切に改修するために

株式会社トクバイの根岸です。みなさん、SSSランキングの調子はどうでしょうか。僕は今朝家を出たときには766位でした。一睡もしてません。

さて、今回もブラウザ拡張を作ったよという話です。"外部サイト向けバナー・リンク"という、我々の管理下にないウェブサイトに設置するウィジェットなどの管理・改修をどのように行っているのかについて書きました。

外部サイト向けバナー・リンクについて

トクバイが運営しているクックパッド特売情報は、今日のチラシが見られる、お買い物をするひとたちのためのサイトです。閲覧できるチラシ情報自体は、基本的にはスーパーの店員さんによって管理がされています。 では店員さんがなぜチラシをアップロードするのかというと、単純な観点でものを言えば、販促のためです。 それでは、なぜ、販促ができるのかというと、様々な認知・再訪チャネルがクックパッド特売情報には存在し、お買い物をするひとたちがクックパッド特売情報にやってきやすいようになっているからです。 このチャネルはいくつもありますが、その中のひとつが"外部サイト向けバナー・リンク"という機能になります。

技術的には、iframeでcookpad.comからサーブしているコンテンツをウィジェットやバナーとして参照可能なように提供したり、適切な実装がなされているリダイレクタ(特定の店舗にチラシがあったらチラシの1枚目・無かったら店舗ページにリダイレクトする、みたいな)へのリンクを提供したりということをしています。これらをスーパーの店員さんが自社のウェブサイトに貼ることで、お買い物をするひとたちにチラシ情報が知られて、販売が促進するという形ですね。

運用上の課題

管理下にないウェブサイトに提供しているコンテンツを適切に管理・改修するには、求める内容によって様々な手法があると思います。結論を先に書いてしまいますが、我々は人力で頑張って管理するという方法を選択していました。バナーを貼ってみたのですがという連絡を受けて、目で見て、電話をかけて、このようにしてはどうでしょうかと営業さんからスーパーの店員さんにお話するのが適切だと考えています。これは、技術的な利用のされ方に対する判定とは独立して、お買い物をするひとたちにとって設置されたバナー・リンクが合目的かどうかを人間が判断する必要があるからだと僕は考えています。

さて、最近になって子会社化によるブランド変更に伴い、バナー・リンクを全面的に刷新することになりました。しかしあらためて全面的にチェックし直そうとすると、実はこれまで行っていたチェックでは問題があることに気づきました。

端的に言うと、技術的な部分のチェックが非常に複雑で、これまでは不十分だったということになります。

6種類の提供されているバナー・リンクが適切なリソースから提供されているかをチェックするには、その内容を判定するために、目のあたりに非人間的な正規表現が実装されている必要があります。もちろんhtmlの基礎的な知識も必要です。またぱっと見て、公式が提供しているバナーなので大丈夫かと思いきや、じつは少しだけ改変した画像にリンクを貼っているという状況もありました。そのようなもののチェック作業を繰り返していると人間は疲弊します。

そしてもちろん、機械的にチェック可能な部分に人間のリソースを割いてしまうことは、とてももったいないことです。より重要な、お買い物をする人たちのための判定が、上手くできなくなってしまうからです。

この、人力でチェックして案内に至るというプロセスの機械的なチェックの部分を、システムで解決するというのが今回のエントリーのテーマになります。

解決

普通に考えると脳髄眼球と管理下にないウェブサイトの間にはコントローラブルなシステムが入る余地がないように見えますが、もちろん、あります。ブラウザ拡張です。

実を言うとこのチェックには専任の担当者が以前いて、僕は例のごとくdotjsで管理機能を提供していました。機能としては、◯✕の判定を各リンクに対して判定しサイト右上にずらっと並べるという簡易なものです。ところが、dotjs scriptでこのチェック機構を担当者に渡して運用するのは問題がありました。

  • 開発が終了しました
  • 証明書周りでどうしてもインストールが煩雑になるため、エンジニア以外にはチーム内運用が難しいです
  • インストール後の修正の適用が難しいです

結果として営業チームでは担当者の離職に伴いロストテクノロジー化、眼球正規表現の時代に戻ってしまいました。申し訳ありません。

先程も申し上げたブランド変更に伴って全外部サイトの再チェックが走るにあたり、dotjs scriptをChrome拡張に書き換えました。多少ヒアリングして○△✕の3段階に変更し、どのステータスのバナー・リンクがどこにあるのかわかるようにしました。レポジトリ管理して、rakeタスクで公開フローを進められるようにする、という感じでメンテナンス性の向上も図っています。

また、業務にChrome拡張を利用する上で結構重要なtipsとしてChrome拡張は限定公開可能という点があります。覚えておいて下さい。

そんな感じで、現任の主担当者にドキュメントと、動作チェックのためのテストサイトを共有して、チーム内で運営してくださいね、よろしくお願いします。とお渡ししました。

結果

パッと作ったブラウザ拡張によって、結果として、膨大なドメイン知識が必要な状態から、✕って表示されたら直すのを進める、という一発アクショナブルな状態になりました。✕でました、はい管理しておきます、ご連絡しました、直りました!という感じで、ステータスを機械的に形式化したことによってチーム内に共通言語ができ、恊働がうまくいくようになりました。バナーっぽく見えるからメンテ不要に見えて、実はお手製の画像、というようなパターンも非常に簡単に認識出来るようになりました。一つのページのバナー・リンクのステータスが全て右上に出ているので、OKになるとオールグリーン感が出てわりとうれしいと言う声も聞きました。

あとはもちろん、新規の実装などで判定の基準が変わることもありますが、そう言った更新もすぐに反映されるようになりました。リリースしてしばらくは積極的に営業チームから状況を引き出して都度改善を入れていましたが、即時反映されるのでとても楽でした。

Chrome拡張、便利ですね。コンテンツの質を担保するために人手が入ることは、時には必要で、適切なことですが、機械でやるべきことは機械でやりたいです。頑張っている営業チームと、店員さん、サービスを使ってくれているお買い物をするひとたちに感謝しながら、みんなに便利そうなものを開発して、いい感じになっていくのを眺めているのは、とてもたのしいなと毎日思っています。

iOSアプリの継続的デリバリーへの取り組みについての勉強会を開催しました

$
0
0

f:id:gigi-net:20160913200616j:plain

技術部モバイル基盤グループの三木(@)です。

去る9月13日、「Cookpad Tech Kitchen iOSアプリの継続的デリバリーへの取り組み」と題して、iOSエンジニア向けの技術交流イベントを行いました。

このイベントでは、iOS開発の中でも特に大規模アプリの開発フローや、品質改善を支えるための技術をテーマに、弊社のエンジニアから3つの発表をしました。

この記事では、その様子についてお伝えします。

クックパッドiOSアプリの品質管理のための取り組み

まず、技術部品質向上グループの松尾(@)から、クックパッドiOSアプリ開発体制について紹介しました。

この発表では

  • どのような組織構造を元にアプリ開発が行われているか
  • 月1のリリースサイクルを実現するためのリリースフロー
  • テストサイズとテスト戦略
  • UIテストを中心とした自動テスト手法

などについての概略を紹介しました。

サイズごとのテストについては以下の記事でも解説しています。

CDC Testing on iOS

次に、私、三木より、Consumer-Driven Contract Testing(CDC Testing)と呼ばれる、クライアントとサーバーサイドアプリケーション間の関係性に着目したテスト手法をiOSアプリに組み込んだ知見について発表しました。

CDC Testingは、クライアントからのAPIサーバーへのリクエストを記録し、継続的に検査するテスト手法です。 これにより、APIの変更による連携の崩れを検出することができます。

サーバーサイドアプリケーション間のCDC Testingについて、当技術ブログにいくつか記事がありますので、ご興味のある方はあわせてご覧ください。

また、今回紹介した、iOSのユニットテスト内でPactを用いてAPI仕様を作成するためのライブラリ、Phakchiも公開しています。

クックパッドiOSアプリの開発フロー

最後に、投稿開発部森川(@)から、サービス開発に携わるエンジニアの目線から、 開発フローの中でどのように品質を改善していくかという手法について発表しました。

コードレビューやKPT会など、開発速度を上げるために行っている工夫についていくつか紹介しています。

また、今回の発表でお伝えしきれなかった内容について、以下の記事もあわせてご覧ください

開発速度を上げるための Pull-Request のつくり方 - クックパッド開発者ブログ

懇親会

上記の発表の後、懇親会を行いました。

懇親会では、他の弊社iOSアプリエンジニアも参加しながら、参加者と様々な意見交換や談話をしました。

多くの皆様にご参加いただけたおかげで、より深掘りした話をしたり、各社のiOS開発の知見を共有するなど、活発な情報交換が行えたと思います。

また、今回はAppleになぞらえ、リンゴをテーマにした料理も振る舞われました

f:id:gigi-net:20160913202255j:plain

まとめ

いかがでしたでしょうか。

クックパッドでは、今後もiOS開発をテーマに同様のイベントを行っていきたいと考えています。 次回は11月頃の開催を予定しています。詳細が決まりましたら、当ブログなどでお知らせします。

また、クックパッドでは、一緒にiOSアプリ開発を行っていくエンジニアを募集しています。ご興味のある方は是非遊びにいらしてください。

Amazon Redshiftへ継続的にデータをロードする際に気をつけること

$
0
0

こんにちは、インフラ部データ基盤グループの小玉です。

データ基盤グループでは、Amazon Redshift(以下、Redshift)へ継続的にデータをロードする仕組みを、約半年に渡り構築・運用してきました。この記事では、その中で学んだことを共有させて頂きます。

弊社では情報系システムの一部に、AWSが提供するRedshiftという分散データベースを利用しています。情報系システムとは、データ分析を主な用途とするシステムのことです。なかでもRedshiftはSQLを使った大量データの高速な分析に最適化されているため、DWH(データウェアハウス)としての利用に適しています。

DWHの構築に必要なタスクとしては、データソースの特定、モデリング、データの抽出・変換・ロード(ETL)、クエリツールやBIツール導入、パフォーマンス・チューニング、メタデータの管理、バックアップ・リストアなど、がありますが、今回は「データの抽出・変換・ロード(ETL)」に関する話になります。

DWHへの一般的なデータロード

DWHへのデータのロードは、日ごと、週ごと、月ごとなどの決まったタイミングで、DWHユーザの分析クエリ(以下、ユーザクエリ)の流れない深夜から早朝に、バッチ処理でドカっと行うことが多いです。例えば、毎日早朝に最新のユーザマスタをロードしたり、月初に前月分の売上データをロードする、といった具合です。

このような処理が一般的である理由としては、「日中はユーザクエリのためにリソースを空けたい」、「業務が月末締めなのでそれに合わせたい」、「DWH用データベースは細かいINSERTが苦手なことが多い」といったものが挙げられます。

とはいえ、スピード命のこの業界では、「今朝デプロイした機能のログを午後には見たい」、「10分前のログを元に分析を実施し、その結果を本番システムに反映したい」といった要望も珍しくありません。

Redshiftへ継続的にデータをロードする仕組み

そこで弊社では、一部のログデータ(約1万レコード/秒)について、数分から数時間間隔でRedshiftへ継続的にロードする仕組みを構築し、運用を始めています。その仕組はさっくり以下の通りです。

  • アプリケーション(ウェブサーバ等)がFluentdへログを送る
  • FluendがS3にログをまとめて書き込む
  • 独自ロードシステムが...
    • S3上のログを読み込み、クレンジングや変換を行い、再びS3へ書き込む
    • S3上のクレンジング済ログをRedshiftへロード(COPY)する

このような仕組みを構築、運用する上で気をつけるべきこと(苦労したこと)の一つは、「ロード処理のリソース消費を最小限に抑えること」です。

なぜロード処理のリソース消費を最小限に抑える必要があるのか?

継続的にデータをロードするということは、日中、ユーザクエリが実行されている最中にデータのロードが行われるということです。この場合、ユーザクエリとロード処理は、CPUやI/Oなどのリソースを分け合って実行されることになります。よって、ユーザクエリへの影響を出来るだけ小さくするために、ロード処理のリソース消費は最小限に抑えるべきです。

それを踏まえ、我々は以下のような方針で上記のロードシステムを構築・運用しています。

  • ELTではなくETLを選択する
  • ロードシステムのバックエンドDBは分ける
  • 基本的なロードの最適化を怠らない

ELTではなくETLを選択する

DWHへデータをロードする工程は、一般的にETLと呼ばれています。ETLとは、ソースシステムからデータを抽出し(Extract)、加工・変換をした上で(Transform)・ロードする(Load)する処理のことです。ただし、最近ではELT、つまりデータベースへロードした後に加工・変換をする流れも多く見られるようになってきており、ETLとELTのどちらを選択するかはDWH構築におけるデザイン・チョイスの一つになっています。

見出しにもある通り、Redshiftへ継続的にデータをロードする場合は、ELTではなくETL、つまりロード前に加工・変換処理を実施する方式を検討するべきです。なぜならETLの場合は、加工・変換処理のためのINSERT/SELECTをRedshift上で実行する必要が無く、リソース消費を抑えることが出来るからです。

弊社では独自システムを使い、Redshiftの外でデータの加工・変換を済ませた後で、ロードしています。このような仕組みを構築することで、ロード処理に関わるリソース消費を最小限に抑えることができました。また、このレイヤーを導入することで、SQLでは難しい加工・変換処理を行うこともできるようになりました。

ロードシステムのバックエンドDBは分ける

読み書きの多いウェブアプリケーションのバックエンドDBとしてRedshiftを使う方は居ないと思います。しかし、上記の独自ロードシステムのような比較的小さなシステムであれば、そのバックエンドDBとしてRedshiftを使っても良いと思うかもしれません。

実は、我々がそうでしたが、今はRDS上のPostgresSQLに移行しています。移行の理由は、ロードシステムからの書き込みの負荷が、無視できないレベルだったためです。

Redshiftを含むDWH用途の分散データベースは、MySQLやPostgresSQLとは異なったデータアクセスパスを持ち、またロックやトランザクションも分散システム用のアルゴリズムで実装されています。そのため、ロードシステムに限らず、アプリケーションのバックエンドDBとして利用する場合は、その仕組みを十分に理解しておく必要があります。

基本的なロードの最適化を怠らない

上記の独自ロードシステムには、まだ実装出来ていない部分もありますが、公式ドキュメントに記載されているような、基本的なロードの最適化も忘れずに行う必要があります。なかでも重要なのは「COPYの対象ファイル数を、Redshiftのスライス数の倍数にすること」です。

スライスとはRedshiftのノード上で処理の実行を担うプロセスのことで、専有のディスク、CPU、メモリを割り当てられています。ロードを含む多くの処理はスライス単位で並列実行されるため、Redshiftの能力を最大限引き出すには、全てのスライスに均等に負荷をかけることがとても重要です。

例えば、システム全体で4スライスある場合、500MBのファイルを2つロードするより、250MBのファイルを4つロードするほうが高速です。なぜなら、全てのスライスがロード処理を実行することになり、リソースを無駄なく活用出来るからです。なおこの場合、ファイルを分割するだけでなく、そのサイズを出来るだけ揃えることも大切です。

まとめ

以上、この記事では、Redshiftに継続的にデータをロードする際に気をつけるべきこととして、「ロード処理のリソース消費を最小限に抑えること」を挙げ、弊社での取組みについて紹介をさせて頂きました。

また、クックパッドでは、一緒にデータ基盤を作っていただけるエンジニアを募集しています。ご興味のある方は是非遊びにいらしてください。

MySQLを1〜2時間でスケールアウトする

$
0
0

最近、Elastic BeanstalkやECSと戦っているSREチームの菅原です。 P5をやりたいのにPS3もPS4も持っていないので指をくわえて羨ましがっている毎日です。

この記事では、突然のアクセス増に備えるために、MySQLのスレーブを1〜2時間でスケールアウトできるようにした話を書きます。

MySQL on EC2

クックパッドは周知の通りAWSを利用していますが、主要なデーターベースについてはAmazon RDSではなくMySQL on EC2を使っています。 これは以下のような理由によるものです。

  • 歴史的な経緯: AWS移行当時、RDSが無かった。また、移行後もしばらくはTritonnを使っていたため、RDSを使うことができなかった
  • オンラインメンテナンスの実現: VPCルートテーブルを使った仮想IPとMHA for MySQLを使ってダウンタイムゼロのマスタDBの切り替えを実現しています。 RDSによるDNSベースの切り替えでは、どうしてもダウンタイムが発生してしまいます。
  • 複雑なレプリケーション構成: 主要DBは内外様々なサービスが利用しているため、レプリケーションの構成が複雑になっています。あるスレーブでは特定のテーブルをレプリケーションしていなかったり、またあるスレーブでは別のDBと同居していたりなど。RDSでこのような複雑な構成に対応することは難しいです

スケールアウトと暖機

TV放映など突発的なアクセス増があった場合、DBの負荷も増大するためスケールアウトが必要になることがあります。クックパッドの場合、サービスの特性としてリードのアクセスが圧倒的に多いため、DBをスケールアウトする場合には主にMySQLのスレーブを増やして、サービスに追加することになります。

DBのデータはインスタンスにアタッチされているEBSのスナップショットとして、定期的にバックアップが取られています。新規にスレーブを作成する場合は以下のような手順になります。

  1. MySQL on EC2のインスタンスを立てる
  2. スナップショットからEBSを復元してインスタンスにアタッチ
  3. レプリケーションが追いつくのを待つ

これで新しいスレーブができました。「早速サービスに入れよう」…とはいきません。 作ったばかりのMySQLはデータがメモリにキャッシュされていないため、クエリが投げられるとディスクへの読み書きが発生し、処理に時間がかかってしまいます。 またスナップショットから復元されたEBSは、最初にブロックにアクセスしたときにはS3からデータをダウンロードしてくるため、その後のアクセスよりもレイテンシが増加します。

このように暖機の行われていないスレーブをサービスに投入すると、サービスの応答速度の低下を招き、障害にもつながります。

EBSの暖機

gp2/1000GBのEBSをfioで暖機してみたところ、約19時間ほどかかりました。

さすがに時間がかかりすぎるので「サービスに即時投入できるようにレプリケーションし続ける小さいインスタンスを用意しておく」とか「バックアップ用のスレーブのEBSを使って新しいスレーブを作る」などいくつか対策を考えてみたのですが、同じ部の先輩が「I2インスタンスを使うとよいのでは?」とアドバイスをくれました。

I2インスタンスは大容量で高速なインスタンスストアが使えるインスタンスタイプです。 DBで使っているファイルの総容量はEBSのサイズよりも小さいので、EBSからインスタンスストにコピーする方がEBS全体を暖機するより処理時間は速くなります。 また、インスタンスストアはインスタンスに物理的にアタッチされるボリュームなので、暖機などしなくても高速に使えます。

なるほどと思って、総容量300〜400GBのDBのファイル群をcpを使って8並列でコピーしてみたところ、だいたい3時間ほどかかりました。EBSを暖機するよりはずっと短くなったのですが、それでもまだ時間がかかります。 並列でcpを走らせるとある程度はスループットが出るのですが、ファイルごとの処理は直列なためサイズの大きいファイルがあるとそれに引きずられてスループットが下がってしまいます。

そこで一つのファイルをチャンクに分けてddでコピーするツールを作り、それを使ってコピーしてみたところ、3時間かかっていた処理を1時間程度まで短縮することができました。

MySQLの暖機

MySQLのデータをメモリに乗せる作業は、以前はMySQL::WarmerをRubyにポートした自作ツールを作って、手作業で行っていました。

サーバ上でメモリ使用量を見ながらウォームアップツールで主要なテーブルの暖機を行い、キャッシュが飽和したらサービスに少し入れてみて、スロークエリの出たテーブルをまた暖機…と悪い意味で職人的な作業であり、大量のMySQLに対して行うには非常に手間がかかりました。

そこでMySQL 5.6のInnoDBバッファープールのプリロード機能を使って、暖機作業を高速化しました。 InnoDBバッファープールのプリロード機能は、稼働しているMySQLのバッファプールの状態をファイルに出力しそれを起動時に読み込むことで、暖機の手間を省いてくれるものです。 基本的には同じサーバ上のMySQLでの利用が想定されていると思うのですが、今回は稼働中のスレーブのバッファプールをダンプしてそれを新しく作ったスレーブに読み込ませることで、暖機作業を機械的に行えるようにしました。

具体的には以下のような手順になります。

  1. 稼働中のスレーブの1台で定期的にバッファプールのダンプを行うcronを設定する
  2. ダンプファイルは圧縮してS3に保存しておく
  3. 新規に作成したスレーブはS3から最新のダンプファイルをダウンロードして、起動時に読み込む

ディスク上のデータの物理的な配置が完全に一致しているかはやや疑問でもあるのですが、手作業よりも圧倒的に手間が少なく、また十分な効果も得られているのでこの方法をとっています。

スケールアウトの手順

現在、MySQLのスケールアウトが必要な場合、以下のような手順で行っています。

  1. Kumogataを使ったCloudFormationのテンプレートを準備しておく
  2. 環境変数でサーバ台数を指定できるようにしておき、CloudFormationで必要な台数のインスタンスを起動する
  3. CloudFormationによって、起動したインスタンスには最新のバックアップから作成されたEBSがアタッチされる
  4. インスタンスが起動するとcloud-initの起動時スクリプトによって以下の作業が行われる
    • MySQLのセットアップ
    • EBSからインスタンスストアへのデータのコピー
    • S3からバッファプールのダンプファイルをダウンロード
  5. MySQLが起動してレプリケーションの再開とバッファプールのロードを行う
  6. レプリケーションが追いついてバッファプールのロードが終わると、Slackに通知が来る
  7. 新しいスレーブを人間がサービスに追加する

まとめ

この仕組みを導入することで、MySQLのスケールアウトのために「TV放映の前日、前々日から準備を始めて」「19時間ちかくを暖機に費やして」「職人芸でサービスに追加」していた作業が、「TV放映の当日、1〜2時間前にコマンドをたたいて」「Slackに通知が来るまで放置して」「通知来たらおもむろにサービスイン」といった具合に、大幅に省力化・短時間化できました。

オペレーション作業に特有の「長い時間かかって手持ちぶさたな割に目を離すことができないから他の作業がしにくい時間」って、ほんと嫌ですよね… 今後もそんな作業があればさっさと自動化して、QoLの向上を図っていきたいものです。

ペンとふせんで!スマホUIのアイデアプロトタイピング

$
0
0

f:id:transit_kix:20161011214557p:plain検索事業部のデザイナー倉光です。

今回は、開発現場でアイデア発散フェーズにやっていることの一例を紹介したいと思います。UIデザインの手法として比較的知名度は高く、デザイナー以外でも学びたいという要望も多い「ペーパープロトタイピング」についてです。

前提として

プロトタイピングにはフェーズと目的に応じて様々な手法がありますが、今回は「小規模チームでアイデアをぽんぽん出し、伝え合うためのプロトタイピング」の話です。ユーザーに実際に評価してもらうためのプロトタイプの作り方についてはこの記事では割愛させていただきます。

また非デザイナーの方は「いやいや、デザイナーじゃないと画面なんてうまく書けないよ…」と躊躇してしまうかもしれませんが、本記事では社内のディレクター/エンジニア/インターン生が書いた成果物も掲載していますので、そちらも参考になると思います。


目次


1.ペーパープロトタイピング(紙実装)とは

「プロダクトをこんなかたちにしたい」という画面案を紙に書き、それを成果物として利用/評価/改善をすることを指します。私は個人的に「紙実装」と呼んでいます。コードを用いた本実装との対比ですが、小さく価値検証するための実装方法の一つであるという捉え方をしています。

どんな時に有効?

主に開発初期の工程で使われます。以下のような場面です。

  • アプリの新機能について構想していて、自分の頭の中にある画面の関係性を整理したい時
  • 複数人で議論していて、その場でざっくりと画面案の方針を決めてしまいたい時
  • チームメンバーへ、自分のアイデアを伝える時

なぜこれをやるか?

正解のわからないサービス開発においては、初期に1つでも多くのアイデアを発散させてからデザイン→実装へと収束していくことが最も近道であると考えています。また、アイデア発散の際には早く具現化することで「果たして実現性のありそうなアイデアかどうか」の判断ができるからです。


2.実施方法

f:id:transit_kix:20161011214545p:plain

まずはこちらを準備しましょう。手元で揃う道具で構いません。(ちなみに実際に使っている道具名はこちらです*1。)スマートフォンは紙を撮影したり、プロトタイピングツールを動作させるために利用します。

2-1.かんがえる

画面の設計図を書くことに関しては絵の上手い下手はありません。画力に自信がないあなたも、まずは手書きのスケッチをオススメします。

PCを開く前に、まずはペンを持とう

ツールの習熟度に振り回されずに、アイデアを生み出すことができます。また、いきなりツールを触ることの弊害として、そのツールの機能で出来ることしか試さなくなってしまうことが挙げられます。サービスに載せたい情報が曖昧なままにツールを触りだすと、早くキーカラーを決めたくなったり、それっぽい高品質画像を素材サイトから探す方に熱心になってしまうという罠にも気をつけなければいけません。

頭の中の整理をする

そもそも、体験を考慮した遷移設計ってどう考えたらいいの?…そんな時は画面を書く前に頭の中を整理します。もし作る画面が1画面のみだったとしてでも、あなたがもっとユーザーのことを考えてみたいと思ったならばユーザーがその日アプリを起動するきっかけと、最後に何をしたら満足してアプリを閉じることになるか…その一連の流れをコピー用紙に矢印や図を使ってスケッチしてみましょう。

f:id:transit_kix:20161011215346p:plain [例]アプリ「みんなのお弁当」のSNSシェアページをデザインする前に書いたもの

書き出してみると、デザインするのはSNSシェアページの一画面のみであっても、シェア先で表示するカードのデザインや、それを見た人がどういった経路を辿るのかというのがだんだん見えてきますね。もう少し厳密に体験を定義したい場合は、ユーザーストーリーを書き出します。(詳しくは 「インターンシップ「サービス開発演習」の舞台裏」の回にて)

2-2.描く

長方形のふせんを用意します。縦向きにしましょう。 スマホっぽい比率の紙になりました。

f:id:transit_kix:20161011214557p:plain

今から作りたいと思っている画面を書いてみましょう。スクロールするような長い画面は、2枚目、3枚目のふせんを更に下につなげていきます。

慣れないうちはペンを使い分けなくても良いです。使い分けたい場合は、サインペン(太)はボタンの境界線や目立たせたい見出しに、サインペン(細)は注釈や説明文などに利用します。

f:id:transit_kix:20161011214638p:plainユーザーが実際に目にするであろうテキストを配置しよう

本当に有効なアイデアかどうかを検証するには可能な限り完成予想に忠実にしてあげましょう。「ここにテキストが入ります」「ああああ」「hogehoge」などの曖昧なテキストはやめましょう。

オーバーレイするUIパーツは別のふせんで

サイドメニューや部分スクロールするUI、ダイアログは違う色の小さなふせんで上から貼ると、いろんな画面で使い回し可能です。後述する「手動実演」の時にも、手軽に画面状態を変えられます。

どんどん捨てる

いくつかの案で悩んでいたら、上から別案を張り付けることもできます。書き損じたら、使える部分だけ残してハサミで切り取っても可です。とにかく躊躇せず、切り刻んでください。

実際に画面に置く部品の書き方についてはfladdictさんの 「ペーパープロトタイピング入門 – 第4回 見やすいペーパープロトの描き方」がわかりやすくまとまっていると思います。

2-3.つなぐ

ひとまず1画面ができたら、その画面の中にあるボタンを押したら進む次の画面など、ユーザーストーリーに登場する画面の集合体を作りましょう。作業中に考慮漏れに気づき、新しい画面が増えていくこともあります。プロダクトの全貌が明らかになっていくイメージです。

ちなみにふせんを壁に貼りながら、関係性に沿って配置&前後の画面を線で繋いでいくと、簡易遷移図が出来上がります。

f:id:transit_kix:20161011215349p:plain

2-4.さわってみる

出来上がった画面を、今度はユーザーの行動に沿って動かしてみましょう。

手動実演

上の図は、iOSウィジェット機能を活かしたアイデアを手動で動かしている動画です。「ここを押したら、画面がシュッと移動して、こうなります」…このように、手でふせんを入れ替えながら実演してみせるのも良いでしょう。施策相談中にそのままプロトタイピングが始まっちゃう時には、割と頻繁に見かける光景です。

f:id:transit_kix:20161011214650p:plain

実機にプロトタイプを入れて、ユーザーと同じ環境下で触ってみる

出来上がった画面は撮影して各種プロトタイピングツール(Prott/Invision/Flinto for macなど)に転送しリンクを設定すれば、「操作することのできる試作品」にすることができます。昼間に作ったプロトタイプを帰宅途中のスーパーで開き、食材を探しながら歩き回ってみたりしたこともありましたが、デスクにいる時と全く受ける印象が違います。

実際に操作ができるようになることで格段にアイデアの解像度はあがり、様々な課題が可視化されます。実際「最高かよ…」と思ってたアイデアが具現化したら全然イマイチだったみたいなことは日常茶飯事なので「紙実装のうちにわかって良かったですね!」ということで次の策を打ちましょう。


3.現場でどう活かすか

使われ方

アイデアのプレゼン/ミーティングの成果物/仕様概要/アイデアの自己検証…として利用されます。ちなみに紙実装はそれ単体の出来で評価されることはありません。どういったユーザーストーリーでその画面を操作していくのかを同時に定義しています。

周囲への共有方法

f:id:transit_kix:20161011215912p:plain最終的にはGitHub上の関連Issueに、情報を貼りつけます(プロトタイピングツールのシェアURLも)。

f:id:transit_kix:20161011224717g:plain紙実装である程度の方向性を確認した後、これを元にデザイナーがグラフィックやトランジションを考慮した設計に移行していきます。

紙の保管方法

f:id:transit_kix:20161011214620p:plainふせん一式は重ねることでコンパクトに保存することができます。またA4のコピー用紙に並べて貼ることで、一覧性を保ったまま保存することもできます。ただしあくまで中間成果物なので、Issueに貼った後は長期間実物を保管することはあまりありません。


4.開発に取り入れた影響

f:id:transit_kix:20161011214654p:plain

いろんなUXデザインの手法をクックパッドの現場流にアレンジして個人的にやっていた紙実装ですが、数ヶ月前に社内ブログで紹介したところディレクターを中心に反響があり、その後実際にどんな変化があったのかについて触れます。

(余談ですが画面プロトタイピングにおけるふせんの活用については、海外では電子書籍も出ています。少し利用方法は異なりますが( *2 )。)

再構成がしやすいので、開発へ移行するまでのスピードがより早く

画面同士の関係性を保ちながら壁に貼れたり、その場で別案を上から重ねたり…といった取り回しがしやすいことが何よりアイデアを考えることに集中できます。短時間で大量のアイデアを具現化し、その場で「それいいね!」と議論しながらアイデアを拡張する際にはおすすめです。

スマートフォンのUIアイデアを書き込む用紙としては、ペーパープロトタイピングのための専用ノートパッドや印刷して使えるテンプレートPDFなどがあります。社内デザイナーも各自が様々なツールを試していましたが、アイデア発散フェーズではこの方法が最も作業自由度が高く、コラボレーション性があるという声がありました。

デバイスのサイズ感を意識したアイデアが出せるようになった

スマホに似たサイズの紙にアイデアを書くことで、自然とデバイス実寸が考慮されたアイデアを考えやすくなりました。各種PCに入っているソフトウェアの図形ツールを駆使した画面案を見かけることがなくなった点は個人的に嬉しく思っています。(頑張って作ってくれたところ申し訳ないなとも思うのですが…これらは往々にしてディスプレイ上で拡大された状態で作成されているので、入りきれない量のテキストが極小サイズで画面に配置されていたりするのが悩みの種でした)。

詳細の説明なしで、やりたいことが伝わる

これは アラビア語版クックパッドを開発しているレバノン拠点スタッフ(非デザイナー)が作ったプロトタイプの一画面です。レシピ検索結果画面のアイデアスケッチを表しています。残念ながら私はアラビア語は全く読めないのですが、この画面を含む一連のプロトタイプを触ってみることで、彼がどんな機能を作ろうとしているのかが自然と理解できました。リモート開発や言語の壁がある環境においても、いち早く動くものを見せることは意思疎通のために有効だと考えています。


まとめ

最後に、紙実装のメリット / デメリットについてまとめておきます。 f:id:transit_kix:20161011214707p:plain

注意点は以下です。

サイズ感については、必ず実機で検証すること

手に入りやすいふせんですが、厳密に言うとスマートフォンとはサイズ/アスペクト比が異なります。ボタンに実機で問題ないタップ領域が確保されているか?などは、別途本番デザインの際に検証してください。

実装しちゃったほうが早いこともある

何を確かめるためにプロトタイピングするかによっては、実装に着手したほうが早く良い知見が得られることもあります。*3「手段が目的化する」ことは本末転倒ですので、場合に応じて使い分けてください。

…いかがでしたでしょうか?議論を加速し、皆でサービスの在るべき形を素早く生み出していくための1つの手法が紙実装です。デザイナーではない人が、または開発者ではない社内のスタッフが、もっと気軽に自分たちのサービスについてデザインを議論できるようになることで、よりサービスの開発速度を加速させていきたいと思います。

この他のデザイナーの取り組み事例は、こちらのデザイン記事一覧にてご覧ください。 クックパッドでは「プロダクトを率先して、具現化していきたい!」というデザイナーを募集中です。

*1:私は「アスクル 貼ってはがせるオフィスのノート 75x127mm / 38x 50 mm」「パイロット フリクションボールペン(黒 / 0.5)」「ぺんてる水性サインペン(黒)」を使用しています

*2:「The $1 Prototype: Lean Mobile UX Design and Rapid Innovation for Material Design, iOS8, and RWD」活用方法などの詳細は異なりますが、ふせんをスマートフォンに見立てるという点では同じです。洋書ですが、 「日本語解説版」はアジャイルUCD研究会が公開しています。

*3:例えば、実際のコンテンツ情報(240万品のレシピデータ、ユーザープロフィール、その人が興味のあるキーワードなど)をUIに反映しないと本当に良い体験が提案されるかどうかがわからない画面は、まずはstaging環境でHTMLプロトタイプを作成し、検討した上でネイティヴ化のためのUIデザイン/API準備を行なっています。

"使える"プロトタイプ主導の開発プロセス

$
0
0

検索事業部の須藤です。 クックパッドの検索周りのサービス開発を担当しています。

はじめに

最近ではプロトタイピングツールも充実し、コードを書かなくとも動的なモックアップが作れるようになるなど、思いついたアイデアをより早く、より最終的なアウトプットに近い形でメンバーに共有することができるようになったと感じています。

また、実際にコードを書いてユーザーに公開するための効率的な手法や、公開後の検証方法についても様々なツールや知見が共有されており、より精度の高い定量評価ができるようにもなってきたかと思います。

一方、これらの効率化が進んでも、実際に打った施策の数を増やせたか、最終的にサービスインできたプロダクトの数が増えたかというと、そこまで実感がありません。

その理由のひとつは、思いついたアイデアを具体化して作り始めるまでの初期段階と、実際にそのプロダクトを(テスト目的であっても)公開に耐えうる完成度に持っていくまでの間には、相変わらず高いハードルがあるからだと思います。

f:id:sudokohey:20161017095937p:plain

この「間」の部分にある、発想の仕方や開発の進め方こそが所謂「サービス開発」の勘所だと思うのですが、この段階でのプロダクトの作り込みに関しての具体的な知見は、あまり目にすることがありません。

もっとも、提供するサービスの種類や成熟度、作っている組織の規模やカルチャーによって、その扱いは千差万別で、なかなか共有可能なメソッドにならないことがその理由だと思うのですが、今回は敢えてその部分にスポットを当てて、プロダクトの完成度を効率良く高めるために実践している、"使える"プロトタイプ主導の開発プロセスについて書いてみようと思います。

本当にイケてるアイデアを見極める

実際の生活の中でユーザーと同じ立場になって使用感を確かめることができないプロトタイプは、その形態がどうであれ「良さそう」以上の評価はできません。 また、経験上、その段階での良さそうなアイデアの殆どは、作り込んでいく過程のどこかで「ダメそう」に変わります。

効率良くプロダクトの完成度を高めるためには、思いついたアイデアが、本腰を入れて作り込んでいくに値するものかどうかを、できるだけ早期に見極める必要があります。

実際に"使える"プロトタイプを作る

そのためには、プロトタイプと言えども、実際に「使える」ものをつくることが大事です。 これは、webサービスであれば、一般に公開されている本サイトと同じ体験ができるものをつくるということであり、例えば、

  • 思いついたキーワードで自由に検索ができる
  • リアルタイムに様々な情報が更新される
  • 思考が途切れずに操作するに足りる実行速度である

といった要件を満たすものでなければ、正確な見極めができません。*1

1日で作れるものをつくる

そこまでのものをつくるとなると、企画を出して、仕様を詰めて、デザイナーやエンジニアを集めてがっつり開発するのと変わらないのでは?と思うかもしれません。 また、プロトタイプである以上、プロダクションでの公開を前提とした開発と同等に時間がかかっていては本末転倒です。

そこで、1日で作れるものをつくるという制約を自分の中に持つようにしています。

アイデア段階での構想が大きなプロダクトであっても、体験を構成する要素を機能単位に分解し、その中から、そのコンセプトを成立させるために欠かせない機能や遷移を抜き出します。 まずはその部分にフォーカスし、それがユーザーのサービス内での行動動線の中でどのように機能するのかを確かめるために必要な分だけの実装をします。

スピード最優先でつくる

実装と言っても、書いているのは簡単なコードばかりです。 例えば「食べ方提案」と呼んでいる以下のようなUIを持った機能を例に挙げると、まず簡単なスクリプトを書いて、食べ方のデータを用意します。本来であれば、DBにテーブルを作ってそのデータをインポートして、さらにデータの更新方法を準備して...と進むはずですが、それをやっている時間すら惜しいので、UIに合った使いやすい形にデータを整形して出力し、ハードコードして済ませます。

コンセプトを確かめる程度であればこれで十分です。

f:id:sudokohey:20161017100328p:plain:w300
※開発中のプロトタイプの画面

また、ぱっと見は完成形と変わらない新機能が日々追加されていく環境を用意することは、いくつかの面でメリットがあります。

ちなみにアプリの場合は?

chankoのような機能もなく、プロダクションのAPIに手を入れる必要があること、また、デプロイゲートなどを使った確認のコストも高いと感じることから、現時点ではアプリ向けの機能のプロトタイピングもweb上で行うようにしています(web版で公開予定のないものであっても)。

f:id:sudokohey:20161017100648p:plain:w300f:id:sudokohey:20161017100704p:plain:w300
※chanokoを利用してweb上に実装したアプリ向けのプロトタイプの画面。(web版での公開予定は今所無い)

もちろんアプリならではの表現などは再現し辛いため、コアになる機能や遷移をまずweb上に用意し、細かなインタラクションについてはデザイナーが部分的に動的なモックを作るなどし、並行して検討します。

初期段階で必要以上に議論しない

ちょっと話が逸れますが、クックパッドでは「動くものを前に議論する」という言葉が時々出てきます。 これは裏返すと、実際に「使える」状態にないものに対してあれこれ言ってもあまり意味がないという事です。 ですので、余計なインプットを入れずに、使えるプロトタイプとして最初にデプロイするまでは、直感を信じて作り切るようにします。

ちなみに、ここで全くイケてないものを作ってしまった場合(結構あります...)のダメージを最小化するためにも、1日でつくれるものを意識しています。

基盤技術になりそうなものは、別ラインで開発する

さて、上記のようなプロトタイピングを繰り返していると、

  • あるレシピからその料理名を簡単に抽出したい
  • あるレシピの主材料が何かを判定したい

といったような具体的な要件が明確になってくる事があります。

例えば「簡単!うまうまゴマいんげん」というレシピがあったときに、人間が見ればこれが「いんげんの胡麻和え」であることは一目瞭然なのですが、機械的に正確にこれを判定しようとすると結構難しかったりするので、その分野の解決が得意なエンジニアに別ラインで開発を依頼します。

こういった技術開発を依頼される側からすると、開発する技術の具体的な使われ方がわからないため、正解の状態をイメージできなかったり、どの程度の精度のものを仕上げれば良いのかが分からず、仕様を定めるのに時間がかかるケースがあると思うのですが、使えるプロトタイプがそこにあるため、比較的スムーズに話がすすみます。

また、仕様通りに作るよりも、プロダクトに貢献できるイメージが持てると、気持ち的にだいぶ違います。先程いくつかのメリットと書きましたが、その一つがこれに当たります。

いつのまにかまともなコードに!?

捨てる前提のコードでプルリクエストを送っていると、「それ、他のところでも使えそうそうだからメソッド用意しといた」みたいな感じで、部分的な開発を肩代わりしてくれる神様のようなエンジニアが出てきたりします。依頼する前に解決するという奇跡のコラボレーションです。

これを繰り返していくと、いつの間にかハリボテ(?)のコードが、まともなコードになってたりします。これもメリットです。

実際に使ってみて評価する

さて、これが最も大事なことですが、使えるプロトタイプが用意できたら、これを実際の生活の中で使って評価します。平日に料理をするのはなかなか難しいのですが、毎日小さく開発していくと、週末には5つのアップデートが載っています。これを週末に使い倒します。

作ったプロトタイプは、次にあげるような不意に訪れるピンチを救ってくれるものになっているでしょうか?「良さそう」だと思ったアイデアが単なる妄想に過ぎなかった場合は、ポケットの中のプロトタイプがそれを教えてくれます。

  • 小松菜で何か作ろうと思って買いに行ったら、台風による日照不足の影響で、1束250円もした。隣には白菜が128円で積まれていた。さてどうする?*2

  • 29の日(肉の日)の特売目当てでお肉を買いに行ったら、時間が遅かったためにほとんど売り切れていた。残っていたのは普段あまり使わない手羽先だけだった。さてどうする?

  • 夕飯のおかずは冷凍してある鶏肉で...と考えていたが、いざ料理を始めると、冷凍庫の中には、あるはずの鶏肉が無かった(!)。かわりにすっかりその存在を忘れていた、鱈の切り身が見つかった。さてどうする?

このようなイレギュラーが現実では沢山おこります。そしてこれらは、机の前で開発しているときには想像できません。誰かが事前に用意したペルソナにはもっとざっくりとした、ありがちなシナリオしか設定されていません。

さてどうする?

これを解決するために、"使える"プロトタイプ主導の開発プロセスを実践しています。

誰がこの役割を担うのか?

このサイクルを一人で回すには、様々なスキルが要求されます。

  • 周辺にある実用段階の技術を幅広く理解する
  • それフル活用して、現時点で実現可能かつ斬新な企画をいくつもひねり出す
  • コンセプトを体現するシンプルなインターフェースをデザインする
  • 実際にコードを書いて、1歩先の姿を作り続ける

こう書くととてもムズカシイことのように見えますが、クックパッドには、これを実践するデザイナーやエンジニアがちらほらいます。新卒社員の中にもいたりします。

もしこの記事を読んで「自分にぴったりだ!」と感じた方がいらっしゃいましたら、ぜひこの 役割をお願いしたいと思っておりますので、ご応募お待ちしております!

*1:クックパッドでは、行単位でレプリケーションされた開発用のデータベースに接続して開発できるようになっていること、また、chankoを利用してプロダクションのコードに影響のない形でプロトタイプをそのままプロダクションに載せることができるようになっているため、この要件を満たすとは比較的容易です。

*2:先々週にあった自分の実体験です

来年も Cookpad TechConf やります

$
0
0

こんにちは! @yoshioriです。

今年年明けに Cookpad TechConf 2016という、クックパッドのエンジニア、デザイナーがサービスづくりの過程で得た技術的知見や経験をみなさんに発表させていただくイベントを開催させていただきました。
こういったイベントは続けてナンボ!!! ってことで来年もやります!!!
前回は会場キャパシティ 250 人の所、1000 人以上のお申込みを頂きました。
今回はその反省も含めレイアウトを変更し会場キャパシティ 350 人用意しました!!!

今年もエンジニア組織的な話から海外展開で得た知見、基盤技術、サービス開発、デザイン、機械学習など多種多様なコンテンツを用意していますので、知っている分野は更に知見が深まり、知らない分野のこともザックリ知れる。そんな一日にしたいと思っています。

一同お待ちしています! みなさま奮ってご参加ください!

Cookpad TechConf 2017

タイムテーブル

タイトル発表者
Cookpad under a microscope 成田 一生
Deliver apps to global audience 滝口 健太郎
Building infrastructure for our global service sorah
クックパッドで取り組めるデザイン(仮) 若月 啓聡
モバイルアプリのA/Bテスト基盤 加藤 龍
チームでサービス開発をするための取り組み 丸山 亮
サービスのプロトタイピング時に心がけていること(仮) 須藤 耕平
フェージングのすゝめ 〜サービスの大規模リニューアルの話〜(仮) 京和 崇行
快適なサービス開発を支える技術 国分 崇志
Real World Machine Learning 染谷 悠一郎
行動ログでプロダクトを改善するには 兼山 元太
Cookpad awakens 庄司 嘉織

お申込み

お申込みはコチラから


非SPAなサービスにReactを導入する

$
0
0

投稿開発部の外村(@hokaccha)です。今回はReactについてのお話です。

ReactとSPA

最近JavaScriptやそれを取り巻くフレームワークなどの話題では、サーバ側はAPIのみを提供し、View(HTML)は全てJavaScriptで描画するような、いわゆるシングルページアプリケーション(以下SPA)についてよく語られます。

一方で、SPAを構築するにはコストがかかることも事実で、特にフロントエンドエンジニアが多くない環境では、従来通りサーバーサイドでViewを書きつつ動的な部分だけJavaScriptで処理するというアーキテクチャのほうが現実的な場合も往々にしてあります。

今回はこのような、サーバー側でHTMLを生成し、一部の動的な部分だけをReactで書くためのTipsを紹介します。

なお、基本的にサーバーサイドはRails前提ですが、RailsにおけるReactの開発環境の構築方法などについて以下の記事や資料を参照ください。

コンポーネントの例

例えばブログ記事に「いいね」が押せる機能を考えてみましょう。機能としては

  • いいねできる
  • いいねが解除できる
  • 自分がいいねしているかどうかわかる
  • いいねしているユーザー数が見れる
  • いいねユーザーの一覧がポップアップで見れる
  • ユーザー一覧は20件ごとに読み込む
  • いいね押したときにログインしてなければログインの誘導ポップアップが出る

このように、小さいコンポーネントではありますが、複数の状態や機能があり、いいねの付け外しやユーザー一覧の取得はAjaxで行う必要があります。

テンプレートをhamlやerbで書いてjQueryでDOM操作をして実現することもできそうですが、このような機能をjQueryだけでメンテナブルなコードを書くのは簡単ではないと思っています。一方Reactは宣言的で見通しのよいコードでコンポーネントを記述でき、Viewの機能のみを提供するという単機能なライブラリのため、こういった部分的に利用するケースでも導入しやいです*1

また私自身、これと同じような機能をjQueryとReactの両方で実装した経験がありますが、例えこのぐらい小さい機能であってもReactのほうが楽に実装できると感じました。サーバー側のテンプレート言語とReact側のJSXとでテンプレートの言語が分かれてしまうというデメリットはありますが、個人的にはそこを差し引いてもメリットのほうが大きいと思っています。

react-railsを使った自動マウント

このようにReactを動的なコンポーネントだけに使っていくという手法の場合、面倒なのがReactコンポーネントのマウントです。SPAの場合は基本的にルートコンポーネント一つをマウントすれば済みますが、こういった構成の場合は1ページに複数のコンポーネントをマウントするケースが多くなります。

例えばブログ記事のページで、いいねとコメントの2つの動的なコンポーネントがあるとします。まずはテンプレートを次のようにして

<h1><%= @entry.title %></h1><%= @entry.body %><divclass="like-component"></div><divclass="comment-component"></div>

JavaScript側で対象のDOM要素に作成したReactコンポーネントをマウントします。

document.addEventListener('DOMContentLoaded', () => {let like = document.querySelector('like-component');
  let comment = document.querySelector('comment-component');

  ReactDOM.render(React.createElement(LikeComponent), like);
  ReactDOM.render(React.createElement(CommentComponent), comment);
});

2つくらいであればこれでもいいですが、コンポーネントを作る度にこのようなコードを書かないといけないのは面倒です。また、コンポーネントの初期値としてpropsを与えたいというケースも出てくるでしょう。

そこでRailsの場合はreact-railsを使うのがオススメです。react-railsにはサーバーサイドレンダリングなどの興味深い機能もありますが、今回はview helperとreact_ujsを使った自動マウントの機能を紹介します。

先程の例はreact-railsを使うと次のように書くことができます。

<h1><%= @entry.title %></h1><%= @entry.body %><%= react_component('LikeComponent') %><%= react_component('CommentComponent') %>

JavaScript側ではreact_ujsを読み込んでおき、コンポーネントをグローバルから参照できるようにしておくだけで自動的にコンポーネントがマウントされます。

また、引数でpropsを渡すこともできます。

<%= react_component('LikeComponent', liked: @current_user.liked?(@entry), likeCount: @entry.likes.count) %>

このようにすることでLikeComponentに初期値をpropsとして渡すことができ、Ajaxで通信せずとも初期描画を行うことができます。

また、react_ujsの自動マウントはturbolinksにも対応しており、turbolinksでページ遷移したときに自動でマウント・アンマウントを行ってくれるという機能があります。jQuery時代にturbolinksを使って$(document).ready()が走らなくてハマる、という経験されたことがある方には嬉しいかもしれません。

Railsを使わない場合や、それだけのためにreact-railsを使いたくない場合は同じような仕組みを実現するのは大した手間ではないので自作してもいいと思いますが、1ページに複数コンポーネントをマウントする場合は、何かしらこのような自動マウントの仕組みがあると便利です。

react-micro-container

Reactではルートコンポーネントが全ての状態を管理し、子のコンポーネントにはpropsとして値を渡すようにすることで、できるだけコンポーネントから状態を取り除くというプラクティスがあります。このとき子要素で発生したイベントをルートコンポーネントに伝える手段が必要になります。

愚直にやるとイベントハンドラを子要素に渡し、全てのコンポーネントでイベントを拾って一つ上の親にあげていくという処理が必要になります。例えば、いいねコンポーネントで、「いいね」や「もっと見る」を押したときのイベントをルートコンポーネントまで伝えるのは次のようなイメージです*2

f:id:hokaccha:20161026134922p:plain

これがいわゆるイベントのバケツリレーです。今回のような小さいコンポーネントの場合も、内部でコンポーネントを分割していくと容易に数段のネストしたコンポーネントになります。

何かしらのFluxフレームワークを使ってもよいですが、こういった小さいコンポーネントにFluxフレームワークはオーバースペックなことも多いです。そこで拙作ですが、react-micro-containerという小さいライブラリを使うと、イベントのバケツリレーだけを簡略することができます。

f:id:hokaccha:20161026134928p:plain

個人的には小さいコンポーネントではこれぐらいで十分なケースも多いと思っています。詳しい使い方などはこちらの記事を参照してみてください。

小さいReactアプリケーションのためのライブラリ書いた - Qiita

注意点として、これは小さいコンポーネントであればうまくいきますが、規模が大きくなってくるとイベントの数が多くなりすぎて破綻してくるので、そういった場合はFluxフレームワークを導入するなどの対応が必要になるかもしれません。

まとめ

サーバー側で静的なHTMLを出力しつつ、動的にしたい部分だけをReactを使って実装する際のTipsについて紹介しました。

ReactはFluxなどを使って大きいアプリケーションを作ろうとすると、とたんに設計が難しくなってきますが*3、こういった小さいコンポーネントから導入する方法であれば、JavaScriptの設計になれていなくても導入しやすいですし、現状のアプリケーションに一部分導入するということも可能です。

Reactに興味はあるが難しそうで二の足を踏んでいるという方はこのようなところから利用してみてはいかがでしょうか。

*1:PolymerやVue.jsも同じようなことが実現できそうですが今回はReactにフォーカスしています

*2:コンポーネントのツリーはわかりやすくするために簡略化しています

*3:Reactに限った話しではなく、規模が大きくなれば何を使っても難しくなります

【学生限定】夜の合同説明会を開催します【クックパッドxドワンゴxグリーxはてな】

$
0
0

将来に悩んでいる学生のみなさん、こんばんは。成田(@mirakui)です。

11/18(金)に、ドワンゴさん、グリーさん、はてなさん、そしてクックパッドという4社の合同で「夜の合同説明会」を開催することになりました。

エンジニア志望の学生さんに向けたパネルセッションなのですが、普通とは違った趣向ですので、この4社に興味がある方はぜひご参加ください。

お申込みは下記の connpass からお願いします。

夜の合同説明会 - クックパッド, ドワンゴ, グリー, はてな - connpass

以下、このイベントがどうすごいのか説明します。

パネリストが豪華

私を始め、各社のトップエンジニアが一同に介します。このメンバーが一箇所に揃うのはめったにない機会だと思います。レジェンドの皆さんの前に私も緊張しています。

  • 司会
    • 庄司嘉織 (クックパッド株式会社 人事部長/エンジニア) @yoshiori
  • パネリスト
    • 藤本真樹 (グリー株式会社 取締役 執行役員常務 最高技術責任者) @masaki_fujimoto
    • 大西康裕 (株式会社はてな 執行役員 サービス・システム開発本部長) @yasuhiro_onishi
    • 成田一生 (クックパッド株式会社 VP of Engineering) @mirakui
    • 清水俊博 (株式会社ドワンゴ 人事部長/エンジニア) @meso

全員飲んでいる

登壇時、パネリストは皆お酒を飲んでいます。お酒を交えながら本音で語り合うイベントというのが今回の趣旨です。

その場でしか聞くことができないぶっちゃけトークが飛び出す可能性が高いです。私は色んな意味で緊張しています。

※参加者のみなさまにもお酒を振る舞うため、20歳以上限定とさせてください

気軽に質問を投げられる

申し込み時に質問を書くところがあるので、偉い人が酔っ払っていれば答えてくれるかも? というギリギリを考えて攻めてみてください。

なにが飛び出るかわかりませんが、パネリスト一同みな覚悟して望んでいます。この業界に興味のある学生の皆さんは、この機会にぜひお越しください。

お申込みはお早めにこちらまで!

夜の合同説明会 - クックパッド, ドワンゴ, グリー, はてな - connpass

サービス開発におけるアプリデザイナーの役割について

$
0
0

投稿開発部デザイナーの辻(@tomoya_tsuji_)です。

去る10月26日、「Cookpad Tech Kitchen#3 サービス開発におけるアプリデザイナーの役割」と題して、デザイナー向けのイベントを行いました

このイベントでは、「クックパッド」「トクバイ」のiOS/Androidアプリサービス開発において、デザイナーの役割や大切にしていることをテーマに、4名のデザイナーが発表をしました。

この記事では、その様子についてお伝えします。 尚、今回のイベントでの発表資料は公開を予定しておりません。

登壇者発表

デザイナーの組織の話について

まず、VP of Product Design 兼 投稿開発部長の池田(id:tikeda)から、クックパッドのデザイナー組織について発表しました。

ここでは、

  • クックパッドには、多種多様なデザイナーがいること
  • 横串と縦串を意識したデザイナーの組織編成
  • デザイナーとしての目標や評価軸について
  • プロダクトオーナー思考について

などを紹介しました。

デザイナー組織の体制については以下の記事で解説しているので、是非一読下さい。

毎日の料理のためのアプリデザインの毎日

次に、検索事業部のデザイナー倉光から、クックパッドのアプリ開発で取り組んでいる開発フローやプロトタイプを事例と共に発表しました。 f:id:tomoya-tsuji:20161102145541j:plainユーザーインタビューやGitHubを利用したデザインレビュー、アプリエンジニアがデザインを提案するなどの工夫についていくつか紹介しました。 GitHubを利用した開発やプロトタイプを作り、開発を行っていくフローに関して、当技術ブログにいくつか記事がありますので、ご興味ある方はあわせてご覧ください。

楽しい買物を増やすためのデザイン

次に、株式会社トクバイのデザイナー吉井より、トクバイアプリの開発フローと知見について紹介しました。 f:id:tomoya-tsuji:20161102145555j:plain

この発表では

  • トクバイアプリ開発がどのように行われているか
  • 理想の形への到達プロセス
  • レビューの改善

などについての概略を紹介しました。 トクバイのアプリ開発については以下の記事でも解説しています。

複数プラットフォームで新機能を開発時に苦労したこと

最後に、投稿開発部デザイナー若月(@puzzeljp)より、iOSアプリで新しくリリースした「限定公開レシピ」の開発時に気をつけたことや苦労したことについて発表しました。 f:id:tomoya-tsuji:20161102145603j:plainプラットフォームごとに異なる機能を整理するために表を作って可視化し、共有したり、実装後のデザイン修正によるスケジュールの遅延時の対応などをお話しました。

プラットフォームに応じたデザインに関しては以下の記事でも紹介しています。

QAセッション

上記4名の発表の後、パネルディスカッション形式のQAセッションを行いました。 当日、参加者の皆様から沢山の質問をいただきました!

ここでは、当日時間がなくお答えできなかった質問から3つ選んで回答します。

f:id:tomoya-tsuji:20161102145437j:plain

Q:実装されたものとデザインとの差異が合った場合、どうやってエンジニアさんにフィードバックしていますか?

A:差異が合った場合、デザインの修正を行って Zeplin でエンジニアさんに共有して修正してもらうことが多いですが、最近では Xcode の Interface Builder を触ってデザインの確認・変更の一通りをやり、エンジニアさんには PR で確認してもらいなるべくやり取りを少なくなるよう努力しています。(投稿開発部・若月)

Q:ユーザーインタビューの頻度・実施のタイミングや関わるメンバー(役割分担)や人数などを教えてください。

A:実施タイミングは「特定の施策について、利用状況をユーザーさんの背後の生活環境も含めて聞きたいとき」です(参考までに、検索事業部では2016年10月は2名に実施)。インタビューユーザーの探し方ですが、クックパッドに会員登録されている方々の中で対象ユーザーとして条件の近しい方にまずはメールでお声がけしています。 インタビューに中心的に関わるメンバーは2名程度ですが、インタビュー結果はドキュメントにまとめ次第、すぐに開発チーム全体に共有されます。(検索事業部・倉光)

Q:デザイナーが少ない中でもうまく回す工夫などありますか?

A:自分がやるべき範囲を見極めることを意識しています。 トクバイでは隔週ごとに約1ヶ月先までの大枠の開発計画を立てているので、 そこでサービスの全体感やエンジニアの先の動きを見て、 デザイナーとしてどう動くべきかを早めに掴むようにしています。 また、デザイナーのコストをどこにかけるのかの判断も重要ですね。 細かい部分は直接のデザイン指示やレビューで賄うなど、 手が足りない範囲をコミュニケーションでカバーすることも必要だと思います。 (トクバイ・吉井)

懇親会

上記セッションの後、懇親会を行いました。

懇親会では、他の弊社デザイナー、エンジニアも参加しながら、参加者と様々な意見交換や談話をしました。 f:id:tomoya-tsuji:20161102145655j:plain

多くの皆様にご参加いただけたおかげで、より深掘りした話をしたり、各社のアプリ開発の知見を共有するなど、活発な情報交換が行えたと思います。

また、今回はハロウィンが近かったので、ハロウィンをテーマにした料理も振る舞われました。 f:id:tomoya-tsuji:20161102145710j:plain

まとめ

いかがでしたでしょうか。

この他のデザイナーの取り組み事例は、こちらのデザイン記事一覧をご覧ください。 クックパッドでは、今後もデザイナー向けのイベントを行っていきたいと考えています。

一緒にサービス開発を行っていくデザイナーも募集しています。 ご興味のある方は是非!ご応募お待ちしております。

分析SQLのコーディングスタイル

$
0
0

SQL、書いてますか?

こと大規模データ処理の分野においてはSQLはもはや標準インターフェイスであり、 分析やらバッチやらに関わっている皆様は日々大量のSQLクエリーを生産していることと思います。

そこでちょっと気になるのが、 SQLのコーディングスタイルってどうするのが一般的なんだっけ……? という点です。 イマドキはSQLなんてO/R mapperに吐かせることが多いからなのか、 それともコードを広い範囲で共有することがそもそもないからか、 SQLのコーディングスタイルについて見聞きすることは他のプログラミング言語に比べるとだいぶ少なく、 いまいち決定版と言えるスタイルがないなと感じています。

そんなわけで本日は、SQLのコーディングスタイルについての意識を活発化させるべく、 クックパッドでわたし(青木)が使っているコーディングスタイルから特徴的な点を紹介したいと思います。 特に、分析バッチで用いるような巨大なSQLのスタイルについて話します。

なお、この記事では、コーディングスタイルを網羅的に示すことはしません。 いまさら「演算子の周囲には空白文字を置く」やら「コードはインデントする」のように どうでもいい(積極的に抵抗する人がいない)スタイルについてえんえん書くのは 時間の無駄でしかありませんし、基本方針がわかりにくくなるからです。 意見の分かれやすい、抵抗の大きそうなところに集中して述べていきたいと思います。

サンプルコード

まず、わたしが普段採用しているコーディングスタイルをお見せします。

次のクエリーは、適当にそのへんのファイルからSQLを拾ってきて、適当にテーブル名やカラム名を変えたものです。 ある程度の長さがないと事情が理解してもらえないと思うので、まあまあ長めのものにしました。 なお、この記事の内容にはクエリーの意味は関係ないので、読む必要はありません。スタイルだけ見てください。

select
    user_id
    , user_session_id
    , min(log_time) as session_start_time
    , max(log_time) as session_end_time
    , count(*) as num_steps
    , max(case session_step when 1then keywords elsenullend) as step1
    , max(case session_step when 2then keywords elsenullend) as step2
    , max(case session_step when 3then keywords elsenullend) as step3
    , max(case session_step when 4then keywords elsenullend) as step4
    , max(case session_step when 5then keywords elsenullend) as step5
    , max(case session_step when 6then keywords elsenullend) as step6
    , max(case session_step when 7then keywords elsenullend) as step7
    , max(case session_step when 8then keywords elsenullend) as step8
from (
    select
        user_id
        , user_session_id
        , row_number() over (
              partition by user_id, user_session_id
              orderby log_time
          ) as session_step
        , log_time
        , keywords
    from (
        select
            user_id
            , sum(session_delta) over (
                  partition by user_id
                  orderby log_time
                  rowsbetween unbounded preceding andcurrentrow
              ) as user_session_id
            , log_time
            , keywords
        from (
            select
                user_id
                , case
                  when
                    lag(log_time) over (partition by user_id orderby log_time) isnullor log_time > lag(log_time) over (partition by user_id orderby log_time) + interval '00:30'then1else0endas session_delta
                , log_time
                , trim(word1
                      ||''|| coalesce(word2,'')
                      ||''|| coalesce(word3,'')
                      ||''|| coalesce(word4,'')
                      ||''|| coalesce(word5,'')
                      ||''|| coalesce(word6,'')
                  ) as keywords
            from
                activity.search_log
            where
                log_time between timestamp '2015-02-01 00:00:00'and timestamp '2015-02-01 01:00:00'
        )
    )
)
groupby user_id, user_session_id
orderby user_id, user_session_id
;

このスタイルは会社の先輩が使っていたスタイルが便利だったのでそれをベースに、 ウィンドウ関数やcase式のスタイルを追加・改良したものです。 ここから主な論点として、次の7点に注目したいと思います。

  1. 大文字と小文字の使い分け
  2. カンマの位置
  3. セミコロンの位置
  4. select、from、whereを左右どちらに寄せるか
  5. joinのインデント
  6. インデント幅
  7. 標準SQLとの向き合いかた

いかにも炎上しそうな項目から順に述べていきましょう。

1. すべて小文字を使う

SQLのコーディングスタイルについて語るとき、最初にして最大最悪の障害は大文字小文字の使いかたではないでしょうか。

JavaやRubyのように比較的新しいプログラミング言語はあまり大文字を使わない傾向にあります。 しかしSQLはメインフレーム全盛の時代から生き残っているだけあって、 大文字アルファベット文化がいまなお大きな影響力を持っています。 そのため、SQLを古くから書いている人ほど大文字をよく使うように感じています。

大文字小文字のスタイルを大きく分類すると、以下の3つに分けられるでしょう。

  1. すべて大文字
  2. キーワード(予約語)は大文字
  3. すべて小文字

わたしのスタイルは3の「すべて小文字」です。

まずスタイル1ではない理由は簡単です。 大昔からSQLを書いている人ならともかく、このサイトを見るような読者層にとってみると、 「すべて大文字」というのは、ほぼ誰もやりたくないスタイルと言ってよいでしょう。 個人的にも、正直すべて大文字のコードは読みたくありません。 よってこのスタイルは最初から検討もしませんでした。

次に2の「キーワードは大文字」について。これはおそらく最大派閥ですが、わたしはこのスタイルは採用しません。

この派閥を支持する意見としては、「大文字のほうが見やすい」という主張をよく見ます。 ですがこれは大変怪しい主張です。

「大文字は見やすいからキーワードのように構文上重要な役割を持つ語は大文字である」……と本当に考えるのなら、 JavaやRubyについても同じことを言うべきです。 しかしJavaやRubyで予約語が小文字であることに対して文句を言っている人は見たことがありません。

なぜか。小文字で十分見やすいからです。

最近はどんな開発環境だろうともシンタックスハイライトくらいは完備しているでしょうし、 わざわざ大文字にせずとも視認性は十分得られます。 あえて大文字にする理由は、歴史的な理由以外にはないと思います。

「おまえは今まで使ったSQLのキーワードがいくつあるか覚えているのか?」

しかしもちろん、「キーワードだけ大文字」に見やすさという理由がなかったとしても、歴史的な継続性はあるわけです。 少なくともわたしの主張する「すべて小文字」派よりは多少なりとも昔の面影を残しているぶん、 古くからSQLを書いている人たちにもアピールするという利点はあるでしょう。

ですが、それでもわたしが「キーワードだけ大文字」を採用しないのは、このルールがあまりにも厄介だからです。

SQLには、他のプログラミング言語に比べて遥かに多くのキーワードがあります。 selectやfrom、join、asなどは当然として、 関数名もたいていキーワードなのでcountもmaxもsumもキーワード、 ウィンドウ関数のrankもrow_numberもキーワード、rowやunpreceedingもキーワード、 extract関数で使うyearやmonth、dayなどもすべてキーワードです。 あまりにもキーワードが多すぎて、たぶん誰もすべてのキーワードを覚えていません。

特にuserやactionのようないかにもよく使いそうな単語がキーワードなのが最悪で、 これらが現実的な確率でカラム名として使われてしまいます。 そうなったときに、こいつらも大文字にするのか、クオートだけして小文字にしてしまうのか、 といったしょーもないことで悩む必要が出てきます。

また、RDBMSごとの特有のキーワードというものも存在します。 しかしそういったDB固有のキーワードはエディターがサポートしていないことが多く、 DB固有のキーワードだけは小文字にされてしまう……みたいな悲しい事態に陥ります。 UDF(User-Defined Function)やUDT(User-Defined Type)のような、 ユーザーが独自に定義する関数や型も同様の問題を起こします。

具体的にはこんな感じの見ためになるわけです。

SELECT"USER"
    , COUNT(user_id)
    , some_udf("USER")
    , EXTRACT(YEAR FROM created_at)

これが、本当に、見やすいのでしょうか?

もう正直気持ち悪い面倒だわ、どれがキーワードだか覚えられんわで、いいことは何一つないように思われます。 ここまでやるなら「すべて大文字で書くよ」派のほうがまだ一貫性があって楽かもしれません。

真面目に従えない使い分けルールを決めるくらいだったら、そもそも大文字小文字の使い分けをやめたほうがまだマシです。 そして大文字と小文字のどちらかに揃えるとしたら、小文字のほうが幸せになれる人が多いはずです。

2. 改行前後のカンマと演算子は次の行の先頭に置く

次の話題に行きましょう。

カンマや演算子のところで改行する場合は、次の行の先頭にカンマや演算子を置きます。 つまりこの部分ですね。

select
        user_id
        , user_session_id
        , row_number() over (
              partition by user_id, user_session_id
              orderby log_time
          ) as session_step
        , log_time
        , keywords

andなどの論理演算子も次のようにすべて先頭に置きます。

where
    hst.user_id isnulland hst.updated_at isnull

このスタイルを選んだ理由は、記述しているカラムや条件のかたまりが最もわかりやすいからです。

最初のコードを見てもらえばわかるように、ウィンドウ関数やcase式が入ってくると、 インデントだけではselect文にカラムがいくつ書かれているのかよくわからなくなってきます。 しかし不幸にしてSQLではカラムの順序に大変大きな意味があるので、 何カラムめに何の値が入っているかというのはかなり重要な情報です。 そこでカンマや演算子を先頭に並べて書いておくと、それを数えるだけで項目数がすぐわかるわけです。

カンマを前に置くスタイルについては編集上の利点を挙げる人もいますが、わたしは特にそこは重視していません。 あくまでカラムの数と位置のわかりやすさを重視します。

もっとも、演算子はともかくとして前カンマについては、単純に気持ち悪いと感じる人が多いでしょう。 正直わたしも最初のうちは「先頭キモいな〜、これ本当に前に書くのかな〜」と思っていたのですが、 ちょっと面白かったので試しに3日間書いていたら慣れました。 わりと簡単に慣れるので試してみてください。

ちなみに、他のプログラミング言語でも例えばLispのマクロでは前カンマを使いますし、前例がないわけでもありません。 プログラミング言語は自然言語ではないのだから、必ずしも自然言語由来の自然さにこだわる必要はないと思っています。

3. 文末のセミコロンは単独行に置く

3つめの話題。文末のセミコロンは単独で、別の行に置きます。 つまり次のように書くわけです。

select ...
from ...
where ...
;

これは実はあまり大きな理由はなくて、カンマの前置に揃えているだけです。 強いて言うと、psqlなどのコマンドラインクライアントにコピペするときにセミコロンだけ抜いてコピペがしやすい、という利点があります (セミコロンさえ打たなければCtrl-Cでキャンセルできるので、変な失敗が起きにくいのです)。

4. 各句のキーワードはインデントしない

4つめの話題。 ちょっとあまりに面倒くさすぎて信じられないのですが、 世の中には次のようにキーワードを右寄せにしたがる派閥も存在しています。

select count(*)
  from mst.users
 where user_id = 5;

わたしはsyntax off; set sw=4 ai smd ts=8 etですべてのエディタ設定が完了する オールドタイプ人類なのでさすがにこれに付き合う気はありません。 select、from、where、order byのレベルはすべてインデントを揃えます。

そもそも、この右寄せスタイルの効果があるのはそうとうに短いクエリーだけです。 サブクエリーのネスト5段、1 selectごとにカラムが10個……みたいな、 分析系ではよくありがちなクエリーには無用の長物でしかありません。 むしろ各句の左端が揃わなくなるため、サブクエリーのネストレベルがわかりにくいという欠点が目立ちます。

そして何よりも、キーワード右寄せスタイルは「select for locking access」のとき (あるんですよ世の中にはそういう文が……)に26文字インデントしなければならないのが最悪だと思います。 こんなことになるわけです。

selectfor locking access count(*)
                     from mst.users
                    where user_id = 5
;

カラムの右揃えは空白文字と時間の無駄です。黙って左に揃えましょう。

5. join式はfromよりもインデントする

5つめの話題はfrom句内のインデントについて。

ジョインが存在する場合のインデントも判断の分かれやすいところです。 わたしが採用しているスタイルは次のように、joinの記述をfromよりもインデントします。

from
    work.weighted_segments s
    inner join source.factor_score_flg f on s.guest_user_id = f.user_id
    inner join source.factor_score_mst m using (attr_id)

一方で、次のようなインデントスタイルも根強く存在します。

from work.weighted_segments s
inner join source.factor_score_flg f on s.guest_user_id = f.user_id
inner join source.factor_score_mst m using (attr_id)

ですが、このスタイルはわたしに言わせればインデントの役割を完全に放棄しているとしか思えません。

上記の式では3つのテーブルをジョインしていますが、その場合、3つのテーブルをジョインしたリレーション全体がfrom句の値です。 1つめのテーブルだけではありません。 ならば、文法の内包構造をインデントに表現しようとした場合、3つのテーブルと「inner join」は すべて「from」より下位にあるべきで、「from」と「inner join」を同列に並べる選択はありえないでしょう。 そもそも「from」は句ですが、「inner join」は二項演算子です。その時点で両者は対等ではありません。

「何もインデントしない」スタイルを採用するのでない限り、 from句に含まれるコードはすべてインデントすべきです。

6. インデントとネスト、そしてwith句について

6つめの話題はようやくインデントの深さについてです。 わたしのスタイルではSQLのインデントは空白4文字にします。このへんは趣味です。

強いて4スペースにする理由を挙げるなら、バッチではかなりネストの深いサブクエリーを使うことが多いので、 2スペースくらいだとすぐに見分け付かなくてきつい、くらいでしょうか。 4スペースでもよくわからなくなるときがあります。 しかし8スペースまで深くすると今度は200桁くらいになってしまうことがあり、さすがに深すぎます。 SQLの場合はCで関数を分けるような気軽さではサブクエリーを分離できないので、回避しにくいことも問題です。

ちなみにサブクエリーの代替になりネストも減っていいじゃんと話題のwith句ですが、 わたしは実行プラン(explain)を見るまでは絶対にwith句にはしません。 プランナーを信用していないからです。 基本的に、RDBMSのプランナーは新しい構文を使うほどよくしくじります。 新しい構文は用心してかかるのが得策だと考えます。

またSQLバッチに関して言えば、withを使って見ためのネストだけ下げるよりは、 ビューを作ったり、ワークテーブルを追加して対処したほうが見通しがよくなるだろうとも思います。 with句だろうがサブクエリーだろうが途中経過は人間には見えないので、 巨大なクエリーでは開発・運用が難しいことに変わりはないからです。

7. 標準SQLのこと、忘れてください……

最後に標準について。

ことSQLに関して言う限り、ポータビリティを考えてANSI標準だけを使うなどという選択は間違いです。 少なくとも、現実的ではありません。

そりゃあもちろん、O/R mapperが生成する類のこの程度のクエリーなら問題はないでしょう。

select * from entity_table where id = 230985;

そしてこれ以上に複雑なほとんどすべてのクエリーが標準をはみ出すことを覚悟しなければなりません。

……いや、それはさすがに言い過ぎでした。言葉の綾というやつでした。

ですが例えばPostgreSQLのリファレンスマニュアルから適当な構文のページを開いて、 「この構文はPostgreSQL独自拡張です」のフレーズが何回登場するかを数えてみてください。 インデックスを張ったとたんに、upsertをしたいと思った瞬間に、ちょっと特殊な制約を付けたいと思ったばかりに、 サクッとANSI標準から踏み出すことを覚悟しなければならないのがSQLの現実です。 そして、標準の範囲内ですべての処理を書くことよりは、DB固有拡張のほうが必要性が遥かに上です。 標準にこだわってDB固有拡張を避けるのは悪手と言わざるをえません。

そもそもポータビリティを考えるという場合、DBを乗り換えることが前提ですが、 DBをプロジェクトの途中で変えることなどまずありません。 これはむろん鶏と卵の関係にあります。SQLがまともに標準化されていないからDBを乗り換えることができず、 どうせDBを乗り換えることはできないから標準なんてどうでもいいのです。 たいへんウンザリします。

各種RDBMSの差がもうちょっと少なくなってもらえないかという気持ちはやまやまですが、 いまのところは標準は「使えるときだけ使う」のが現実解でしょう。

おわりに

この記事では、わたしがクックパッドで使っている分析SQLのコーディングスタイルについて、7点に絞ってお話ししました。 コーディングスタイルはいつの時代も戦争の引き金になるので、いま大変嫌な予感がしています。 みなさんの手元ではどんなスタイルが使われているのか、はてブコメントなどでお寄せいただければ幸いです。

最小限にこだわるサービス開発の試み

$
0
0

こんにちは、検索事業部の原田です。クックパッドでプロダクトマネージャーとして検索領域を中心にサービス開発に携わっています。

サービス開発を成功に近づけるためのフレームワークやプロセスについては、書籍等で多くの知見が紹介されています。こちらのブログでもクックパッドの例についてご紹介をしてきました。

クックパッドで大事にしているサービス開発の取り組みのひとつに「仮説をスピーディーに最小限の形で確かめる」というものがあります。言うは易しなのですが、そもそも取り組むべき最小限の形とは? それをどうやって見つけるのか? やっとの思いで何かを見つけられたとして、その後最小限にこだわり判断し続けるには? と、大変な困難が伴うと感じています。

今回は、サービス開発プロセスの中でも特に、どのように取り組むべき最小限の形を見つけ、最小限にこだわり続けながら開発を進めるのかについて、私が気をつけていること(気をつけたいと考えていることも含みます)をご紹介したいと思います。

取り組むべきサービスの判断基準

本題に入る前に前提の話として、そもそも取り組むべきサービスとは何でしょうか。会社の状況や責任範囲により異なりクックパッド内でも考え方は様々で一律の基準は提示できませんが、私の場合は以下のような着眼点を持つことで目指す的をなるべく狭くできるようにしています。

  • それは料理を楽しくするかどうか(情熱をもって取り組めるものか、ユーザーにとって価値のあるものであるか)
  • それは誰にも負けないことかどうか(実現可能なことか、世界一になれる部分はどこか)
  • それは儲かるかどうか(経済的な原動力になるのは何か)

この三つの重なりを意識することが、後々最小限を維持するための拠り所になります。サービス開発に限った話ではありませんが、このような考え方は、書籍『ビジョナリーカンパニー2~飛躍の法則~』の中では「針鼠の概念」として紹介されています。もちろん、初めから三つを重ねるようなアイデアはないことがほとんどです。重ならなかったら着手しないということではなく、開発していく過程で3つを重ねることを常に問い続けるプロセス自体が大事だと考えています。

最小限であることがなぜ大事なのか

ここからが本題なのですが、そもそもなぜ最小限にこだわる必要があるのでしょうか。反対側から考えてみると、最小限でないことにより以下のようなことを失うのではないかと考えています。

  • ユーザーに届くまでのスピード(製品化に至るまでには時間がかかる、機能が多ければそれだけ確かめるまでが遠くなる)
  • コア機能に集中できるメンバーの時間(一緒に働きたいと思うメンバーは常に有限。機能を足す判断をするごとに、最初に定義した取り組むべきコアな部分を開発できるメンバーを失う)
  • 検証のシンプルさ、明確さ(選択肢が多い=ユーザーは判断をせねばならない。ユーザーの生活は十分に忙しいはず。どの機能を使うかの判断をしなくてはならないようなサービスが本当に愛してもらえるかを考える必要がある)

機能を広げる判断をするときには、必ず同時に対で何かを失う判断をしているということを認識するようにしています。

最小限はなぜ難しいのか

初期の段階では、慎重に最小限の形をイメージしていることが多いと思うのですが、プロセスが進むにつれて最小限にこだわることへの難易度が上がっていきます。例えば具体的には以下のような状況が生まれます。

  • 関わるメンバーが増え、より良いアイデアについて議論の場が生まれる。
  • プロトタイプを使ったユーザーからこういう機能があったら便利、ここが不便、という具体的なフィードバックを得る場が生まれる。

上記の例はどちらも、サービス開発を進める上で必要であり不可欠あることは疑いようがないですが、機能を広げる方向に議論がおきやすくなるのは事実です。そもそも多様なニーズをサポートしていないという背景とともに、実現すべき体験・価値といったものの言語化が困難なことが機能追加へのプレッシャーを加速しがちです。上記の例で言うと一つ目のほうは「最小限とは?」の定義が不十分であれば当然は議論は発散しますし、二つ目のほうは利用者の具体的な声は説得力があるのに比べて、機能追加をするデメリットは言語化しづらいことが多いのではないでしょうか。

取り組むべき最小限を見つけ、集中し続けるために

ここまでは最小限であるべき理由の話でしたが、次は最小限の形を見つけるために具体的に実践していることをご紹介します。

企画発想:現実世界の代替手段を注視

企画発想の時点で価値を定める時に「機能ではなく体験をイメージする」という話があります。しかしそもそも体験を言葉にするのはとても難しいことです。

そこで、今その問題を抱えている人が現実世界で行っている解決方法に着目するようにしています。そのことで、言葉に表現せずともメンバー間で議論しようとしている体験のイメージを揃えることができるようになります。現実世界というところがポイントです。もし現実世界での解決方法がないのだとすると、それは取り組むべき十分切実な問題ではないかもしれないこともチェックできます。

例えば、献立に困っているユーザーさんは今どういった解決方法をとっているか? という問題に着目した時に、「Googleで検索している」だと、スマホを手にして情報を探索する体験に縛られてしまいます。現実世界で考えるとどうなるか。例えば、「本屋さんで棚にある雑誌をざーっとながめ、手に取りぱらぱら〜っとする」「母親に電話してアレどうやって作るの?と聞く」「給食の献立表を眺める」という具合になります。

そうすることで上記の例では以下のように体験を具体的にイメージすることができるようになります。

  • 「ざーっとながめたり、ぱらぱら〜っとする」という体験にはどういう意味があるのだろうか→一度にバッと見られることって何か意味がありそう
  • 電話は友だちに訊ねるではダメなのか?→母親に「あのよく出てたタコの炒めた感じのアレどうやって作るの?」って、家でよく出た「アレ」は名前がはっきりしていなかったりする
  • 給食の献立ってレシピないけれどどういうことなんだろう?→給食の献立表ってメニュー名だけだけれどなぜか大体どんな味か想像できるな

このように現実世界を注視することで、言葉にしにくい体験を具現化しやすくなり議論の発散を防いでくれると考えています。

施策開始前:最小限の形を見つけるためのプロトタイピング

上記で定義した体験を製品に落とし込んでいくわけですが、多くの場合はもやもやしながらスタートします。私は、この時点でコンセプトなど言語化の詰めを急がないようにしています。なぜなら、言葉の稚拙さのせいで可能性を閉じてしまうことがあるからです。その代わりに最小限の形を見つけるためのプロトタイピングを行っています。

プロトタイピングするというと、施策をスタートさせた後に行うイメージがありますが、私はコンセプトを決めた後に行うプロトタイピングと、取り組むべき最小限の形を見つけるためのプロトタイピングをわけて考えるようにしています。今回の題材である「取り組むべき最小限の形を見つけるためのプロトタイピング」は、言語化することの限界を超えるためのプロセスとして考えていて、ゴールは技術やビジュアルの仕様を固めることではありません。「私だったら今まで週1回しか使っていなかったけれども毎日使っちゃう」という感覚に、自分以外の人が触わり議論をスタートできる状態にできることがゴールです。ここまでは、言語化が必要ない人数で進める必要があります。

上記の状態を達成できてはじめて、施策化し開発を開始するかどうかの判断ができます。裏を返せば、上記の状態が達成できなければ「開発をしない」という判断をするということです。そういう意味で、開発スタート後のプロトタイピングとは大きく役割が異なります。

施策化:圧倒的な「売り」を見つけて言語化・数値化

もやもやとしたアイデアをめでたく 「私だったら今まで週1回しか使っていなかったけれども毎日使っちゃう」という形にできたところで、ようやく言語化に取り組みます。仲間も増えて開発をスタートできるこのフェーズでは、以下の問いに対する解をもった言葉(=売り)を持つことが、最小限からブレない判断をサポートしてくれます。

  • 利用者の生活をどう変えるか?
  • 競合と比較して圧倒的に差別化された利点は何か?

「売り」の概念や言語化については今回は詳しくご紹介しませんが、ご興味がある方は、『「売れ顔」の法則』をはじめとした消費財におけるマーケティングに関する情報がとても役立ちますのでご覧ください。

売りを一言にできたところでその言葉を数字で表現し、あとは数字を上げるよう行動します。 私の場合はこのように整理して共有したりしています。

f:id:a_harada:20161124182810p:plain

この後も常に機能追加のプレッシャーを受け続けることになるため、取り組むべき最小限にこだわるために何かを判断する時は以下のことに気をつけています。

  • それは数値を10倍にするか?
  • それは絶対に他の人も毎日使うはず、と思えるか?「増えそうだ」「良さそうだ」ということになっていないか?

なお、「数字を上げるよう行動する」についてはこちらブログでも紹介されている「新サービス立ち上げ時の重要指標のデザイン」等に詳しいので、ぜひご覧ください。

施策開始後:最小限以外の事項は、検討の「解禁日」を設定

開発を開始できるとコアな価値自体とは関係がなくても、サービスを届けるためには検討が必要な事項や機能が必ず出てきます。例えば既存のサービスのどこからアクセスしてもらうか、そこに今入っている機能とどう折り合いをつけるか、といったことがその一例です。これは検討しているサービスが公開へのフェーズに向かっている証拠であり、喜ばしい事態です。

同時に、例えば既存機能との調整は特にもやもやとした新しいサービスの価値よりもはるかに具体的にややらなくてはいけないことがはっきり可視化できるゆえ、まだ最小限の形ができてないうちから時間を使ってしまうことが問題です。結局ユーザーに使われなければいくら調整がうまくできても本末転倒、という事態を避けるために、この案件はここの日から考え始める、という「解禁日」を設定しスケジュールに明記して周囲に宣言するようにしています。そのことで、それまでは最小限の実現だけに集中できる環境を用意することができます。

「最小限」かどうかの確かめ方

あなたが今取り組んでる施策の「ターゲットでない人」、瞬時に言えますか?

もしも途中で「今最小限の形で進められているかな?」と不安になることがあったら、この問いを思い浮かべてみてください。瞬時に具体的に答えられない場合は危険信号。最小限の製品ではターゲットは限られた人であり、ターゲットではない人が明確に存在するはずです。

まとめ

ゼロからのサービス開発でも既存サービスに対する開発であっても、機能を広げない判断は大変です。良さそうな機能をどんどん追加できるなら… という誘惑にいつも揺らぎそうになります。しかし、機能を追加する判断をすることは、その機能が良いとか悪いということとは別に、必ず対でユーザーに判断という負担を強いること、開発する仲間の集中を失う判断をしているということに他なりません。開発する仲間の力が分散することは、サービス改善が遅れる、もしくはそもそもできないという形でユーザーに反映されます。

気がついたら複雑すぎてメンテナンスされていない機能がたくさんあるというような不幸な状態は誰も望んでいないはずですが、前述のように言語化に限界がある以上そうなりやすい状況は必然であることを前提に、判断をサポートする仕組みづくりに取り組んでいきたいと感じています。

最後になりますが、クックパッドでは既に存在するものにとらわれない視点でサービス開発に取り組むことに興味のある方を募集しています。ぜひご応募ください!

実例に基づいた大規模 iOS アプリの継続的な開発についての勉強会を開催しました

$
0
0

f:id:nano-041214:20161201191121j:plain

技術部モバイル基盤グループ新卒エンジニアの日高(@natan3)です。

去る11月17日、「Cookpad Tech Kitchen #4 〜Cookpad × MoneyForward〜」と題して、iOS エンジニア向けの技術交流イベントを行いました。

https://cookpad-tech-kitchen.connpass.com/event/43082/

このイベントでは、 iOS開発の中でも特に大規模アプリの品質を維持するための設計や、複数の言語圏や様々なパートナー企業に合わせたアプリの提供をテーマに、弊社のエンジニアから2つ、マネーフォワードさんから2つの発表がありました。

この記事では、その様子についてお伝えします。

iOSアプリケーションの海外展開

まず、海外事業部の西山(@yuseinishiyama)から、海外事業向けのiOSアプリケーションの開発フローについて紹介がありました。

How to make your app international by Yusei Nishiyama Published November 18, 2016 in Programming

この発表では

  • 言語圏に合わせたコンテンツや UI のローカライズ
  • RTL 対応
  • その他アプリのローカライズに関する Tips

などについて紹介しました。

私個人としては、普段日本向けの開発しかしていなかったので、アラビア語圏への対応などとても興味深かったです。

また、グローバルプラットフォームならではのサービス開発に対する取り組みは以下の記事でも紹介しています。

海外のユーザーを向いたプロダクト開発の工夫iOSアプリケーションの国際化と地域化

トクバイ新アプリと Swift と

次に、クックパッドの子会社であるトクバイの行川(@hatuyuki4)から、トクバイアプリがどう Swift で開発を進めているかについての発表がありました。

トクバイ新アプリとSwiftと by Hatuyuki4 Published November 21, 2016

この発表では、Swift + RxSwift + MVVM の実装について具体例を交えながら紹介しています。

リアクティブな処理にすることで複雑なコールバックによる辛さから開放されたり、UnitTest が書きやすくなったりと Rx のメリットがわかりやすく示されているので、Rx 導入を考えている方にぜひオススメしたいスライドでした。

Ruby on Rails にはなりますが、react-rails を用いたアプリを開発する際の知見についてはこちらの記事で紹介しておりますので、興味の有る方はぜひこちらも御覧ください。

非SPAなサービスにReactを導入する

iOS Clean Architecture のすすめ

3番目の発表では、マネーフォワードさんの iOS エンジニアである児玉さんから、マネーフォワードではどのような設計で iOS アプリを開発しているのかについての発表がありました。

iOS Clean Architecture のすすめ by koutalou Published November 17, 2016 in Technology

この発表では、Clean Architecture の実装例と導入する際のメリットを紹介しています。

児玉さんの Github アカウントで iOS-CleanArchitecture のサンプルが公開されているので、私も試してみようと思いました。

児玉さんが執筆されたマネーフォワード エンジニアブログの記事はこちらにありますので、tvOS やマネーフォワードの開発体制に興味がある方はぜひこちらも御覧ください。

マネーフォワードのtvOSアプリケーションを開発した話

マネーフォワードの変遷と「パートナー企業版」の展開

最後に、マネーフォワードさんの iOS エンジニアで、取締役の都築さんから、マネーフォワードがどのようにアップデートを重ねてきたかについて発表がありました。

マネーフォワードの変遷と「パートナー企業版」の展開 by Takayuki TSUZUKI

この発表では、マネーフォワードさんがかつては Titanium で開発されていたという意外な事実や、パートナー企業版の開発で起きた問題をどうやって解決したかについて紹介しています。

アプリのリニューアルはとても大変な作業なので、きっと色々な苦労を経て今のマネーフォワードさんのアプリがあるんだなと感じました。

都築さんが執筆されたマネーフォワード エンジニアブログの記事はこちらにありますので、マネーフォワードさんのアプリのリニューアルについてもっと詳しく知りたい方はぜひこちらも御覧ください。

iOS App資産タブリニューアルを終えて

質疑応答

f:id:nano-041214:20161201191148j:plain

質疑応答では、

  • Clean Architectureの導入に伴う初期コストをビジネスサイドにどのように説明したか
  • Rx を実際に取り入れる過程や導入後の開発効率の変化

に関した内容の質問に対して

  • 複雑化してメンテナンス性が下がったコードに対して、開発効率が10倍になる!などで熱意を示してきっかけを作ったこと
  • 全員が理解していればすごく効率が上がるが、移行期には同じ処理を二つのアーキテクチャで書く必要があるなどの問題もあること

といった回答を得られました。質疑応答も大変盛り上がり、とても有意義な時間になったと思います。

懇親会

上記の発表の後、懇親会を行いました。

懇親会では、他の弊社の iOS アプリエンジニアやデザイナも参加しながら、参加者と様々な意見交換や雑談をしました。

多くの皆様にご参加いただけたおかげで、iOS 開発にありがちなことで盛り上がったり、各社の iOS 開発の知見を共有するなど、有意義な時間を過ごすことができました。

今回は合同イベントということで、弊社のロゴとマネーフォワードさんのロゴを用いた押し寿司も振る舞われました。

f:id:nano-041214:20161201191234j:plain

まとめ

いかがでしたでしょうか。

クックパッドでは、今後もエンジニア向けのイベントを行っていきたいと考えています。来る 2017 年 1 月 21 日には、 Cookpad TechConf 2017を開催する予定です。

また、クックパッドでは、一緒に iOS アプリ開発を行っていくエンジニアを募集しています。ご興味の有る方は是非遊びにいらしてください。

青森観光アプリ開発コンテストに参加してきました

$
0
0

こんにちは。研究開発部の山田(@y_am_a_da)です。

11/18(金)〜20(日)の3日間、「観光」をテーマにしたアプリケーションの開発コンテストである「青森観光アプリ開発コンテスト」が星野リゾート 青森屋で開催されましたのでその報告をさせていただきたいと思います。

青森観光アプリ開発コンテストとは

青森県のファンを増やすため、青森屋が県内各地域の魅力を全てお客様に伝えきることの出来るアプリを企画・開発するコンテストです。

今回のコンテストでは、実際に観光客の目線を持てるように、参加者は青森屋に2泊3日の宿泊をしながら企画・開発を行いました。

クックパッドからは、投稿されているレシピのデータを提供するとともに、コンテスト当日もサービス開発やデータの使い方に関するアドバイザーが数名参加しました。

また、アマゾン ウェブ サービス ジャパンからもAmazon Web Serviceのアドバイザーが参加していました。

1チーム最大5名で、下は21歳、上は50歳と非常に幅広い年齢層の人々が集まり、アプリケーションの企画・開発を行いました。 当日に即興で決まったチームもいくつかありましたが、みなさんすぐに打ち解けて積極的にディスカッションを行っていました。

成果発表

コンテスト最終日に行われた発表は、作成したアプリケーションがどう役に立つのかを短時間の劇で表現するという形式のものでした。 いくつか賞がありましたが、その中でも青森屋賞、Amazon Web Service賞、クックパッド賞についてご紹介致します。

青森屋賞

  • チーム名:KBS
  • タイトル:青森屋探検隊 これなにアプリ

青森屋にはさまざまなものが展示されているのですが、それがなんなのかよくわからない!という自身の体験から生まれたアプリケーションです。

アプリケーションの内容は以下のとおりです。

  • 青森屋にある展示物にアプリを起動した状態でスマホのカメラをかざすとそれが何かを教えてくれる
  • アプリ内に青森屋の全体図を入れておき、ゲーム要素を追加して、青森屋を探検したくなる仕組みを作る

特に後者の機能が小学生の夏休みの自由研究のテーマとしても使ってもらえるのでは。ということで受賞となりました。 こちらは青森屋で来年サービス実現にむけて取り組むそうで、実現の際に入賞チームが宿泊御招待という副賞がついていました。

Amazon Web Service賞

  • チーム名:necco
  • タイトル:青森屋Watch

「体験をもう1つ多く」をコンセプトに青森屋で起きている色々なイベントを体験できなかった。いつどこでやるのかわからなかった。ということを防止するアプリケーションです。 青森屋では青森にまつわる様々なイベントが日々行われており、滞在中の満足度向上の他「他にもこういうのがあった、見たい」と知る事で再訪につながるそうです。

アプリケーションの内容は以下のとおりです。

  • 青森屋のどこでいつ何がやっているかがわかるようにタイムラインとかオススメが見れる

こちらのチームはある企業から5人組で参加しており、抜群のチームワークで最も高いデモンストレーションのクオリティを見せていました。

クックパッド賞

  • チーム名:青森フレーバー
  • タイトル:里山の郷土レシピからみつける、私だけの旅レシピ

こちらは独立したアプリケーションではなく、クックパッドに機能を追加するというものでした。内容は以下のとおりです。

  • 各地域の人が位置情報とともにレシピを載せることができる。
  • 「レシピから自分の旅のルートを決める」という項目をクックパッドにつけ、利用者は作ってみたいレシピベースで旅行の経路を決めることができる。

旅行ではその土地の郷土料理を食べることが楽しみの1つとなっているということ、実際に料理教室にその土地に旅行に来た人が来ることが少なくないということから考えられたそうです。

まとめ

私自身もコンテストが始まる前のアイスブレイクとして、頭の体操、ブレインストーミングのようなものに参加したのですが、置かれている環境や、価値観の違った人と意見をぶつけ合い、答えを出す作業というのはとても刺激的で楽しいものでした。

また、こういったコンテストにはメンターの方が来られることも多く、自分の知らない分野の疑問についても専門家の目線からアドバイスを頂けることがあるのでかなりお得感がありました。

クックパッドでは積極的にハッカソンを開催しており、直近では中高生No1を決めるcookpadハッカソンも開催します。今後も積極的にハッカソンなどを開催していきますので、その際にはぜひご参加頂ければと思います。


EarlGreyを使った画面操作を伴う自動テスト

$
0
0

こんにちは、技術部品質向上グループの茂呂一子です。

品質向上グループではモバイルアプリにおける自動化されたテストの一環として、画面操作を伴うテストを実施しています。 例えば、古くからAppiumを使い、その結果を判定するという施策を行っています。(参考: iOSアプリデザインリニューアルの舞台裏の舞台裏)

クックパッドではAppiumも利用していますが、より高速に実行できるツールとして、EarlGreyというGoogle製のフレームワークの導入を試みています。

この記事では、EarlGreyの導入と現状についてまとめます。

内容は、iOS Test Night #1でLT発表した内容からの抜粋です。LT資料はこちら

画面操作を伴うテストの自動化

クックパッドでは、 モバイルアプリの最低限の機能を確認する用途で画面操作を伴うテストを自動化しています。 このテストは、Android/iOSアプリのテストの区分戦略にて定義しているサイズをもとにすると、L/Eサイズに当たるような領域にを対象にしています。 ユーザーに提供する体験を簡易的なシナリオとしてまとめ、 それが完遂されることとレイアウトのくずれが発生しないことをみて、最低限のアプリの価値を確認しています。

画面操作の自動化を支援するツールとして、Appiumを聞くことが増えてきたように思います。

クックパッドでもAppiumは利用していますが、開発ラインに組込むにはよりCIへの組込みが容易で、より高速に実行できるツールが望ましいです。 その候補となるツールには、XCUITest、EarlGreyなどがあります。

Xcode 7が出た頃にXCUITestを試しましたが、テストプロセスの起動に失敗したり、画面上の要素(ボタンなど)の検出に不安定さがあり、実用を目指すには至りませんでした。 その後、EarlGreyが登場し、試用をすすめています。

EarlGreyを使ったテスト

EarlGreyは、Google製のUI Automation Test フレームワークです。 Xcode の提供する XCTest Framework の上で動き、Xcode上でコーディングして実行することができます。

Appiumに比べると、 実装が製品コードに近い場所にあり、実行速度が速く、XCTestの拡張なのでCIへの組込みが容易です。 一方、Swiftコードで記述するために可読性が下がるほか、 テストのライフサイクルが狭いためにシミュレータの初期化できず、前後のテスト実行の成否に影響を受けやすいです。

EarlGreyは、基本的な機能がそろっており、特定の要素が表示されるまでスクロールするなどの便利な機能も提供されています。 しかし、クックパッドアプリで使うにはひとつ足りなくて困ることがあります。

SystemAlertを操作したい

iOSには、カメラの使用やプッシュ通知の許可など、ユーザーに許諾を求めるダイアログがあります。(以下システムアラート) システムアラートは、許可する/しないを選択する必要があり、画面操作をする上で避けて通ることができません。

f:id:ichiko_revjune:20161209203230p:plain

XCUITestでは、システムアラートの操作が可能となっていますが、EarlGreyはXCTest上で動くため、この操作ができません。 EarlGreyのリポジトリにissueがありますが、すぐに解決ができない状態のようです。 このissueの中に、Facebook/WebDriverAgentが使えたというコメントがありました。 これをヒントに、WebDriverAgentLibを使用して実現を試みました。

以下の試みは、EarlGrey v1.3とXcode 7.3.1、Swift 2.2を使用しています。

WebDriverAgentを使ってシステムアラートを操作する

WebDriverAgentLibは、Carthage対応されていますが、試した時点ではコンパイルできないなどの不具合があり、一部のコードをインポートする形で導入しました。

WebDriverAgentLibの導入には手間がかかりましたが、これによってシステムアラートの操作は可能になりました。 以下のようなコードで、システムアラートの項目を選択することができます。 (FBAlertは、WebDriverAgentLibが提供するクラス)

letalert= FBAlert(application:XCUIApplication(privateWithPath:nil, bundleID:"BUNDLE_ID"))
ifletalertElement= alert.alertElement() {
    letbuttons= alertElement.descendantsMatchingType(XCUIElementType.Button).allElementsBoundByIndex
    letbutton= buttons.filter { $0.label == label }.first
    ifletbutton= button {
        button.tap()
    } else {
        GREYFailWithDetails("Labled Button '\(label)' on Alert Dialog was not found.", details:"")
    }
} else {
    GREYFailWithDetails("Alert view was not found.", details:"FBAlert.alertElement returned nil.")
}

残念なことに、製品コードの開発が Xcode 8 に移行してからは、FBAlertを使ってシステムアラートを操作することができなくなりました。 WebDriverAgentはPrivate APIを使用するため、Xcodeのバージョンが変わることで整合性がくずれたのでしょう。

しっかり使って育てていきたい

EarlGreyはOSS(オープンソースソフトウェア)として公開されているフレームワークなので、必要な実装は追加していくことが可能です。 今回は、コミットするところまでいけませんでしたが、自分たちが使っていきたいものなので、まだ育てるフェーズと思ってどんどん試していきましょう。 クックパッドでは業務時間中のOSSへの貢献も認められているので、フレームワークへも貢献しつつ、それを活用していきたいですね。

クックパッドではこのような取り組みを共に実施し、より良いサービスを提供し続ける為にエンジニアを募集しています。ご興味のある方は、是非とも覗いてみてください。

Interface Builderをデザイナーさんに触ってもらうにあたってやったこと

$
0
0

こんにちは、投稿開発部の市川です(@masaichi)
主に、クックパッドiOSアプリの投稿周りの機能を担当しています。

はじめに

みなさんはiOSアプリを開発する際に、どうやってレイアウトを調整していますか? クックパッドでは大体の場合は、デザイナーにZeplinなどでレイアウトの指示書を貰いエンジニアが実装するという流れで組んでいます。 しかし、このやり方の場合、終盤の細かなデザインの調整の際に、修正と確認が細かく発生してしまい、デザイナーとエンジニアの時間を細切れに使ってしまう、という問題がありました。

今回、この解決の手段として、終盤のデザインの調整をデザイナーさん自身にInterface Builderで調整をしてもらうトライを行いました。 10月頃、iOSアプリに追加した「みんなの投稿」機能を題材に、この過程と効果を紹介します。

なぜInterface Builderを触ってもらうことにしたのか

「終盤の細かなデザインの調整の際に、修正と確認が細かく発生してしまい、デザイナーとエンジニアの時間を細かく使ってしまう」という問題の解決として、時間を決めてペアプログラミングをする、というのもあります。 これも短い時間で集中して対応することが出来るのでうまくワークします。

とはいえ、この手段もエンジニアが実装をしてデザイナーが指示を出し、というやり方になるので、こだわりきれない点もあるのでは・・と考えていました。

そんな時、夏にあったiOSDCというカンファレンスで「デザイナーにStoryboardをお任せする技術」という話を聞いたことと、一緒に仕事をしているデザイナーさんからInterface Builderを覚えたいという話があったため、トライしてみよう、ということになりました。

みんなの投稿画面

今回対象にした「みんなの投稿」は以下のような画面です。

f:id:masarusanjp:20161226102904p:plain:w320

1つのタブの中身はUITableViewを使ったリストで、リスト中の1セルは高さも固定、表示する要素も条件で出たり消えたりしない、という画面設計になっています。
Interface Builderを使った最初のトライにはこのようなシンプルな画面が適してると考え、これを選択しました。

以下、実際に機能が取り込まれるまでに行ったことを書いていきます。 「デザイナーにStoryboardをお任せする技術」に沿った内容となっています。

1. ViewControllerとView, Autolayoutのことを教える

まずは座学とペアプロでViewController, View, AutoLayoutの事をざっくりと学んでもらいました。
Interface Builderはグラフィカルで取っ付き易いツールだと思いますが、 iOSアプリケーションの構造を知らずに触っても、何が起こるのか想像がつかず、混乱するだろうと考えたからです。

事前に資料を渡し、ホワイトボードで図を交えて教え、ペアプログラミングをして実際に動かしてもらいました。

f:id:masarusanjp:20161226103058p:plain

いきなり全部を覚えて貰うのは、新しいことが多すぎるので難しいと考えて、これは2回に分けて行いました。
それぞれを1時間〜1時間半くらいで2日に分けました。

本題に入る前に補足をしますと、座学がたったの2日で合計3時間というのはかなりすんなりと行ったと思っています。 その背景として今回一緒にトライを行ったデザイナーさんは 普段からCookpad本体のRailsアプリケーションのCSSやHTMLに関しては自ら修正してGitHubでPRを投げており、ターミナルでの作業や開発フローに慣れている。 また、その開発を行う関係で「手元にXcodeなどの必要なツールキットも揃っている」というのがあると思います。

ViewControllerとView

初回ではViewControllerViewについて学んでもらいました。
大事な点は2つあると考えています。

  • iOSアプリケーションで、ViewControllerViewがどういう役割で、どう見えているのか
  • Interface Builder上ではそれらはどう見えているのか

1つめについては、View Controller Programming Guide for iOSに十分な内容があり、こちらを利用して説明を行いました。
具体的にはiOSのアプリケーションは1つの画面に最低でも1つのViewControllerがあり、複数のViewViewControllerの持つViewの上に配置されることで、画面を構成している、というような内容です。

2つめについては、1つめの点を踏まえてペアプログラミングをしながら実際に手を動かして学んでもらいました。 題材はMaster-Detail Applicationにしています。初期状態で画面遷移があり、1つの画面に最低でも1つのViewControllerというのが動かしながら見せられるためです。

AutoLayout

次にAutoLayoutについてです。 AutoLayout制約をViewに与えることでレイアウトを決める、Appleの機構です。 ここでの大事な点は以下3点だと考えています。

  • AutoLayoutのレイアウトのための考え方
  • Interface Builder上での設定の仕方
  • Interface Builder上で発生し得るエラーとその対処方法

1つ目のAutoLayoutの考え方については、Auto Layout ガイドAutolayoutの考え方のページに詳しく書かれています。
ホワイトボードで図や式を書きながら、AutoLayoutの制約に基づいたレイアウトの考え方を教えます。「デザイナーにStoryboardをお任せする技術」でもお話がありますが、数式や図を交えて説明をすると、すんなりと腹落ちをしてくれていました。

2つ目はInterface Builder上での制約の設定の仕方です
急に粒度の細かな話しになりますが、大事だと考えています。Interface Builder上での制約の設定は結構クセがあって混乱の元になるからです。(例えば、8px空けていたところを12pxにしたいと考えて、pinを選択して12と入れると、同じ属性に対する制約が複数出来てエラーになる、というのはありがちでわかりにくいミスだと思います)
ここからは、ペアプログラミングをしながら、実際のツール上で触って設定の仕方を教えています。

3つ目は、Interface Builder上で発生するAutoLayoutに関するエラーがどういうケースで発生するのかとその対応について教えました。
エラーの場合は、AutoLayoutが座標を決められない状態なので、エラーの内容を見て決められるように制約を設定しようとか、ワーニングは単に制約とInterface Builder上の位置がズレているので、制約か配置のどちらかを合わせてあげれば問題ないよ、といった内容です。 Interface Builderで制約を設定する過程でどうしてもエラーが発生する事があり、それぞれの対応方法を教えておくと、デザイナーが作業するときに、エラーに悩まされて不安になることがなく、良いと思います。

2. 実装

座学が終わったので実装に入ってもらいました。

エンジニア側で「あとはInterface Builderでレイアウトの調整をすればmasterにPRが出せる」という状態にまでして、 GitHubのissueにどのファイルを編集する必要があるのか書いて受け渡しています。

f:id:masarusanjp:20161226103110p:plain

ファイルの受け渡しはFeatureブランチを切っておいて、それをpullしてもらう形です。 ファイルの内容の細かなところは口頭やSlackで説明しています。

渡したときには画面はこんな状態でした。

f:id:masarusanjp:20161226102916p:plain:w320

これをSlackや口頭で相談を受けながら整えていってもらいます。

f:id:masarusanjp:20161226105553p:plain

3. PRを送ってもらう

整ったところで、FeatureブランチにPRを投げ貰いました。

f:id:masarusanjp:20161226102938p:plain

xibのレビューも含めて行います。例えば、misplacedが残っていないか、下位互換を考慮すると厳しいものが入っていないか等。 一通りの修正をしてもらい、終わったところで、Featureブランチにマージします。 その後、エンジニアがFeatureブランチからmasterにPRを出し他のエンジニアのレビューも受けてmasterにマージしました。

f:id:masarusanjp:20161226102951p:plain

効果

今まで終盤で起こりがちだった、「iPhone6では良いのだけど、iPhone5や4sではレイアウトを調整をしたほうが良い」というケースがあります。 ここに細かくデザイナーとエンジニアの時間を細かく使ってしまっていました。
しかし、今回はデザイナー自身がシミュレータで確認をしながら調整をしてくれたので、この問題が解消されました。

今回のケースは「iPhone5の場合にサムネイルが大きすぎる」という問題でした。
左のレシピ作者のアイコンを少し小さくする等の調整はAutoLayoutの制約を利用して、デザイナーさん自信が調整をしてくれています。
(※ セルの高さのみコードで調整しています)

Before After
f:id:masarusanjp:20161226103043p:plain:w240f:id:masarusanjp:20161226103052p:plain:w240

感想と振り返り

実施後、KPTを用いて振り返りを行い、以下のことがでてきました。

エンジニア

エンジニアとしては、まず問題が解決出来たことも良かったと考えいます。 また自身のAutoLayoutへの知見も深まりました。leadingleftの違いや、equal以外の関係性の使い所等。ついコードを書いて解決してしまっていたところを、一緒にAutoLayoutの制約でなんとか出来ないかを考えて解決できたのは、良い経験でした。

デザイナー

みんなの投稿のデザイン調整を行うことができ、Interface Builderを触れるようになりました。 目的が、リソースがいっぱいいっぱいのエンジニアの細かいデザイン調整の時間を減らすことだったのでそれを達成できたのは大きかったです。逐一エンジニアの席にいって「あと数ptずらしてください…あっやっぱり戻してください…」のような時間がかかるコミュニケーションが無くなり自分自身で調整して、シミュレーターで確認できたのは大きかったです。Interface Builderを使いこなせてないので、今後もInterface Builderが使える場面では積極的に触っていこうと思います。

課題

まだ簡単な画面をトライしただけなので「他の画面ではどうなのか」「結局のところデザイン確認がコードレビューに逆転しただけになってしまっているのでは」など、考えられる課題はたくさんありそうです。

また、デザイナーにInterface Builderを触ってもらうのでなく、エンジニアがSketchを受け取ってレイアウトを調整する取り組みもありそうです。 実際にフリルさんでは、その取り組みについて記事が書かれています。(フリルのiOSアプリ開発におけるエンジニアとデザイナーの作業分担について)

Sympliのような、SketchのデータをInterface Builderへいい感じに取り込んでくれるプラグインも出てきており、試してみたいです。

どの手段を取るにせよ、大切なことはエンジニアとデザイナーがお互いを尊重して歩み寄っていく姿勢だと私は考えています。

まとめ

今回のトライでは、目論見通りエンジニアもデザイナーも双方で細かな調整と確認で時間が取られることがなく、終盤の細かなデザインの修正が行えて良かったのではないかと考えています。

アプリを開発をする上での1つの事例として参考になれば幸いです。

Swiftプロジェクトのビルド時間を計測・改善するxcprofilerを作った話

$
0
0

技術部モバイル基盤グループの@です。

我々のチームでは、iOS/Androidアプリの認証、決済、ロギングと言った基幹部分の開発のほか、各事業部のモバイルエンジニアの開発効率を上げるための業務改善を日々行っています。

その一環として、さまざまなモバイル開発上の指標を収集・監視し、問題の発見や、施策への効果計測に利用できるようにしています。 例として、iOS/AndroidのCIの実行時間や、開発期間中のissueの量の変化、コード全体のSwift対応率などがあります。

f:id:gigi-net:20161228111203p:plain

f:id:gigi-net:20161228111221p:plain

収集したデータは、オープンソースのデータビジュアライゼーションツールであるGrafana上にダッシュボードを作成し、監視しています。

この記事では、iOS版クックパッドアプリでビルド時間を計測、改善をした事例についてご紹介します。

コマンドごとの実行時間の計測

まず、CIサーバーで実行されている各Shellコマンドについて、コマンドごとの実行時間を調べ、合計時間の経過を監視するようにしました。

しかし、コマンドごとに見てみると、この計測方法では、xcodebuildに大部分の時間がかかっていることが判明し、これだけでは具体的な改善が難しい状況でした。

f:id:gigi-net:20161228111238p:plain

ビルド時間の内訳について詳細に把握する方法はないのでしょうか。

コンパイラフラグによるビルド時間の計測

この問題はXcode標準のデバッグ機能を利用することで解決します。

Xcodeプロジェクトに以下のコンパイラフラグを追加すると、それぞれのメソッドについてのビルド時間を計測してくれるようになります。

-Xfrontend -debug-time-function-bodies

f:id:gigi-net:20161228111139p:plain

しかし、結果はプレーンテキストとして出力され、閲覧、収集のしづらいデータフォーマットでした。

また、このログを整形して表示してくれるGUIツールはありましたが、CI上で実行することができず、運用が難しい問題もありました。

xcprofiler

そこで、Swiftコードのビルド時間をメソッド単位で計測して様々な形式で出力するユーティリティ「xcprofiler」を開発しました。

xcprofilerはRubyGemsから導入できます。

gem install xcprofiler

xcprofilerは最新のビルドログを自動で取得し、整形して表示してくれるCLIを提供します。

例えば、以下はCookpadのiOSアプリで実行した例です。

$ xcprofiler Cookpad -l 10
+-------------------------------------------------------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+
| File                                                  | Line | Method name                                                                                                                                                   | Time(ms) |
+-------------------------------------------------------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift              | 9    | final didSet {}                                                                                                                                               | 5003.8   |
| xxxxxxxxx.swift                                       | 44   | init(title: String?, message: String?, content: Content?, actions: [AlertAction])                                                                             | 143.8    |
| xxxxxxxxxxxxxxxxxxxxxxxxx.swift                       | 11   | func cellHeightFromConstraints<CellType : UITableViewCell>(tableView: UITableView, createFromNib: Bool = default, cellUpdater: ((CellType) -> ())) -> CGFloat | 117.0    |
| xxxxxxxxxxxxxxxxxxxxx.swift                           | 55   | @objc func myFolderDidRemoveRecipeNotification(_ notification: Notification)                                                                                  | 115.9    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift                    | 136  | private func assets(in rects: [CGRect]) -> [PHAsset]?                                                                                                         | 115.4    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift | 4    | private static func makeInconsistentNotificationSettingAlert() -> AlertViewController                                                                         | 114.0    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift           | 5    | @objc(showFromViewController:completion:) static func show(from viewController: UIViewController, completion: @escaping (Bool) -> Void)                       | 107.9    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift                 | 8    | @objc override init(frame: CGRect)                                                                                                                            | 102.5    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift                 | 31   | private final func configureAppearance()                                                                                                                      | 102.1    |
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.swift           | 7    | private static func makeAlertController() -> AlertViewController                                                                                              | 102.1    |
+-------------------------------------------------------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+

内部では、Xcodeの出力したビルドログを取得、デコードし、ビルド時間についての出力のみを抽出しています。

カスタムレポーターを実装して、メトリクスを監視する

xcprofilerにはReporterという仕組みがあり、独自のReporterを実装することで、解析結果を様々な方法で出力することができます。

今回は、実行結果をGrafana上で閲覧するために、データソースであるinfluxdb向けのReporterを実装し、最新の実行結果をテーブルにしてみました。

#!/usr/bin/env rubyrequire'xcprofiler'require'influxdb'includeXcprofilerINFLUX_DB_TABLE_NAME = 'table_name'classInfluxDBReporter< AbstractReporterdefreport!(executions)
    client ||= InfluxDB::Client.new(
      ENV['INFLUXDB_DB_NAME'],
      host: ENV['INFLUXDB_HOST'],
      port: ENV['INFLUXDB_PORT'],
      username: ENV['INFLUXDB_USERNAME'],
      password: ENV['INFLUXDB_PASSWORD'],
    )

    payload = executions.map { |e|
      key = "#{e.filename}:#{e.line}:#{e.column}#{e.method_name}"Hash[*[key, e.time]]
    }.reduce(&:merge)
    client.write_point(INFLUX_DB_TABLE_NAME, {values: payload})
  endend

profiler = Profiler.by_product_name('Cookpad')
profiler.reporters = [
  StandardOutputReporter.new(limit: 20, order: :time),
  InfluxDBReporter.new(limit: 20),
]
profiler.report!

このようなスクリプトを実装し、CI上から実行すれば、最新のビルド時間をGrafana上で定常的に監視することができます。

f:id:gigi-net:20161228111315p:plain

Swiftのビルド時間を高速化する

では実際に、これらのデータを元にSwiftコードのビルド時間をカイゼンしてみましょう。

一番遅いメソッドは、ビルドに5003ms、約5秒もの時間がかかっています。 この値はCPU時間で、実時間ではないのでしょうが、他のメソッドと比べると特異的に遅いヶ所であることがわかります。

該当箇所のコードを実際に見てみましょう。

// 日付ラベルを表示するために、日付の区切りとなる写真のインデックスを求める// (例) [photo(10月30日), photo(10月29日), photo(10月29日), photo(10月28日), photo(10月28日), photo(10月27日)]// という配列の場合、日付の区切りとなるインデックスは [0, 1, 3, 5]letindicesShowDateLabel= [0] + photos.reduce([]) { (photosGroupedByDate, photo) ->[[Photo]] invarresult= photosGroupedByDate
    ifletpreviousPhotoDate= photosGroupedByDate.last?.last?.cookedDate, calendar.isDate(photo.date, inSameDayAs:previousPhotoDate) {
        result[result.count -1].append(photo)
    } else {
        result.append([photo])
    }
    return result
}
.map { $0.count }
.reduce([]) { (result, element) ->[Int]inreturn result + [(result.last ??0) + element]
}
.dropLast()

このビルド時間の遅さは、このように大量のメソッドチェイニングが行われていることにより、型推論が複雑になっていることが原因だと推測できます。

試しに、メソッドチェイニングをやめ、適宜テンポラリな変数に格納し、型情報を与えてみました。

// 型を明示するletgroupedPhotos:[[Photo]] = photos.reduce([]) { (photosGroupedByDate, photo) ->[[Photo]] invarresult= photosGroupedByDate
    ifletpreviousPhotoDate= photosGroupedByDate.last?.last?.date, calendar.isDate(photo.date, inSameDayAs:previousPhotoDate) {
        result[result.count -1].append(photo)
    } else {
        result.append([photo])
    }
    return result
}
letcounts:[Int]= groupedPhotos.map { $0.count }
indicesShowDateLabel = [0] + counts.reduce([]) { (result, element) ->[Int]inreturn result + [(result.last ??0) + element]
}
.dropLast()

わずかこれだけの変更でビルド時間が5003msから152msに、 つまり 97%も短縮することができました。 この値は、複数の開発者が毎日アプリをビルドし続けることを考えると大きな差と言えます。

このように現在のSwiftコンパイラによる型推論は、特定の場合においてビルド時間に多大な影響を与えてしまうことが見て取れます。 コードの簡潔さを取るか、ビルド時間を取るかはトレードオフであり、今後の課題と言えそうです。

まとめ

今回開発したxcprofilerは、どのプロジェクトでも簡単に利用でき、ワンショットで実行してみるほか、定常的な監視にも利用することができます。 ぜひご自分のプロジェクトに導入してみてください。Pull Requestもお待ちしております。

クックパッドのモバイル基盤グループでは、開発者のための問題解決が好きなモバイルエンジニアを募集しています。

iOS/Android アプリエンジニア | クックパッド株式会社 採用情報

Cookpad TechConf 2017 はライブ配信も行います!

$
0
0

f:id:Yoshiori:20170118132915j:plain

こんにちは! @yoshioriです。

こちらで告知した「Cookpad TechConf 2017」 ですが、ライブ配信することが決定しました!!!

残念ながら抽選から漏れてしまった方々、エントリーに間に合わなかった方々、また、今この記事を見て知った方々、ご安心ください!

僕自身も色々なカンファレンスに参加、もしくはライブ配信でみたりしますが、Twitter などのハシュタグを追いかけつつ見るのが一番楽しいと思っています。ですので、会場に来られない方も是非、ライブ配信で楽しんでいただければと思います。
ちなみにハッシュタグは #CookpadTechConfになります!

そして、肝心のライブ配信 URLはこちらです!
https://techconf.cookpad.com/2017/streaming

残り一週間を切って僕もドキドキしていますがどうぞお楽しみに!!! では!

Cookpad TechConf 2017 開催報告

$
0
0

f:id:Yoshiori:20170121154607j:plain

こんにちは! @yoshioriです。

先週の土曜日の 2017/01/21、技術系カンファレンスCookpad TechConf 2017を開催しました。
第二回となる今年は、ライブ配信・Wi-Fi 設置などの新しい取り組みやサプライズ発表など盛りだくさんでお届けしました。

この記事では色々なリンクをまとめたいと思います。

発表資料と動画

  1. Cookpad under a microscope by 成田 一生
  2. Go Global by 滝口 健太郎
  3. Building infrastructure for our global service by sorah
  4. サービス開発におけるデザインの取り組み方 by 若月 啓聡
  5. モバイルアプリのABテスト by 加藤 龍
  6. チームでプロダクト開発をするための取り組み by 丸山 亮
  7. よりよい検索体験の為の情報設計とプロトタイピング by 須藤 耕平
  8. 組織全体でGitHubを使うようになるまで by 長 俊祐
  9. 快適なサービス開発を支える技術 by 国分 崇志
  10. Real World Machine Learning by 染谷 悠一郎
  11. 行動ログでプロダクトを改善するには by 兼山 元太
  12. Cookpad awakens by 庄司 嘉織
  13. Spot Instances in Cookpad by 荒井 良太

記事やブログ等

笹田耕一入社について

「Go Global」 の発表内容について

「組織全体でGitHubを使うようになるまで」の発表内容について

参加者の方々の感想

togetter

* 僕が見つけたのを集めただけなので漏れがあるかもしれません><(新しい記事など見つけたら@yoshioriまで連絡いただければと思います)

最後に

今年もなんとか無事に開催することが出来ました。
お楽しみいただけていたら幸いです。
引き続き技術的な挑戦をドンドンして、来年の Cookpad TechConf 2018 で発表できるようにするつもりです!来年もぜひお楽しみに!!!

Viewing all 802 articles
Browse latest View live