「新機能作成時に開発ブランチに細かくmergeしていく戦略」について社内勉強会で発表しました

はてなのアプリケーションエンジニアのid:shiba_yu36です。社内技術勉強会で「新機能作成時に開発ブランチに細かくmergeしていく戦略」という発表をしたので、資料を公開します。

speakerdeck.com


以下、簡単に文字でまとめておきます。

戦略

  • ユーザーに新機能が見えないようにする工夫をし、新機能のbranchもどんどん開発ブランチにmergeしていく
    • mergeされたものは随時本番にリリースされるが、ユーザーに見えない工夫をしているので問題なし
  • PRは可能な限り細かくする
  • 機能が完成したら最後にユーザーに新機能を見えるようなPRを作り、mergeしてリリースする


なぜこの戦略を使うのか

いろんな失敗を経験して、この戦略を最近使っている。

  • 失敗パターンその1: 巨大PRパターン
  • 失敗パターンその2: 新機能リリースブランチパターン

失敗パターンその1: 巨大PRパターン

新機能を1ブランチで作って一気にPRするパターン。以下のような困り事がある。

  • 超コンフリクト問題
  • レビューする観点が多すぎて見落としてしまう
    • バグの混入を防ぎにくくなる
  • 指摘が多くなりがちで、レビューされる側が疲弊する
  • レビュー依頼 -> レビュー完了 -> レビュー依頼 -> ...のサイクルが多くなり、レビューする側もレビューされる側も疲弊する
  • mergeしたところ他にmergeした箇所と競合して動かなくなったということが起こる
  • etc...

大体500行の変更を超えてきたあたりで辛くなる印象。

失敗パターンその2: 新機能リリースブランチパターン

新機能のリリース用ブランチを一つ用意して、そこにPRを送るパターン。PRは細かくレビューされ、リリース用ブランチへmergeするため、巨大PRパターンよりましに見える。

しかし、以下の困り事が起こる。

  • 最後のmergeしたところ動かなくなる問題が解消しない
  • 最後にリリースPRを全チェックしないとなんとなく安心できない
  • 他の機能で必要になったメソッドをcherry-pickしまくる

「他の機能で必要になったメソッドをcherry-pickしまくる」が一番つらい。例えば

  • feature1でfind_hoge_blogというメソッドを作る
  • feature2でも同じメソッドを使うことになった
  • find_hoge_blogを作ったcommitをcherry-pickする?
  • しかしそのcommitをcherry-pickするとそのメソッド以外のコードも紛れてくる
  • コピペするしかない???

のような状況が起こる場合がある。

これらの失敗から

これらの失敗から、最初の戦略を用いることになった。


ユーザーに新機能を見えないようにするパターン

最初に掲載した戦略を見ると、「ユーザーに新機能が見えないようにする工夫」という部分ができれば、細かくmergeすることが出来る。このような工夫のパターンは以下のようなものがある。

  • ロジックのメソッドから作る
  • ユーザーに見えない部分から作る
  • エンドポイントを本番では見えないようにする
  • 一部のHTMLだけ本番では隠しておく
  • リニューアルパターン

ロジックのメソッドから作る

  • Logic::Blog->find_public_blogsとか、Logic::Blog->createとかだけ作ってPRする
  • ロジックをユーザーに見えるControllerとかで使わなければ当然影響なし

ただし、次の点に注意。

  • ユースケースを分析しておかないと、実際に使う時に使えないことに気づくことがある
  • 使い方擬似コードを書きつつ、それも合わせてレビューしてもらうと良い

ユーザーに見えない部分から作る

例えば「MySQLのLIKE検索から、Elasticsearchに置き換えて自由なソートも利用可能に」という機能を作ることを考える。これをもう少し分割すると以下の4つになる。

  1. Elasticsearchの環境作り(手元、開発環境、本番)
  2. 検索したいデータのElasticsearchへの同期
  3. 検索クエリでの検索裏側をMySQLからElasticsearchに置き換え
  4. Elasticsearchを使ってソートを実装

このようにすると、3番まではユーザーに影響がないので、いつリリースしても問題ないので、細かくmergeできる。

エンドポイントを本番では見えないようにする

用途

新機能を特定のURLで提供する時に使える。

方法

  • 開発環境ではURLでその機能にアクセスできるように
  • 本番環境ではNot Foundにする
  • 単なるフラグだけで実装可能

コードのイメージは以下の通り。

# /feature1でアクセスできるエンドポイント(Controller)
sub feature1 {
    my ($req, $res) = @_;

    # 開発環境でのみONになるフラグ
    # フラグは環境変数を使ってもいいし、フレームワークの設定などを利用しても良い。
    # TODO(feature1_release): リリース前に消す
    if (!$ENV{CAN_SHOW_NEW_FEATURE}) {
        return $res->not_found;
    }

    ...
}

これでこのURLでアクセスする機能は細かくPRを作って開発できる。

最後に見えるようにするには

完成したら、開発環境でのみONになるフラグを使ったif文を消すだけでユーザーに公開できる。

一部のHTMLだけ本番では隠しておく

用途

あるページの一部に新機能追加する時に使える。

方法

テンプレートエンジンを使っていると簡単。フラグをテンプレートに渡して、特定HTMLを消したりするだけで実現可能。

...

[%- IF can_show_new_feature %]
  <section id="feature1">
    ...
  </section>
[%- END %]

...

もしこの要素に関係するJSを実装したい時も、要素のあるなしでJSを実行するかどうかを決めれば、JSも細かく作ることができる。

最後に見えるようにするには

完成したら、テンプレートのIF文を削除するだけでユーザーに公開できる。

リニューアルパターン

用途

新機能でページがガラッと変わり、特定HTMLを隠すとかでは対処不能な時に利用できる。

方法

  • そのページ用のコントローラとテンプレートのセットをもう1つ用意する
  • 新しいコントローラは本番では見えないように
  • 最後に前のやつとごそっと入れ替える

別URLでアクセスできるコントローラを作り

# /feature1_renewalでアクセスできるエンドポイント
sub feature1_renewal {
    my ($req, $res) = @_;

    # 開発環境でのみONになるフラグ
    # TODO(feature1_release): リリース前に消す
    if (!$ENV{CAN_SHOW_NEW_FEATURE}) {
        return $res->not_found;
    }

    ...
}

feature1_renewal.htmlのようにHTMLファイルも別で作る。

これで、新しいページはユーザーに見えることなく細かく実装できる。

最後に見えるようにするには

  • feature1_renewalメソッドをfeature1メソッドに改名し
  • feature1_renewal.htmlをfeature1.htmlに改名し
  • 対応するCSSやJSなどを移動する

ということをすれば、/feature1でアクセスできるページを一気にリニューアルできる。


まとめ

  • 新機能作成時に開発ブランチに細かくmergeしていく戦略について紹介した
  • ユーザーに見えないようにするパターンを5つ紹介した
  • 実際にはこの5つをうまく組み合わせることで、最後にユーザー向けにリリースするためのPRの変更量を最小にすることができる
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy