Four Keys を集計して「現在地」を確認してみる
こんにちは。CAMPFIREエンジニアの hayashida です。
Four Keysの集計を通して、自分たちの「現在地」を示す取り組みを始めました。今回はその集計の導入に関しての話をさせていただきます。
背景
現在、CAMPFIREでは、よりよい開発を通してより高い価値を届けていく取り組みを行っています。ただ、これまではシステムにおけるメトリクスの取得は行えていたものの、なかなか開発における「現在地」を定量的に計測する活動は行えていませんでした。
そこで、DevOpsの指標として広く活用されている Four Keys を取得することにしました。
Four Keysとは
Four Keysとは、DevOps Research and Assessment(DORA)による研究で示された、ソフトウェア開発チームのパフォーマンスを示す4つの指標です。
デプロイ頻度
正常な本番環境へのリリース頻度
変更のリードタイム
commitから本番環境稼働までの所要時間
変更障害率
デプロイが原因で本番環境に障害が発生する割合
サービス復元時間
本番環境での障害から回復するのにかかる時間
Four Keys は、この指標をもとにした DORA での分析により、「エリート」「ハイ」「ミディアム」「ロー」の4つのグルーピングが行われています。これにより、世界中の開発チームと比較してどのレベルにあるかを評価することができます。
独自の定義
今回、シンプルかつ効率的に集計を行うため、Four Keys のDORAにおける定義から少し変更して集計をしています。
デプロイ頻度
本番環境のデプロイのトリガーとなる、productionブランチへのマージ数
変更のリードタイム
Pull Request作成から、masterブランチへマージされるまでの時間
変更障害率
本番環境へのデプロイ数に対する、本番環境での障害発生数
サービス復元時間
本番環境で障害発生から回復までの時間
変更のリードタイムについては、本来の定義からは外れています。この点については、現状では作業開始タイミングを適切に取得するのが難しかったというのがあります。
また、まずは現在の開発を大きく変更することなく導入したかったので、定義をかえて取得することにしました。そこで、比較的取得が容易で、Pull Requestのサイズなどに影響されやすい、Pull Requestの作成からマージまでの時間を取得するのが適切だろうという判断をしました。
変更障害率については、障害と起因となるデプロイの紐づけがなかったり、外部サービスなどのデプロイ起因でないものを含めずに評価すると障害発生の実態との乖離がでるため、単純に障害発生数をデプロイ数で割って集計しています。
最初の取り組み
最初に取り組みを始めた際には、「まずは集計してみる」ことにしました。
そこで、デプロイ頻度、変更のリードタイムについては、GitHub の Pull Request から取得し、変更障害率、サービス復元時間については、過去の障害記録(ポストモーテム)を参照しました。
GitHub の Pull Request については、 gh コマンドで、GraphQLを叩いて必要なデータを取得した結果をCSVファイルに出力し、Google Sheets にインポートして集計しました。
一方、過去の障害記録については、機械的な集計が難しいものの、半年程度であれば発生数が少ないため、手動で Google Sheets に記入して集計しました。
結果として、最初に目標としていた自分たちの「現在地」を確認することができました。
集計の自動化
最初に目標としていた「現在地」を確認することができましたが、毎回手動で集計する箇所が多く、定期的に集計していくには、自動化できる部分は自動化していく必要があると感じました。
自動化に際して、すでに DORA でのGCP上での構築のためのリポジトリ(https://github.com/dora-team/fourkeys)などもありましたが、現在の開発規模からは too much であると判断しました。
前述の通り、すでに Google Sheets 上で手動集計に関しては構築していました。そのため、GitHubから集計できるものは比較的簡単に自動化できると考え、今回は、Google App Scripts(以下GAS)で集計をして自動化することにしました。
GASでは、以下のようなスクリプトを作成して、特定期間にマージされた Pull Request の情報を取得しています。その後、集計した内容を対象のシートに書き込んでいます。
const githubEndpoint = "<https://api.github.com/graphql>";
function fetchPullRequests(from, to, cursor) {
const withCursor = cursor ? `, after:"${cursor}"` : '';
const range = `merged:${from}..${to}`;
const graphql = `{
search(query: "repo:myrepo is:pr is:merged ${range} base:production sort:updated-asc", type: ISSUE, first: 100 ${withCursor}) {
pageInfo {
startCursor
hasNextPage
endCursor
}
nodes {
... on PullRequest {
createdAt
mergedAt
}
}
}
}`;
const option = {
method: 'post',
contentType: 'application/json',
headers: {
Authorization: 'bearer ' + githubApiToken
},
payload: JSON.stringify({ query: graphql })
};
const res = UrlFetchApp.fetch(githubEndpoint, option);
const json = JSON.parse(res.getContentText());
}
GASでは、定期的に実行させることができるトリガー機能があるため、毎日早朝に自動的に集計が実行されるようにしています。
これにより、手動で工数がかかっていた大部分を自動化することができました。
一方で、今回の構成で実装した上で、いくつかの注意点があります。
まず、GitHub API Token をGASのプロジェクトから読み取れる必要があるのですが、スプレッドシートを共有すると、もれなくGASのプロジェクトも共有されてしまい、共有範囲全体に GitHub API Token が公開されてしまいます。そのため、現在、共有するスプレッドシートと実行するGASのプロジェ
クトは分けています。
また、GAS は GitHub との連携などがややこしいため、現時点ではリポジトリでスクリプトの管理ができていません。
最後に、GitHub GraphQL API では、ページネーションを利用しても search では最大1000件しか取得できません。そのため、1000件を超えないように期間を区切って複数回取得するようにしています。
Four Keys以外の集計
最初は Four Keys のみを集計していましたが、現在は Four Keys 以外にも、課題や状況の把握などのために集計を行っています。
例えば、Pull Request 作成 〜 マージ までの各フェーズの時間や、Dependabot の Alert の作成、解消状況なども週次の時系列でウォッチしています。
これからの取組み
Four Keys の集計によって、自分たちの「現在地」を知ることができました。
しかしながら、あくまで「現在地」であり、「目的地」を示すことができていないため、改善の必要性などは判断できない状態です。そこで次のステップとして、現在の開発の課題などを深掘りし、その改善の目的にあった適切な Four Keys の目標値を設定していきたいと考えています。
今後も Four Keys を通した取り組みで、よりよい開発を通して高い価値を届けていくことにつなげていきたいと思います。