Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

Four keys指標とは

こんにちは。プラットフォーム開発部 のthorです。

プラットフォーム開発部では、生産性を測定して継続的に向上させる事を目標としています。どのような指標で測定するかは各チームに任されていて、私のチームでは以前のchikaの記事で紹介されていた、Four keys指標を使って生産性を測定する事にしました。

そこで、今回はFour keys指標とはどのようなものか、私のチームではどのように測定する事になったかをご紹介したいと思います。

Four keys指標

Four keys指標(メトリクス)とは、Devops Reseach & Assessment(DORA)が確立したソフトウェア開発チームのパフォーマンスを示す以下の4つの指標のことです。この指標を使って、チームのパフォーマンスを確認する方法の説明はこちらのブログがあります。

  • デプロイの頻度 - 組織による正常な本番環境へのリリースの頻度
  • 変更のリードタイム - commit から本番環境稼働までの所要時間
  • 変更障害率 - デプロイが原因で本番環境で障害が発生する割合(%)
  • サービス復元時間 - 組織が本番環境での障害から回復するのにかかる時間

(『エリート DevOps チームであることを Four Keys プロジェクトで確認する』より)

パフォーマンスのレベルは、各指標について上記のように分類されるそうです。出来れば、エリートになりたいですね。

なぜFour keysが重要か

DORAは毎年「State of DevOps Report(DevOpsの現況に関するレポート)」を出しています。このレポートで報告している研究の調査・分析手法を紹介・解析する本が『LeanとDevOpsの科学[Accelerate] テクノロジーの戦略的活用が組織変革を加速する』として出版されています。

この本では「Four keys」でのハイパフォーマーは、以下の3つの「組織のパフォーマンス」のパフォーマンス結果でもローパフォーマーを上回る傾向にあり、両者の対比は一貫して2倍を超えていると報告(P. 31)しています。

つまり「組織のソフトウエアデリバリの能力は、組織に競争上の優位性をもたらす」ので重要だという事です。

なお本には、ソフトウエアデリバリの能力はどのようなプラクティスと関連があるかについての分析も述べられています。

指標の測定の自動化

指標の測定は自動化している組織もかなりあるようです。また、データ指標の測定用のオープンソースプロジェクトが、GitHubで公開されています。

システム構成 (『four keysプロジェクトのGitHub』より)

このfour keysプロジェクトでは、GoogleのCloud RunやBigQueryを使って自動化された測定がされています。

指標の計算方法

指標の計算方法の解説は、英語ですがfour keysプロジェクトのGitHubで解説されています。BigQueryのコードもコメント付きで解説されています。本ブログでも大まかな内容を説明します。

デプロイ頻度

原文のデプロイ頻度の解説はこちらです。頻度の計算は平均値ではなく中央値を用いています。平均値は外れ値の影響を受けやすいので、中央値を使っているのでしょう。

下記のBigQueryの拡張のSQLを使って、まず最初に3ヶ月分のカレンダー・テーブルを生成します。

WITH last_three_months AS
(SELECT
TIMESTAMP(day) AS day
FROM
UNNEST(
GENERATE_DATE_ARRAY(
    DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH), CURRENT_DATE(),
    INTERVAL 1 DAY)) AS day
-- FROM the start of the data
WHERE day > (SELECT date(min(time_created)) FROM four_keys.events_raw)
)

上記のクエリで以下のようなテーブルが生成されます。

day
2022-12-11
2022-12-12
...
2023-03-10

上記のテーブルを以下のテーブルと後述のSQLで外部結合して、日毎、週毎のデプロイ頻度の中央値を求めて、デプロイ頻度を分類しています。

フィールド名 説明
deploy_id 文字列 デプロイのID
changes 文字列の配列 デプロイに含まれる変更のID(バグトラッキングシステムのチケットID等)
time_created タイムスタンプ デプロイの完了日時
SELECT
CASE WHEN daily THEN "Daily" 
     WHEN weekly THEN "Weekly" 
      -- If at least one per month, then Monthly
     WHEN PERCENTILE_CONT(monthly_deploys, 0.5) OVER () >= 1 THEN  "Monthly" -- 半分以上の月でデプロイしている
     ELSE "Yearly"
     END as deployment_frequency
FROM (
  SELECT
  -- If the median number of days per week is more than 3, then Daily
  PERCENTILE_CONT(days_deployed, 0.5) OVER() >= 3 AS daily, -- 週のデプロイ日数の中央値が3以上
  -- If most weeks have a deployment, then Weekly
  PERCENTILE_CONT(week_deployed, 0.5) OVER() >= 1 AS weekly, -- 半分以上の週でデプロイしている

  -- Count the number of deployments per month.  
  -- Cannot mix aggregate and analytic functions, so calculate the median in the outer select statement
  SUM(week_deployed) OVER(partition by TIMESTAMP_TRUNC(week, MONTH)) monthly_deploys
  FROM(
      SELECT
      TIMESTAMP_TRUNC(last_three_months.day, WEEK) as week,
      MAX(if(deployments.day is not null, 1, 0)) as week_deployed, -- その週にデプロイしたら1
      COUNT(distinct deployments.day) as days_deployed -- その週にデプロイがあった日の数
      FROM last_three_months
      LEFT JOIN(
        SELECT
        TIMESTAMP_TRUNC(time_created, DAY) AS day,
        deploy_id
        FROM four_keys.deployments) deployments ON deployments.day = last_three_months.day
      GROUP BY week) -- まず週ごとにまとめる
 )
LIMIT 1;

これによって

  • 週に3日以上デプロイしていれば、Daily
  • 3ヶ月の半分以上の週でデプロイをしていれば、Weelky
  • 3ヶ月の半分の月でデプロイしていれば、Monthly
  • 上記でなければ、Yearly

と判定されます。 また、上記のSQL

  • PERCENTILE_CONT(days_deployed, 0.5)が、週に何日デプロイしているか
  • PERCENTILE_CONT(monthly_deploys, 0.5)が、月に何回デプロイしているか

の値になります。

変更のリードタイム

原文でのリードタイム解説はこちらです。

まず、デプロイに関する、全ての変更(commit)のリードタイムを以下のように求めます。

SELECT
  d.deploy_id,
  TIMESTAMP_TRUNC(d.time_created, DAY) AS day,
  -- Time to Change
  TIMESTAMP_DIFF(d.time_created, c.time_created, MINUTE) AS time_to_change_minute -- デプロイ時刻 - コミット時刻
FROM four_keys.deployments d, d.changes   -- changesは配列型なので注意
LEFT JOIN four_keys.changes c ON changes = c.change_id;

上記のクエリから更に以下のように中央値を求めて、変更のリードタイムを算出します。

SELECT
  day,
  median_time_to_change
FROM (
  SELECT
    day,
    PERCENTILE_CONT(
      -- Ignore automated change (デプロイした時の副作用で起きるコミットは除外している)
      IF(time_to_change_minutes > 0, time_to_change_minutes, NULL), 
      0.5) -- Median
      OVER (partition by day) AS median_time_to_change
  FROM (
    SELECT
      d.deploy_id,
      TIMESTAMP_TRUNC(d.time_created, DAY) AS day,
      -- Time to Change
      TIMESTAMP_DIFF(d.time_created, c.time_created, MINUTE) AS time_to_change_minutes
    FROM four_keys.deployments d, d.changes
    LEFT JOIN four_keys.changes c ON changes = c.change_id
  )
)
GROUP BY day, median_time_to_change;

リードタイムの分類は以下のように、過去3ヶ月分のデプロイに関する変更(commit)の中央値を計算しています。

SELECT 
  CASE
    WHEN median_time_to_change < 24 * 60 then "One day"
    WHEN median_time_to_change < 168 * 60 then "One week"
    WHEN median_time_to_change < 730 * 60 then "One month"
    WHEN median_time_to_change < 730 * 6 * 60 then "Six months"
    ELSE "One year"
    END as lead_time_to_change
FROM (
  SELECT
    IFNULL(ANY_VALUE(med_time_to_change), 0) AS median_time_to_change
  FROM (
    SELECT
      PERCENTILE_CONT(
        -- Ignore automated pushes
        IF(TIMESTAMP_DIFF(d.time_created, c.time_created, MINUTE) > 0, TIMESTAMP_DIFF(d.time_created, c.time_created, MINUTE), NULL),
        0.5) -- Median
        OVER () AS med_time_to_change, # Minutes
    FROM four_keys.deployments d, d.changes
    LEFT JOIN four_keys.changes c ON changes = c.change_id
    WHERE d.time_created > TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH)) -- Limit to 3 months
  )
)

このように、全て変更(コードのコミットやマージ)の中央値を算出して評価しています。

変更障害率

原文の変更障害率の解説はこちらです。

日毎に、その日のデプロイ失敗数 / デプロイ数 を計算しています。

SELECT
TIMESTAMP_TRUNC(d.time_created, DAY) as day,
SUM(IF(i.incident_id is NULL, 0, 1)) / COUNT(DISTINCT deploy_id) as change_fail_rate
FROM four_keys.deployments d, d.changes
LEFT JOIN four_keys.changes c ON changes = c.change_id
LEFT JOIN(SELECT
        incident_id,
        change,
        time_resolved
        FROM four_keys.incidents i,
        i.changes change) i ON i.change = changes
GROUP BY day;

Four keys指標の分類は、以下のように日毎ではなく過去3ヶ月のデプロイから計算します。

SELECT
CASE WHEN change_fail_rate <= .15 then "0-15%"
     WHEN change_fail_rate < .46 then "16-45%"
     ELSE "46-60%" end as change_fail_rate
FROM 
 (SELECT
  SUM(IF(i.incident_id is NULL, 0, 1)) / COUNT(DISTINCT deploy_id) as change_fail_rate
  FROM four_keys.deployments d, d.changes
  LEFT JOIN four_keys.changes c ON changes = c.change_id
  LEFT JOIN(SELECT
          incident_id,
          change,
          time_resolved
          FROM four_keys.incidents i,
          i.changes change) i ON i.change = changes
  -- Limit to 3 months
  WHERE d.time_created > TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH))
  )
LIMIT 1;

サービス復元時間

原文のサービス復元時間の解説はこちらです。

以下のように、日毎に障害発生時刻から解決のデプロイ時刻までの時間の中央値を求めています。

SELECT
  TIMESTAMP_TRUNC(time_created, DAY) as day,
  -- Median time to resolve
  PERCENTILE_CONT(
    TIMESTAMP_DIFF(time_resolved, time_created, HOUR), 0.5)  -- 解決用デプロイ時刻 - 障害発生時刻 の中央値
    OVER(PARTITION BY TIMESTAMP_TRUNC(time_created, DAY)
    ) as daily_med_time_to_restore,
  FROM four_keys.incidents;

分類方法は以下の通り、3ヶ月分の復元時間の中央値を計算しています。

SELECT
CASE WHEN med_time_to_resolve < 24  then "One day"
     WHEN med_time_to_resolve < 168  then "One week"
     WHEN med_time_to_resolve < 730  then "One month"
     WHEN med_time_to_resolve < 730 * 6 then "Six months"
     ELSE "One year"
     END as med_time_to_resolve,
FROM (
  SELECT
  -- Median time to resolve
  PERCENTILE_CONT(
    TIMESTAMP_DIFF(time_resolved, time_created, HOUR), 0.5)
    OVER() as med_time_to_resolve,
  FROM four_keys.incidents
  -- Limit to 3 months
  WHERE time_created > TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 3 MONTH)))
LIMIT 1;

どのようにFour keys指標を向上させるのか?

まだ指標の測定を開始したばかりですが、どのように取り組んで指標を向上させることができたかのご紹介をできるようになればと思います。

みらい翻訳では、一緒に開発を進めてくれるエンジニアを募集しています!

チームで協働しながら開発を進めたいと思っているエンジニアを探しています。

ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。

miraitranslate.com