Pull Requestから社内全チームの開発パフォーマンス指標を可視化し、開発チーム改善に活かそう

こんにちは。id:shiba_yu36です。MackerelチームでWebアプリケーションエンジニアをしています。最近の開発合宿で、id:syou6162id:polamjagと一緒に、社内の全チームの開発パフォーマンスを表す指標をGitHubのPull Requestから可視化し、開発チームの改善に活かせるようにしました。今回はその紹介をします。

説明するサンプルコードは、次のレポジトリで公開しているので参考にしてください。ここではGitHubのhatenaオーガニゼーションで集計していますが、forkして少し手直しすれば、別のオーガニゼーションの集計も可能になっています。

hatena/pull-request-analysis-sample

開発チームの改善におけるいくつかの課題感

僕は昔から開発チームの改善に興味があり、これまでさまざまなチームに所属するたびに、チームの改善を提案して実施するという活動を続けてきました。例えば、はてなブログチームでGitHubを使った開発フロー改善新機能開発時に細かく開発ブランチにマージしていこう活動レビューがスムーズにいく通知グッズ作りなどです。

しかし、このような改善を実施するとき、次のような課題を感じていました。

  • 問題の改善が、実際に中長期でパフォーマンスに貢献しているか分からない
  • 改善にあたって、個々人がそれぞれ考える「やりやすさ」を言い合うだけになり、水掛け論になることもある
  • 改善したい課題がたくさんあるときに、その優先度の決め方が分からない

そこで、開発チームのパフォーマンスをトラッキングできる定量的な指標を決め、その指標を元にデータドリブンで開発チームの改善を進めていくことで、これらの課題を解決できるのではないかと考えました。

開発チームのパフォーマンス指標に何を使うか

開発チームのパフォーマンスを測る指標はさまざま議論されていますが、今回僕は書籍『LeanとDevOpsの科学』で言及されている指標を利用することに決めました。これによると開発チームのパフォーマンスを測るには、次の4指標をトラッキングすべきとされています。

  • 変更のリードタイム: first commit〜そのcommitが本番にリリースされるまでの時間
  • デプロイの頻度: 本番環境へのデプロイの回数
  • MTTR(平均修復時間): サービスダウン〜復旧までの時間
  • 変更失敗率: 本番デプロイ時のサービスダウン発生率

詳細については当該書籍や、Google Cloud blogの記事「エリート DevOps チームであることを Four Keys プロジェクトで確認する」を参考にしてください。

4つの指標のうち何からまず集計するか

これで開発チームのパフォーマンスを測る4つの指標は決まりました。しかし、始めから全ての指標を定義通り集計できるようにするのは大変です。そこでまず、開発チームのパフォーマンス指標を測る第一歩として、次の2つを集計することにしました。

  • 「変更のリードタイム」の一部
  • 「デプロイ頻度」

この2つの指標は、現在のはてなの開発フローにおいて、GitHubのPull Requestデータから集計しやすいのです。

変更のリードタイムの一部を可視化する

「変更のリードタイム」を集計するには、あるcommitがいつリリースされたかまで追う必要があり、1チームで複数レポジトリ運用をしていたり、レポジトリごとにデプロイ方式が違ったりすると、集計にかなり手間がかかります。

そこで定義通りの集計にとらわれすぎず、次のように「変更のリードタイム」をもう少し集計しやすい形に分解して、その一部を可視化してみようと思います。

変更のリードタイム = first commit〜Pull Requestのマージまでの時間 + Pull Requestのマージ〜デプロイまでの時間

このようにすれば、前者の「first commit〜Pull Requestのマージまでの時間」に関しては、Pull Requestのデータから簡単に集計できそうです。これを「Pull Requestのプロセスタイム」と呼称し、次の手順で可視化してみます。

  1. Pull Requestのデータを定期的にBigQueryにインポートする
  2. 指標に応じたSQLを書き、viewとして保存する
  3. viewをデータポータルで可視化する

データポータルは「Google マーケティング プラットフォーム」に含まれるツールで、以前は「Google Data Studio」とも呼ばれていました。

データを定期的にBigQueryにインポートする

まず、Pull Requestのデータを定期的にBigQueryにインポートします。このとき後からSQLを工夫すればさまざまな集計ができるように、できるだけ生のデータを送ります。定期的に投げ込む手段として、GitHub Actionsを利用しました。

GitHubからデータをインポートするスクリプトは、前述したサンプルレポジトリにある次のファイルを参照してください。

script/import-pull-request.ts

このスクリプトは、上部で定義しているGITHUB_ORG_NAMEに指定したオーガニゼーションの全てのレポジトリのPull Requestを、GCP_PROJECT_IDのBigQueryにインポートします。

RateLimitへの対処やインポート速度の向上のため、多少は見通しが悪くなっていますが、基本的にはPull RequestのデータをGraphQL APIから取ってきて、BigQueryに投げ込むだけです。

実行例は次のようになります。

GITHUB_TOKEN=... GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json START_DATE=2020-10-05 END_DATE=2020-10-12 yarn --silent ts-node script/import-pull-request.ts

次で説明するように自動実行を想定したスクリプトですが、このようにSTART_DATEEND_DATEを指定して、手動で起動することも可能です。

GitHub Actionsのcronで定期的にインポートする

GitHub Actionsのcronを使った定期的なインポートは、サンプルレポジトリの次のファイルで設定しています。

.github/workflows/import-pull-request.yml

前述のスクリプトが毎日起動され、前日にマージされた分のPull RequestをBigQueryにインポートします。

レポジトリのシークレットで、Google Cloud Platformのサービスアカウントの鍵の設定(GCP_SA_KEY_FOR_GITHUB_IMPORTER)と、Pull Requestのデータを取得するためのGitHubトークンの設定(TOKEN_FOR_GITHUB_IMPORTER)が必要です。

また、GitHub ActionsからBigQueryにインポートするため、Google Cloud Platformのサービスアカウントの発行が必要です。これは、サンプルレポジトリでは次の設定ファイルに従って、Terraformで管理しています。

terraform/main.tf(24行目〜43行目)

以上で、特定のオーガニゼーションにある全てのPull Requestのデータを、BigQueryへ定期的にインポートできるようになりました。BigQuery側のPull Requestデータスキーマは、最終的にこのようになりました。

フィールド名 タイプ モード
labelNames STRING REPEATED
headRefName STRING NULLABLE
baseRefName STRING NULLABLE
deletions INTEGER NULLABLE
author RECORD NULLABLE
author. typename STRING NULLABLE
author. login STRING NULLABLE
firstCommittedAt TIMESTAMP NULLABLE
id STRING NULLABLE
additions INTEGER NULLABLE
reviews RECORD NULLABLE
reviews. totalCount INTEGER NULLABLE
mergedAt TIMESTAMP NULLABLE
repository RECORD NULLABLE
repository. nameWithOwner STRING NULLABLE
number INTEGER NULLABLE
title STRING NULLABLE
createdAt TIMESTAMP NULLABLE
url STRING NULLABLE

Pull Requestのプロセスタイムを可視化する

上記を実行して、オーガニゼーションの全てのPull RequestデータがBigQueryに入りました。続いて「Pull Requestのプロセスタイム」を抽出するSQLを書いて、BigQueryにviewとして保存します。

1日ごとに集計すると誤差が大きく分析しづらいため、Pull Requestのプロセスタイムの中央値を、4週間のスライディングウインドウでプロットします*1。そのためSQLはけっこう複雑で、次のようになりました。

bigquery/mart/process_time_history.sql

このSQLでは可視化しやすいように、botのPull Requestを除外して人間が作ったもののみを対象にしたり、単位をhourに変換したりといったことも行なっています。

これをBigQueryのviewとして保存し、データポータルで「Pull Requestのプロセスタイム」の時系列データを可視化します。

オーガニゼーション全体のPull Requestデータなので、これだけで分析するのは難しいですが、次のようなちょっとした仮説や疑問ならこれを見るだけでも立てられそうですね。

  • 新型コロナウイルスにより在宅勤務推奨になった4月くらいから指標がいったん悪化したが、6月後半に戻ってきている。つまり、慣れてくれば在宅でもパフォーマンスが非常に下がるということはないのではないか?
  • 6月末から指標が徐々に悪化しているが、これは何が原因だろうか?

デプロイの頻度を可視化する

続いて、上記と同じ手順で「デプロイの頻度」を可視化してみます。

現状、はてなではgit-pr-releaseを使って、developブランチからmainブランチへPull Requestを作り、マージしたらリリースするというフローになっていることが多いです。このようなPull Requestを数えることで、デプロイ頻度を可視化できそうです。

こちらも1日ごとでの集計だと誤差が大きいため、4週間の移動平均を出してみます。

bigquery/mart/deploy_count.sql

このSQLを書くだけで、全社のデプロイ頻度の時系列推移を次のようにデータポータルで可視化できました。

指標をチームごとに集計する

ここまでPull Requestデータを使って「Pull Requestのプロセスタイム」や「デプロイ頻度」を可視化してきました。しかし、実際にチームの改善に利用するには、これらの指標をチーム単位でトラッキングしたいはずです。そこで、続いてチームごとに可視化していきます。

はてなでは、GitHub Teamsを使ってチームのレポジトリ一覧を管理しているため、この情報を使えばチームごとに集計できます。次の3ステップで行います。

  1. 定期的にGitHub Teamsの情報をBigQueryにインポートする
  2. チーム情報を使いやすいように加工しておく
  3. SQLでチームごとの時系列推移を可視化する

GitHub Teamsの情報を定期的にインポートする

GitHub Teamsの情報も、Pull Requestデータと同様の方法でBigQueryにインポートします。

これでGitHub Teamsの情報が次のスキーマでBigQueryにインポートできました。

フィールド名 タイプ モード
repositories RECORD NULLABLE
repositories. nodes RECORD REPEATED
repositories.nodes. nameWithOwner STRING NULLABLE
name STRING NULLABLE

チーム情報を使いやすいように加工しておく

インポートした生の形式だと集計時に扱いづらいので、もう少し加工しておきます。

チームごとの時系列推移を可視化する

最後に、SQLを工夫して「Pull Requestのプロセスタイム」や「デプロイ頻度」の時系列推移をチームごとに出力します。Pull Requestデータと、使いやすいように加工したチーム情報を結合することで可視化できます。

次のSQLで、チームごとの「Pull Requestのプロセスタイム」の時系列推移が出ます。

bigquery/mart/process_time_history_by_team_name.sql

このSQLではいろいろ可視化できるように、さまざまな指標を出しています。これを使えばチームごとに1本ずつ線ができ、チームごとの「Pull Requestのプロセスタイム」が可視化できます。

チームごとの「デプロイの頻度」の時系列推移を出すのは次のSQLです。

bigquery/mart/deploy_count_by_team_name.sql

こちらもチームごとに1本ずつ線ができます。

これで「Pull Requestのプロセスタイム」と「デプロイ頻度」の時系列推移をチームごとに可視化し、比較できるようになりました。チームごとに集計することで、チーム単位で課題を分析して改善に取り組んだり、上手く改善できてそうなチームを見つけてヒアリングしたりといったことができそうです。

おわりに ── 今後の展望

この記事では、開発チームの改善に活かすためPull Requestデータを使って、開発のパフォーマンス指標から「変更のリードタイム」の一部である「Pull Requestのプロセスタイム」と「デプロイ頻度」を可視化する事例を紹介しました。

前述のようにサンプルコードも公開しましたので、自社の開発のパフォーマンスを可視化する参考にどうぞご利用ください。もし環境にGoogle Cloud Platformを使っているなら、Four Keysのようなツールも使えるかもしれません。

今回は開発パフォーマンス指標のうち一部の可視化にとどまりましたが、今後は以下のようなこともやっていきたいと考えています。

  • MTTRや変更失敗率も集計し、1つのダッシュボードで開発パフォーマンスの指標を可視化し、チーム全体で同じ方向を向いて改善を行えるようにする
  • 他チームと比較することで自分たちのチームの課題を発見し、その課題について一番うまく対応できてそうなチームを指標から見つけ、助言を聞きつつ改善する

参考: 合宿で作ったその他の可視化例

データポータルはかなり柔軟に可視化できるので、合宿では次のような可視化もしてみました。

「Pull Requestのプロセスタイム」の25パーセンタイル、50パーセンタイル、75パーセンタイルと、Pull Requestの数を可視化。

d/d/d(deploys / developers / days)*3の可視化。

ただし、単純にPull RequestのAuthor数で出すと「けっこう厳しく出るね」と会話していて、実際の人数で見るともう少し高くなるかもしれません。

【PR】一緒にデータ駆動でチーム改善しませんか!

はてなでは、データ駆動でのチーム改善に興味のあるエンジニアを募集しています。新卒・中途、東京・京都を問わずぜひご応募ください!

エンジニア採用 - 採用情報 - 株式会社はてな

*1:ある日にプロットされる値は、その日から過去28日以内にマージした「Pull Requestのプロセスタイム」の中央値になっている

*2:レビューのためのGitHub Teamsなど、開発チームよりさらに小さい単位のチームが大量にあるため

*3:1日あたりのデプロイ回数を開発者数で割ったもの。詳細は https://twitter.com/hiroki_daichi/status/1100381137929625600 など