LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

サクッとレビューができる 小さなPull Requestを作るには

大きなPull Requestのレビューがつらい

転職ドラフトでWebアプリケーションエンジニアをしている @iwtn です。 この記事ではチーム開発では当たり前になったレビューにおいて、修正されたファイルがたくさんあるとつらいよね、というお話と、その解決策を提示してみたいと思います。

昨今のWebアプリケーションなどのチーム開発において、レビューはほぼ必須で行われていると思います。 私も業務の1割以上はレビューをしていると思います。 GitHubならPull Request、GitLabならMerge Request(転職ドラフトではGitHubを使っているので、この記事では以下「Pull Request」で統一します)の単位で、レビュアーに依頼するかと思います。

ただ、大きな機能を作っているときにやってしまいがちなのが、例えば修正ファイル数が50個を超えるような、大きなPull Requestを作ってのレビュー依頼です。 それによって、レビューにかなりの集中力が必要になり、結構な時間を取られてしまいます。 個人的にレビューの負荷というものは、そのPull Requestに含まれる修正量の2乗に比例するぐらいのイメージがあります。

修正ファイル数が多いこと自体が問題なのではない

修正ファイルが多くても、サクッとレビューが終わる場合があります。 それは、例えばある形式のファイルを一括で別の形式に変換する内容だったり、linterで新しいルールを入れて適用したときだったりします。 種類的には1つの修正内容で、ただ適用されるファイル数が多かっただけ、という場合です。

つまり、1つのPull Requestを出すときに、その修正が1つだと認識されるような内容であれば、レビュアーもレビューしやすいということになります。

1つの内容に集中する

複数の修正内容が混ざっていると、各ファイルを見るときにどの修正内容に対応する変更なのか、を判断しながらレビューしなくてはならなくなります。 これがレビューをするときに大きな負担となり、そもそもの修正が複雑な場合に、更に認知負荷が増えることになります。 結果、レビューに時間がかかり、レビュアーの作業時間も圧迫します。

こういった問題をどう解決すればいいでしょうか? 基本的には、Pull Requestを小さく分けていくことですが、それにはどういった手法があるでしょうか? 私の経験からですが、解説してみます。

小さなPull Requestの作り方

まずは、実装に手を付ける前に、ちょっと準備をすると良いと思います。 具体的には、修正する対象のファイルを見てリファクタリングなどの必要が無いか確認したり、実装内容を把握して機能を切り分けたりできると、小さくPull Requestを作れると思います。

リファクタリングの修正は気になっても別で出す

実装している途中で、リファクタリングしたくなることはよくあると思います。 この部分を直すには、他の部分も含めてリファクタリングしてから修正したほうが、間違いなくきれいに実装できる、ということはよくあります。 ただ、実装するべき機能部分とは別の部分も修正してしまうと、先程も書いた通り、レビューする際に実装の修正なのかリファクタリングの修正なのかを判断しつつレビューすることになるので、認知負荷が増えるという問題になります。

基本的にリファクタリングは動作に影響しないはずなので、機能を実装した後か、できればその前に1つのPull Requestとして出せると良いです。 レビューもリファクタリングの内容だけに集中してできるので、とても助かります。

新規機能に影響するようなリファクタリングなど、最終的にどうしても分離できないようだったら、そのまま出しても良いとは思います。 すべてを実装し終えてから、コミットし直すこともできなくはないですが、手間がかかりますしコミット漏れなども発生してしまう可能性もあります。 自分も大きなPull Requestになってしまったときは、ごめんねと言いつつレビューを依頼することはあります。

Web API 1つに着目して実装を切り分ける

例えば、あるサービスにTODO機能を追加するとしましょう。

まずは、TODOの一覧画面があります。 また、一覧画面上で、TODOを完了にできたり、削除できたりするボタンがあると思います。 更に、一覧画面には、表示するTODOを絞り込む機能があるとしましょう。基本的には完了していないものだけが表示されていますが、過去の完了したものを表示したり、フリーテキストや締切日などで絞り込めると便利でしょう。 他には、TODOを新規作成するためのちょっとしたフォームや、詳細な内容を変更するための編集画面もあると思います。

アプリやWebだと、このように画面単位でワイヤーフレームを作ったり、機能を考えることになると思います。 しかし、この一覧画面の機能をすべて実装して一度にPull Requestを出すと、とんでもない修正量になります。

ざっと「一覧画面」の機能を挙げるだけで、

  • 一覧として表組みやリストを表示する
  • 完了
  • 削除
  • 絞り込み・検索機能
  • (件数がそれなりにあれば)ページネーション or 無限スクロール

となり、バックエンドのAPIだけで4つほどは必要そうです。

このAPI 1つとそれを使う画面の部分だけ実装してPull Requestを作れれば、レビュアーもその機能だけのことを考えてレビューできます。 そうなると問題点の指摘もしやすいですし、早めにApproveしてくれるでしょう。 また、データベースのテーブル定義などは、APIから最初に使うタイミングで作るのが良いと思います。

フロントエンドとバックエンドが別れたWebアプリケーションや、ネイティブアプリであれば、Web APIの仕様を決め、それぞれがそのAPIの仕様に合わせて実装していくことになると思います。 この場合は、1つのAPIを実装したらPull Requestを出していけば、バックエンド側は小さくなると思います。

小さなPull Requestで作ったときのリリースの仕方

以下、リリースされているリポジトリのブランチは、mainブランチと表記します。 ブランチ戦略として、git-flowやGitHub Flowを使っていても基本的には同じになると思います。

featureブランチを作って、そこから更にブランチを作っていく

大きな機能を実装するのには時間がかかり、他のチームメンバーも開発しています。 mainブランチとfeatureブランチの差分はどんどん大きくなっていき、いざリリースのためのマージするとなったときにコンフリクトも大きくなる可能性が高まります。

mainブランチにマージされるとリリースされる運用の場合、featureブランチをまずは1つ作ってしまいましょう。 更にこのfeatureブランチからブランチを作り、機能を一つ一つ実装して、マージ先をfeatureブランチにしてPull Requestを出していきます。

featureブランチを作って、そこから更にブランチを作っていく

この際、マージ先としてGitHubのDraft Pull Requestを作っておくと、後でPull Requestをまとめていく際に便利です。

ただし、何もコミットがないブランチではDraft Pull Requestは作れません。 そこで、以下のようなgitコマンドで空のコミットをつくると、Draft Pull Requestを作れるようになるので便利です。

$ git commit --allow-empty

リリース用にまとめるDraft Pull Requestを作る

ただ、小さくPull Requestを作ったとしても、レビューを受けて修正が入ることは当然あると思います。そういう場合は、修正をfeatureブランチから作ったブランチにも順次取り込んでいきましょう。 featureブランチを仮のmainブランチとして使うようなイメージです。

Pull Requestで修正があったら、順次pullして取り込む

また、前述の通り、大きな機能を実装していると時間がかかるので、mainブランチに大きな修正が入ってくると思います。 その際は、このfeatureブランチにmainブランチの修正を取り込んでおくと、リリースするときも大きなコンフリクトになる可能性を減らせます。

mainブランチの修正を取り込んでいく

フィーチャートグルを使う

リリースはするけれども、ユーザーに提供する形では出さないフィーチャートグルで、実装した機能をmainブランチにマージしていく技法もあります。 TODOの例だと、トグルをOFFの状態で各機能を作ってmainブランチにマージし、ユーザーには見えない形でmainブランチに修正を取り込んでいきます。 そして、すべてを実装し終わってからトグルをONにしてユーザーに提供します。 詳しくはWikipediaのフィーチャートグルの記事をご確認ください。

小さいPull Requestで小さくフィードバックをもらおう

Pull Requestを小さくできると、レビューもサクッと終わり、リリースも早くできることになります。 また、レビュアーだけでなく、機能自体も小さくリリースできれば、ユーザーからも早めにフィードバックをもらえます。 そのことで、更に改善する観点が生まれ、より良いプロダクトを作る手助けになると思います。

小さなPull Requestは、アジャイルでリーンなプロダクト開発につながるので、いざ大きな機能を作るときは少し立ち止まるタイミングを作ってみましょう。 また、もっとこういう方法もある!ということでしたら、SNSなどで教えていただけると嬉しいです。