もう失敗しない!プロジェクト書きなおして、最高の開発環境を手に入れる

ちくしょう、プロジェクトまるごと書き直したい

自分で作り始めたプロジェクトであっても、途中から相乗りしたプロジェクトであっても、誰もが一度は体験する気持ちではないでしょうか。

私が携わっている「おいしい健康」も例外ではありません。

プロジェクトを全て書き直したいと思う一方で、全てのコードを書き換えようとするアプローチは、これまで作ってきた見た目や機能、ビジネスロジックを再現しきれずに潰えてしまう。という話しも良く聞きます。

実は全て書き直したいのではなく、その大きな目的としては以下のようなモノがあるのではないでしょうか。

  • シンプルな作りに変えたい
  • 不要なコードが重なり、動作が遅くなっているところを解消したい
  • 新しいライブラリ、新しい技術を取り込めるようにしたい

今回は、このような目的を達成しつつ、プロジェクトの書き換えを行った私達の話をしたいと思います。

cookpad本体からのプロジェクト分離

cookpad と おいしい健康

「おいしい健康」はクックパッドの新規事業の1つですが、cookpad のコードべースの中に名前空間を分け、コードとしては本体事業と同居して開発を進めていました。

cookpad は他の記事でも紹介されている通り、非常に大きなRailsのプロジェクトです。

参考) The Recipe for the World's Largest Rails Monolith

このコードベースに乗って開発をすることで、社内にある便利な仕組みを手軽に使ったり、デプロイやエラーの監視などを含め、まるっと cookpad のプロダクトに背中を預けることができます。

プロダクトをゼロから作り始める時、事前準備をさほどせずとも作り始められるところがメリットでもあります。

cookpad のコードベースに同居する不都合

作り始めはそれでよかったのですが、だんだんとプロダクトが成長していくとともに、cookpad のコードベースに乗っていることによるデメリットが目立ち始めました。

  • コードをマージしてから、テスト&デプロイまでに時間がかかる
    • 変更したコードは少しでも、CIでは2万以上ものテストが実行される
  • デプロイできる時間が決まっている
    • 新規事業的にはユーザーに影響が起きなければ、どんな時でもすぐに価値を届けたい
  • Rails の起動やspec を動かすのも時間が時間がかかる
    • gem 読み込みやモンキーパッチなどが多数ある

コードベースを分けることだけにフォーカスする

先ほどのデメリットを全て解決するには、新しく書き直し。。と判断してしまいそうですが、あまりに時間がかかりすぎてしまいます。

事業スピードを止めず、かつ、2つ目までの課題をクリアするために、私達は1年半ほど前にコードベースの分離を行いました。

この分離作業では、cookpad のコードベースをforkし、Model/View/Controller, assets, routing に関して「おいしい健康」関連のコードのみを残す。ということを実施しています。

f:id:tanukiti1987:20151209170557p:plain

コードベースを分けただけの作業がメインではありますが、テストやデプロイのフローが、cookpadとは別になったことにより、「おいしい健康」だけのテスト実行およびデプロイが行えるようになりました。

プロジェクト分離での学び

一部課題は残してしまうものの、一番の課題であるデプロイ周りの課題が解決されました。

また、この作業自体は1人のエンジニアが1ヶ月程度で行えたものでした。期間的にも短く、「おいしい健康」に与えた変更も最小限ですみ、大きな混乱もなく、リリースすることができました。

レールに乗ったRailsアプリケーションへ

先ほどまでの話しで、デプロイ周りの課題は解決できたものの、整理しきれなかった多数のライブラリや、モンキーパッチにより、Railsを起動する時間、テストを実行する時間は長いままでした。

cookpad のコードベースを引き継いできたので、巨大なRailsアプリケーションを動かすために、レールから外れているところが目立ってきます。

  • Railsのバージョンをあげるとき、ライブラリやモンキーパッチの依存関係に激しく悩まされる
    • これまでは開発基盤の人たちの作業に乗れていたが、今はそうではない
  • Railsの起動、RSpec の実行が(普通のRailsプロジェクトと比べると)遅い
    • 消しきれなかった使用していないライブラリが多数存在しいてる
    • 高速化の対策もあったが、cookpad本体に当てられるものが(当然)ほとんど
  • 新しいイケてるライブラリを見つけても導入が難しい

これらを解決することによる事業的なメリットは、短期的には見出しにくいものではありますが、長期的に見ると開発効率は格段に向上し、確実にプラスになります。

第一弾の分離作業により時間が経ち、「おいしい健康」のプロダクトをより成長させようとするほど、いよいよレールに乗ったRailsへと書き換える機運が高まってきます。

着手が遅くなればなるほど、書き換えも難しくなり、レガシーを引きずったまま開発することになることは目に見えているからです。

フルスクラッチでは書き直さない

この段階で成し遂げたいことは以下の3つでした。

  • Rail に乗ったRailsプロジェクトにすること
  • 使用していないライブラリの削除
  • モンキーパッチを出来る限り無くすこと

この3つだけを念頭に、作業を進めていきます。

ロジックは基本的に全て移動するだけ

Rails のプロジェクトを作るときに叩く rails new のコマンドを叩き、愚直にModel からロジックを移していき、既存のテストが通るように修正していきます。

f:id:tanukiti1987:20151209170645p:plain

途中、クラスを分割したくなったり、リファクタリングをしたくなったりしても、まずはグッと堪え、ロジックの移動&テストを通過させることに集中します。 あまりにもコードの最適化に脱線してしまうと、最後まで書き換えを完遂せずにプロジェクトが頓挫してしまうことを防ぐためです。

もちろん、時折の息抜きは必要です。 気分転換がてら rspec にて machinist を使っていた所を全て factroy_girl に書き換えたりもしました :-)

新しく生まれ変わるプロジェクトを愛でるキッカケにもなるので、ほんのりと脱線することはオススメでもあります。

使用していないライブラリの削除、モンキーパッチを出来る限り無くすこと

どうしても、保守的に残してしまいがちなモンキーパッチやライブラリですが、まず一度全て消してしまいます。

specを動かしたり、実際にviewを renderさせたときにエラーがでるようであれば、ライブラリを追加していきます。

合わせて、最新のライブラリでも動作確認を行い、極力新しいものを使うよう心掛けます。

モンキーパッチは、積極的に無くす方向に動き、難しければライブラリを探し、それでもダメなら、当てるという方向が良いかと思います。 実際、我々のプロジェクトに対して当てられていたモンキーパッチは古いものが多く、現在では他のライブラリを用いることで代用可能だったのもがほとんどでした。

書き直したプロジェクトを検証する

全てのロジックの移植が終わり、テストもAll Green となれば、いよいよ検証作業に入ります。

いかんせん、ほぼ書き直したプロジェクトなので、テストがGreenでもきちんと動くかは不安です。

今回、私達は kage を使用し、本番に流れてくるリクエストを裏で書き換えたRailsの動いているサーバーにも流すようにしました。

ユーザーには影響を与えないようにし、実際に頻繁にリクエストの来るところを中心に問題がないかを検証していきました。

検証期間としては、1週間程度を設けましたが、ほとんど大きなエラーも出ず、リリースへの決心がつきました。

安全にリリースする

いざ、リリースするときには、問題があったらすぐに巻き戻せるよう、稼働中のサーバーに加え、新しいプロジェクトのサーバーを用意してもらい、ELBで一度にアクセスの向き先を切り替えるようにしました。

実際の作業は事業部担当のエンジニアではなく、インフラ担当のエンジニアの方にやっていただきましたが、スムーズに切り替えが完了しました。

今回は特に問題なく、巻き戻しも行いませんでしたが、今後同じようなことがあれば、この方法をとるのが安心かなと思っています。

小規模にコードベースを書き直しするメリット

冒頭にも述べた通り、ヘルスケアチームでは2度に渡り、コードベースの書き直しを行ってきました。

どれも一度に全てを変えるものではなく、小規模に着実にリリースしていくような手法を取ってきました。

このような方法は事業部側ともメリット/デメリットが握りやすく、期間も短くて済みます。

それは、事業部としてもエンジニアとしてもモチベーションを保ちやすく、また効果も感じやすいものです。

他にも幾つかのメリットが今回の取り組みでわかってきました。

自分のプロジェクトの奥深い闇を理解できる

自分たちが普段触っているプロジェクトがどのように動いているのか、深く理解することができます。

余談ではありますが、 empty? メソッドと blank? メソッドがモンキーパッチにより同じ挙動をとるようになっていたのは、個人的にはすごく驚いたことの1つです。

また、普段は触らないミドルウェアについても触ることができ、今後の開発スピードを一段とアップさせる要因になったかと思っています。

モダンな開発手法に追従する気力がわいてくる

合わせて、プロジェクトの構造を知れれば、ライブラリは何を使うべきか。バージョンアップしてよいものか。も、ある程度察しがついてくるようになります。

Railsの新しいバージョンを試してみよう。 capybara-webkit ではなく poltergeist を使ってみよう。

などなど、フットワーク軽く、様々な技術的トライが行えるようになります。

これらは、チームに活力を与え、メンバー全体の技術力の向上にも役立つものかと思っています。

次回の書き直しも安心!

小さなものでも成功体験が得られれば、次回どういう風に進めればよいか、イメージは湧きやすくなります。

一度、プロジェクトをキレイにしたら、終わりではありません。おそらく1年後にはまた、イケてないポイントを見つけ、プロジェクトを書き直したくなることでしょう。

その時にも、前回、今回のトライは確実に生きてきます。

そうなってくれば、次回以降もコードベースの書き直しにトライすること、確実に成功させることにも価値が見いだせています。

終わりに

今後も定期的に、このような取り組みをやっていくつもりです。

本件の内容にかかわらず、クックパッドやおいしい健康の開発について、気になることなどあれば、今週末開催の RubyKaigi にも出席しておりますので、関口 までぜひお声がけください!

今回の記事が皆様のお役に立てることを願っています。

/* */ @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;*/ /*}*/