巨大なWEBアプリケーションに巨大な変更を取り入れるためにやったこと

会員事業部ユーザー基盤チームエンジニアの井口(@iguchi1124)です。

ユーザー基盤チームでは、クックパッドのサービス開発者のあらゆる要望に答え続けられるような『柔軟でいい感じのユーザー基盤』を目指し、サービス開発者およびユーザーさんの課題と向き合いながら日々開発を進めています。

第一弾として、普段の開発の様子や一部のユーザーさんに向けてユーザー登録機能をリリースするまでの話も公開されていますので是非そちらもご覧いただければと思います。

今回は、上述の記事にも触れられているようにクックパッドでユーザーさんのアカウント登録や認証情報として電話番号を利用できるようになりましたので、そのためにやってきたことの一部をご紹介したいと思います。

一口に電話番号を利用出来るようになったと言うと簡単そうに聞こえますが実際にはそうでもありません。

クックパッドではこれまで連絡先情報あるいはアカウントの認証に必要な情報としてメールアドレスを使うという前提で長い期間に渡る開発が積み重なってきました。

その状況から、電話番号をメールアドレスと同等に連絡先情報やアカウントの認証に必要な情報として利用するには多くの技術的負債の返却や機能追加が必要になります。

また、ユーザーさんに与える影響を考えると、ユーザーさんに迷惑をかけないようにリリースする順序を考慮したり、関連するサービスでデプロイするタイミングを合わせることも必要になったりします。

このような巨大な変更を取り入れようとしている最中も、並行してクックパッドのサービス開発は継続的に行われています。様々な施策を止めるわけにはいきません。

サービスを「ユーザーさんが一通り触れる」単位で分割する

ユーザー基盤チームでは、ユーザー登録およびログインに関わるサービス内の一連の動きを垂直に分割した、社内ではShishamo(ししゃも)と呼ばれるマイクロサービスに分離することで開発を加速させています。

サービスを分割することで実際に得られた利点は次のようなものです。

  • チームの外の開発者達にコードの変更による影響を与えたり、受けたりしない
  • 自動テストを高速に実行できる
  • 新しいアーキテクチャを素早く取り入れることができる
    • Dockerを利用したナウいデプロイフローを取り入れる
    • Webpackerを利用してナウいjavascript開発環境を取り入れる
      • React.js を導入して再利用性の高いプレゼンテーショナルコンポーネントを設計してみる
    • 電話番号パーサーを導入してみる

また、サービスを水平に分割し地層を積み上げるのではなく垂直に分割することで、新しい技術要素を取り入れる場面では早い段階で技術スタックを試し、技術的に実現可能であることの裏取りができます。

巨大な開発ブランチを作らない

提供したい機能の内容によっては、それぞれの機能の依存関係から変更を同時にリリースしなければならないことがあります。

そういった場合、開発ブランチを作り水面下で作業をすすめ、変更内容が揃った段階でマージし、リリースするということになるかと思います。

しかし、それではリリースするためには負担が開発者だけに留まらず広がってしまうことが想像できます。

  • 開発者の負担
    • 他の開発者との変更の衝突
    • 他の開発者や自分の変更の影響による予想外のバグの発生
  • コードレビューにかかる負担
    • 「よさそうだけど自信がない」、「自信がないけどLGTM」の発生
  • リリース前の動作確認、リリース後の監視にかかる負担
    • テストする必要があるパターン数の肥大化
    • 動作確認の結果おかしそうな動きを直したら別の挙動がおかしくなったので再修正、再確認、再修正、再確認

ユーザー基盤チームでは、Cookieベースのfeature flagを導入することで、この問題の解消に取り組みました。

これによって、ユーザーさんには機能を提供しないサイレントリリースとスタッフによる動作確認を可能にし、最小単位での変更のマージ、デプロイとテストを繰り返すことができました。

実際に、最後のユーザーさんに届けるステップに入る頃には十分にテストされたサービスのうちの、if分岐を取り除く程度のものにできます。

非常に簡単な仕組みではありますがOSSとして公開しています。

https://github.com/iguchi1124/cookie_flag

以下に電話番号でユーザー登録する機能をリリースするために実際に運用した例を紹介します。

ログイン機能の実装の中で、電話番号によるユーザー登録機能がリリースされているときの動きを実装する場合

class SessionsController < ApplicationController
  def create
    if feature_available?(:phone_number_registration)
      # 電話番号またはメールアドレスとパスワードを利用したユーザー認証処理
    else
      # メールアドレスとパスワードを利用したユーザー認証処理
    end
  end
end

電話番号によるユーザー登録機能がリリースされると表示されるリンク

<% if feature_available?(:phone_number_registration) %>
  <%= link_to "電話番号でユーザー登録する", new_phone_number_registrations_path %>
<% end %>

「電話番号によるユーザー登録をしようとしたこと」リソースにfeature flagを適用したい場合

class PhoneNumberRegistrationsController < ApplicationController
  feature :phone_number_registration

  def new
  end

  def create
  end
end

動作確認をするときは feature_available?(:phone_number_registration) が真になるように手動でブラウザのクッキーを設定することで一般のユーザーさんが利用する前に社内のスタッフが機能を試せるようになります。

まとめ

この記事では継続的にサービス開発が行われているクックパッドで巨大な変更を入れるためにやったことのうち、以下の2つのことを紹介しました。

  • 垂直分割によるサービス開発の効率化
  • フィーチャーフラグ導入によるリリースにかかる負担の改善

ユーザー基盤チームでは大きなサービスの基盤を再構築するにあたり、イテレティブかつインクリメンタルに価値を届けることを心がけながら周囲の開発者と協力してサービスの改善に取り組んでいます。

まだまだ失敗も多い道半ばですが、今後もユーザーさんや、となりで働く開発者、そして一緒にサービスを運営している全員にとってよいものである基盤づくりをしていけたらと思います。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://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('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/