モバイルアプリ開発における思いと工夫

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

3月18日にProductivity Engineering − Forkwell Meetup #4において、「クックパッドにおけるモバイルアプリ開発の工夫」というタイトルで発表しました。その内容を補完しつつ、最近のモバイルアプリ開発の取り組みについて紹介します。 (発表資料はこちら)

開発体制とスケジュール

クックパッドでは、Web/モバイルアプリなどのプラットフォームに依らず、機能ごとにチームを組んで開発を行っています。 例えば、検索機能、投稿機能といったサービス内の機能ごとにチームがあり、その中にデザイナーとエンジニアが所属しています。

モバイルアプリの開発という視点から見たときには、機能ごとのチームを横断し、より基盤的な業務を担うメンバーを含めて、iOS/Androidのモバイルアプリの開発を目的とするコミュニティがあります。(会社の組織編成と対応しない枠のため、ここではコミュニティと呼びます。) このコミュニティには、機能ごとのチームからディレクター、デザイナー、エンジニアが参加します。 ほかにも基盤的な業務を担うメンバーとして、モバイル基盤グループと品質向上グループのメンバーも参加しています。

アプリのリリースは、通常2週間から1ヶ月に1回行っています。リリーススケジュールを調整することはあるものの、スケジュールにあわせて機能を入れるかどうかを決める方法をとっています。 リリースに関わるエンジニアは、Android/iOSのリリースでそれぞれ10人程度です。

サービス開発で大切にしていること

私たちが開発で大切にしていることを、サービスを提供する立場と、ユーザー体験の2つの面から述べます。

まず、サービスを提供する立場からの視点です。

私たちのサービスでは、「毎日の料理を楽しみにする」ことに貢献するかどうかを重視します。 そのために「○○によって、毎日の料理が楽しみになる」といった仮説をたて、その正しさを明らかにする必要があります。 仮説は、サービスの変更や機能追加を含む施策として実装されます。 ユーザーさんに使われる様子をデータなどから検証することで、その施策に効果があったのか/なかったのかを明かにし、ひいては仮説の正しさを明らかにします。

施策の効果を計測するには、それが仮説どおりに実装されている前提で、その効果を計測します。 そもそも仮説どおりに実装されていなければ、効果の計測に意味がありません。 仮説どおりに実装されたときでも、例えば、実装された機能に到達できないことがあれば効果を計測できません。アプリの外側で、APIサーバーに問題があるなどして機能を提供できなければ、これもまた計測ができません。

したがって、施策を仮説どおりに実装していること、その効果を計測できる状態を維持することが大切になります。

次に、ユーザー体験の面から見てみましょう。

クックパッドはコミュニティサービスなので、ユーザーが離れていってしまうことはサービスにとっての損失になります。ユーザーが離れていってしまう体験とはどんなものでしょうか。 ユーザーにとって残念な体験が続くと、離れていってしまうことが考えられます。 例えば、操作性を損ってしまうクラッシュの問題があります。 他にも、施策の意図にないが、これまでできていた(当たり前になっていた)ことができなくなった。画面ごとにいろいろな操作があって、覚えづらく、操作に慣れることができない。といったことにも、私たちは注意しています。これらは、ご意見やレビューといったユーザーの反応をもとに、施策として意図しないネガティブな反応が増えることを「ユーザーにとって残念」と捉えているためです。

このようなことから、私たちは、体験の一貫性というものを大切にしています。

リリースフローにあわせたチェックポイント

私たちのモバイルアプリ開発ではリリースフローを定めて運用しています。 このリリースフローを、必要に応じて変更しながら運用しています。現在は、以前の記事では触れていなかった、チェックポイントとなるイベントをいくつか置いています。

今回は、リリースフローに定めたイベントの中から、2つを紹介します。 先に述べた開発において大切にしていることを損なわないための、キックオフと確認会です。

キックオフ

キックオフは情報共有の場としてはじまりました。

以前は、毎日、始業後に10分程度の時間をとって、その日のタスクを共有していました。 顔をあわせ、タスクを共有し、開発の状況を把握するための会です。これを朝会と呼んでいました。 しかし、朝会参加者が増えてきたことと、全社的なフルフレックスへの移行に伴って時間をあわせるコストが増したことから、チャットツールであるSlack上で行うようになりました。

朝会チャットは、専用のチャンネル(部屋)を用意し、毎日、各自のタスクを書き込む形式をとっています。 チャットというツールの性質上、非同期に行われます。そのため、議論に発展した場合も朝会自体の流れを阻害しないというメリットがあります。 一方で、目的が「今日のタスクを共有する」というところに絞られる傾向があり、大元の施策のはなしや、ちょっと未来のはなしといった関連情報を共有することが減ってきました。また、非同期であるがゆえに、他者の書き込みへの関心が薄れやすい傾向にあるようです。 他にも、顔をあわせることで得ていた、顔色や声色から調子の悪さを知る、ということがむつかしくなりました。

全体的に他チームの施策への関心が薄くなった結果、ひとつの問題が発生しました。

ひとつの画面に、異なるチームから複数の施策が実装されました。その結果、同時に表示されたことにより表示がくずれ、効果の計測ができない状態になってしまったのです。

情報の共有ができていなかった、実装前にこの事例のような競合は把握したい、という反省のもと、キックオフがはじまりました。

キックオフは、そのリリースに関係するディレクター、デザイナー、エンジニアすべて(最近では20人程度)が参加します。(参加は任意なので、毎回全員が参加するわけではありません。)

画面や操作に大きな変更がある施策を共有し、必要であればその調整を行います。すべての変更を共有するのではなく、大きな変更があるものを選んで共有を行っています。

施策を共有し、事前調整をすることで、先のような失敗は起きなくなりました。 また、実装前に他チームの施策を知ることで、それへの懸念を伝える機会となっています。仕様の未定義が見つかることもあり、うまく機能しているといえるでしょう。

確認会

確認会は、そのリリースに関わるディレクター、デザイナー、エンジニアすべてが参加します。 すべての変更をマージしたアプリをインストールし、変更内容を確認しながら操作します。

ここでは、全体の一貫性が保たれているか、意図どおりに実装できているかを確認します。 いわゆるリグレッションテストとは違い、過去の実装範囲への影響よりは、新たに実装したものに注目して確認を行います。

確認回は、ユーザーインターフェース(以下UI)とユーザーエクスペリエンス(以下UX)の一貫性を保ちたい、という要求からはじまりました。これは、各チームが独立して施策を実装するので、アプリ全体をみたときにUI/UXの一貫性を失いやすいためです。

あわせて、確認回では、検証端末を持ち寄り、開発中より多くの種類の端末で操作を行うようにしています。これは、開発中に動作を確認する端末が偏りやすいことから、端末依存の問題の検出を目的にしています。

これらの取り組みにより、UI/UXのばらつき、特定端末での問題といったものが見つけやすくなりました。 また、開発中には気づかなかった違和感を見つけることもあります。 見つかった問題は、重要度に応じて修正を行います。ものによっては、次のリリースで対応する場合もあります。

確認会を設定した目的は達成されつつありますが、それだけで開発がうまくいくわけではありません。

例えば、特定の種類のユーザーでしか起きない問題が、確認会以降で見つかるという問題があります。 実はクックパッドのユーザーは状態の種類が多く、無料ユーザー/課金ユーザーというわかりやすい物の他にもいくつか存在します。これらはサーバー側でユーザーを用意するだけでは、網羅することがむつかしく、状態の変化も含めて確認すべきバリエーションが多いという状態です。 状態の掛け合わせで起きるような問題ではない、特定条件のユーザーを模倣すると比較的簡単に見つけることができる問題なので、もっと早く見つけられるようになりたいと思っています。

まとめ

モバイルアプリ開発をうまく回すための、私たちの工夫の一部として、キックオフと確認会について紹介しました。

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

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/