🖍️

コードレビューに着手するまでの時間を可視化する

2023/12/22に公開

株式会社IVRy (アイブリー)のエンジニアの kinashi です。
アドベントカレンダーも後半戦、2023年ももうすぐ終わりですね 🎅

IVRy のプロダクトチームでは毎週 KPT を行い、開発生産性を上げる様々な取り組みをしています。
この記事でも事例が紹介されていますが、チームが良くなっていくのはとてもワクワクします!
https://zenn.dev/ivry/articles/d5ccc376d0840c

チームの生産性を上げていくために、まだまだやらなければいけないことがたくさんあります。
そのために日々の小さな活動として意識しているのが、レビュー依頼が来たらなるべく早く見ることです。

IVRy では多くの PR が約1日でクローズされています。
https://note.com/good_peony927/n/n9ed42614c657

今回の記事ではレビュー完了までのリードタイムを少し掘り下げて見てみようと思います。

なぜレビューに着手するまでの時間を早めたいのか

この考えを意識したきっかけは数年前にいたチームで、フロー効率を上げる取り組みとして、カンバン上のチケットを滞留させないことを目的に、チケットをより早く右のスイムレーンに送ろうと始めたものです。


スイムレーンのイメージ

フロー効率について、当時この記事を読んで勉強させてもらったので引用させていただきます。
https://i2key.hateblo.jp/entry/2017/05/15/082655

機能がリリースされるまでのリードタイムを下記とした際に

  1. 開発
  2. レビュー
  3. レビュー対応
  4. マージ
  5. QA
  6. リリース

1 と 2 の間の時間を短くすることでリリースまでの時間が速くなるのはもちろんのこと、並列で開発している機能との無駄なコンフリクトを減らしたり、方針に齟齬がある場合などに早期に気付くことができたりといったメリットがあります。
(もちろん、レビューしてもらったらなるべく早くレビュー対応をする意識も大事です)

取り組みを始めたばかりの頃は自分の作業を中断してレビューを見るのは辛かったですが、チームとして見たときに、早くレビューに着手した方が全体の効率が上がる実感がありました。

  • なるべく小さな単位でレビューに出す
  • レビュワーに分かりやすいように、やったことを説明する
  • レビューが均等に割り振られるようにする
  • タスクの優先度を加味して、すぐに着手できなそうなら他のメンバーにお願いする

などの工夫も同時に行うと良いでしょう。

IVRy では上記に加えて、GitHub + Slack Integration を使い、レビュー依頼や GitHub 上でメンションがあった際に、Slack 通知で気付ける仕組みを使っています。
https://github.com/integrations/slack

Pull Request のリードタイムを可視化する

前置きが長くなりましたが、「推測するな、計測せよ」 ということで、実際どのくらいの時間でレビューが行われているのかを GitHub API を使い可視化しました。
以降 Pull Request は PR と記載

前提条件

厳密にレビューにかかっている時間は取れないので、最初にコメントされるまでの時間を見ることで、何らかの返信が付いた(≒レビューが開始された)とみなしました。
Approve されるまでの時間もあわせて出力するようにしています。

下記の条件で PR を集計しました。

  • 計測対象
    • 2023/1/1 ~ 2023/12/31 までにマージされたフロントエンドの PR
    • Approve されていないものは除外
    • タイトルが Release で始まるものは除外(ブランチ運用の都合)
    • ラベルに「案件」が設定されているものは除外(大きい機能の親ブランチ)
  • 最初のコメントまでの時間
    • review_requested のイベント(または PR)が作成されてから PR を出した人以外がコメントするまでの時間
    • もしくは最初に Review(GitHub 上の概念) が送信されるまでの時間
  • Approve までの時間
      - review_requested のイベント(または PR)が作成されてから APPROVED の レビューが送信されるまでの時間

これらの PR の 中央値 と 90パーセンタイル を出してみました。

コード

GitHub Rest API を Octokit から呼び出してデータを取得しました。
PullRequests の取得では日時を指定したかったので、search API から取得するようにしています。(これが調べてもなかなか見つからずハマりました)

const octokit = new Octokit({ auth: process.env.PERSONAL_ACCESS_TOKEN })
  let pulls: Endpoints['GET /search/issues']['response']['data']['items'] = []
  for await (const { data } of octokit.paginate.iterator(
    octokit.rest.search.issuesAndPullRequests,
    {
      q: `repo:${process.env.OWNER}/${process.env.REPO} created:${process.env.RANGE_OF_DATE} is:pr is:merged review:approved`,
      sort: 'created',
      order: 'asc',
      per_page: 50,
    },
  )) {
    pulls = [...pulls, ...(data ?? [])]
  }

一覧を取得した後は、pull.number でコメント、レビュー、イベントの一覧を取得しています。
最初は for の部分も Promise.all にしていましたが、Rate Limit に引っかかってしまうことがあったため、この形にしています。

for (const pull of pulls) {
    const user = pull.user?.login
    const [{ data: comments }, { data: reviews }, { data: events }] =
      await Promise.all([
        octokit.rest.pulls.listReviewComments({
          owner: process.env.OWNER,
          repo: process.env.REPO,
          pull_number: pull.number,
        }),
        octokit.rest.pulls.listReviews({
          owner: process.env.OWNER,
          repo: process.env.REPO,
          pull_number: pull.number,
        }),
        octokit.rest.issues.listEvents({
          owner: process.env.OWNER,
          repo: process.env.REPO,
          issue_number: pull.number,
        }),
      ])

計測の起点は最初のコメントよりもレビュー依頼が先の場合のみレビュー依頼があったタイミング、そうでなければ PR が作られたタイミングとしました。

const reviewStartedAt =
      reviewRequestedAt &&
      new Date(reviewRequestedAt).getTime() <
        new Date(firstCommentedAt).getTime()
        ? reviewRequestedAt
        : pull.created_at

あとは前述の条件で時間を割り出して算出しています。
コードは GitHub に置いてあるので全体が見たい方はこちらでご確認ください。
https://github.com/kinashi/measure-review-time

結果

2023-01-01~2023-12-31 (625件)
最初のコメントまでの時間
   50パーセンタイル: 36分
   90パーセンタイル: 22.7時間
Approve までの時間
   50パーセンタイル: 1.1時間
   90パーセンタイル: 1.7日

半年に分割した値も出してみました。7月〜12月のデータで50パーセンタイルが微増傾向ですが、誤差の範囲かなという印象です。

2023-01-01~2023-06-30 (352件)
最初のコメントまでの時間
   50パーセンタイル: 36分
   90パーセンタイル: 23.8時間
Approve までの時間
   50パーセンタイル: 1.0時間
   90パーセンタイル: 1.7日

2023-07-01~2023-12-31 (273件)
最初のコメントまでの時間
   50パーセンタイル: 39分
   90パーセンタイル: 21.3時間
Approve までの時間
   50パーセンタイル: 1.4時間
   90パーセンタイル: 1.7日

結果を見ると半分は1時間以内にレビューされ、2時間以内でマージできる状態になっていたことが分かりました。
90パーセンタイルで見ても最初のコメントまでの時間は1日以内となっていて、良好な状態だと思います。

コメントが付くまでに2日以上かかっている PR を出力し、実際に GitHub 上で見てみると、概ね次のパターンであることが分かりました。

  • WIP の状態が長く、レビュー依頼したあとも開発に時間がかかっている
    • 実装が難しくて悩んでいる場合は、ペアプロやモブプロなどで解消してけると良い
  • 仕様の確認が必要なことが分かり問い合わせてから回答が来るのを待っていた
    • 設計や実装段階で気付けるとベターだが、裏を返せばレビューが機能している証
  • 変更が多いリファクタなど
    • できるだけ分割して PR を出す
    • どうしても変更が大きくなってしまう場合はペアレビューなどのコミュニケーションで効率をあげていく

一定以上時間がかかっているものを出力することで、何に時間が取られているのかも振り返ることができたのは嬉しい誤算でした。

まとめ

今回は PR のリードタイムについて見てきましたが、中央値のデータはなんとなく持っていた肌感と合っていましたし、時間がかかっているものは振り返ることができました。
これからもメンバーが増えていくなかで、今回の値を1つの指標として、レビューを早く見る文化を絶やさないようにしていけたらいいなと思っています。


このタイミングでこの画像はめちゃくちゃやらせっぽい

自分のチームはどうなんだろうと気になった方は1度計測してみてはいかがでしょうか。

最後に

今年は紅白に分かれてアドベントカレンダーを実施中です。
エンジニアの話に限らず様々な記事がありますので、過去の記事もぜひチェックしてみてください!
https://twitter.com/IVRy_jp/status/1730521537294520802

https://adventar.org/calendars/9247
https://adventar.org/calendars/9453

また、IVRyでは一緒に働いてくれるエンジニアを募集中です!
https://ivry-jp.notion.site/IVRy-e1d47e4a79ba4f9d8a891fc938e02271

IVRyテックブログ

Discussion