Project Management DORA и метрики инженерной эффективности
0%

DORA и метрики инженерной эффективности

DORA и метрики инженерной эффективности

Любой руководитель рано или поздно задаёт вопрос: «Мы работаем хорошо или плохо?» Ответ «команда старается» его не устраивает, и тогда рождаются строки кода в неделю, closed story points, процент покрытия тестами и количество коммитов на человека. Все эти числа объединяет одно свойство: они измеряют активность отдельных людей, а не способность организации доставлять ценность. Разница фундаментальная. Активность можно нарастить, не изменив ничего полезного; способность доставлять — нельзя.

DORA (DevOps Research and Assessment) — исследовательская программа, которая с 2014 года пытается ответить на тот же вопрос эмпирически: собрать данные с десятков тысяч инженеров и найти, какие измеримые характеристики процесса доставки статистически связаны с результатами бизнеса. Итог — четыре метрики, которые стали фактическим стандартом отрасли, и модель из двух с лишним десятков практик, которые на эти метрики влияют.

Эта статья — про то, как метрики устроены изнутри: что именно считать, как считать корректно (включая места, где наивный расчёт даёт бессмысленное число), какие выводы из них законны, а какие нет, и почему внедрение DORA чаще всего проваливается не по технической причине.

Предыдущая статья трека — Качество, Definition of Done и релизный менеджмент — описывала, как устроен путь изменения до прода. Здесь мы этот путь измеряем.

Часть 1. Интуиция: почему именно эти четыре числа

Что мы вообще хотим знать

Хорошая метрика системы доставки должна отвечать на два вопроса одновременно:

  1. Насколько быстро организация превращает решение в работающий у пользователей код?
  2. Насколько дёшево обходится ошибка на этом пути?

Первый вопрос без второго порождает «ковбоев»: катим по десять раз в день, прод горит. Второй без первого — «осторожных»: релиз раз в квартал после двух недель регресса, зато чисто. Обе крайности — плохие системы, и обе выглядят отлично по одномерной метрике.

Поэтому DORA берёт две оси — пропускную способность (throughput) и стабильность — и на каждой ставит по паре метрик: одну про частоту событий, вторую про длительность.

Матрица четырёх ключевых метрик DORA

Ключевая находка: скорость и стабильность не конфликтуют

Управленческая интуиция говорит: чтобы стало надёжнее, надо релизить реже и проверять дольше. Данные DORA говорят обратное. Команды-лидеры показывают одновременно и высокую частоту деплоев, и низкий процент отказов; отстающие — плохи по обеим осям. Корреляция между скоростью и стабильностью в выборке положительная, а не отрицательная.

Механизм понятен, если думать про размер партии. Частый деплой физически означает мелкие изменения: меньше кода за раз → проще ревью → уже область поиска при поломке → дешевле откат → меньше страха → ещё чаще деплой. Это самоусиливающаяся петля, и она же работает в обратную сторону: редкие релизы раздувают партию, партия повышает риск, риск требует ещё более тяжёлой процедуры, процедура делает релизы ещё более редкими.

Квадранты 2 и 4 заселены слабо — это и есть эмпирический результат. Подробный разбор методологии — в книге Николь Форсгрен, Джеза Хамбла и Джина Кима «Accelerate: The Science of Lean Software and DevOps» (IT Revolution, 2018); там же приложение с описанием психометрики, латентных конструктов и кластерного анализа.

Часть 2. Строгие определения

Половина провалов внедрения — это спор «а что считать деплоем». Определения ниже — операционные: их можно превратить в SQL, не додумывая.

Deployment Frequency (DF)

Сколько раз за период изменение кода успешно доехало до продакшена и стало доступно пользователям.

Тонкости:

  • Событие — успешный деплой в прод. Провалившиеся выкатки не считаются как деплой (но и не теряются: они попадают в CFR через факт отказа).
  • Деплой ≠ релиз. Если вы катите артефакт за feature-flag и включаете фичу через неделю — событием DF считается выкатка артефакта. Про разведение деплоя и релиза — см. Качество и релизный менеджмент.
  • Единица измерения — сервис или продукт, а не команда и не монорепозиторий целиком. Сложив деплои сорока микросервисов, вы получите красивое «200 в день» и ноль информации.
  • Метрика — счётная и сильно дискретная на низком конце. У команды с релизом раз в две недели «частота» — это фактически бинарный флаг. Поэтому DORA публикует её не как среднее, а как попадание в диапазон.

Lead Time for Changes (LT)

Медиана (или процентиль) времени от первого коммита изменения до момента, когда это изменение работает в проде.

Это не «время от идеи до прода» (то — time to market, оно больше и включает продуктовые очереди) и не cycle time доски Kanban. Граница проведена намеренно: DORA измеряет часть пути, находящуюся под контролем инженерной организации.

Разложение lead time на работу и ожидание

Практическая ценность метрики почти целиком в её разложении. Само число «9,5 дня» ничего не подсказывает; разложение показывает, что 75% интервала — это очереди, и что найм «более быстрых программистов» борется за оставшиеся 25%. Про экономику очередей и закон Литтла, который здесь работает буквально, — Kanban и управление потоком.

Change Failure Rate (CFR)

Доля деплоев в прод, которые привели к деградации сервиса и потребовали немедленного вмешательства: отката, хотфикса, патча, форварда-фикса.

$$\mathrm{CFR} = \frac{\text{число деплоев, вызвавших отказ}}{\text{общее число деплоев}}$$

Что не входит: баг, найденный через месяц в редком сценарии; инцидент из-за отказа железа или внешнего провайдера; плановая деградация. Критерий — «изменение сломало то, что работало, и это потребовало срочного действия».

Главная ловушка: знаменатель. Если считать CFR как «инциденты / деплои» и при этом инциденты регистрирует SRE-команда, а деплои считает CI, вы получите число, где числитель и знаменатель живут в разных вселенных. Нужна связка: каждый инцидент должен ссылаться на вызвавший его деплой (или явно помечаться как не связанный с изменением).

Failed Deployment Recovery Time (FDRT)

Сколько времени проходит от начала отказа, вызванного деплоем, до восстановления работы.

В отчётах 2014–2023 годов эта метрика называлась Time to Restore Service и часто по инерции записывалась как MTTR. В отчёте 2024 года DORA переименовала её именно для того, чтобы отвязать от общего MTTR инцидентов: восстановление после собственного плохого деплоя — свойство вашего конвейера (есть ли откат в один клик), а восстановление после падения дата-центра — нет.

О том, почему «MTTR» как среднее — статистически почти бессмысленная величина, отдельный разговор в части 5.

Обратите внимание на окно наблюдения: без него состояние ok недостижимо и CFR невозможно посчитать за закрытый период. Практика — привязывать окно к скорости обнаружения: если ваш алертинг ловит регресс за 15 минут, окна в час достаточно; если баг всплывает по ночному батчу — сутки.

Часть 3. Уровни производительности и как их читать

DORA группирует респондентов кластерным анализом в Elite / High / Medium / Low. Ниже — часто цитируемая версия таблицы (по данным отчётов 2019–2021), которую нужно читать с большой осторожностью.

Метрика Elite High Medium Low
Deployment frequency по требованию, несколько раз в день от раза в день до раза в неделю от раза в неделю до раза в месяц реже раза в месяц
Lead time for changes менее часа от дня до недели от недели до месяца от месяца до полугода
Время восстановления менее часа менее дня менее дня от недели до месяца
Change failure rate 0–15% 0–15% 0–15% 46–60%

Четыре предупреждения, без которых таблица приносит вред:

  1. Границы плавают год от года. Кластеры вычисляются заново на новой выборке, поэтому «Elite» 2019-го и «Elite» 2024-го — разные пороги. Использовать таблицу как список годовых целей («к декабрю выйти в Elite») — ошибка категории.
  2. Данные самоотчётные. Это опрос: инженеры сами оценивают частоту деплоев по памяти, с добровольной выборкой и понятным перекосом в сторону тех, кому DevOps интересен. Отсюда странности вроде одинакового диапазона CFR у трёх верхних кластеров.
  3. Связь с бизнесом — корреляционная. Утверждение «software delivery performance предсказывает организационную эффективность» получено на латентных конструктах и опросных данных; это не рандомизированный эксперимент, и причинность из него не следует.
  4. Elite нужен не всем. Для банковского ядра или прошивки медицинского прибора «несколько деплоев в день» — не цель. Целью остаётся направление движения: короче обратная связь, мельче партия, дешевле откат.

Актуальные отчёты и калькулятор самооценки — на dora.dev, исходные PDF — в разделе dora.dev/research.

Часть 4. Как считать: от событий к числам

Модель данных

Всё сводится к трём таблицам событий. Ключевое проектное решение — связь инцидента с деплоем; без неё CFR и FDRT посчитать честно нельзя.

Источники событий:

  • DEPLOYMENT — из CI/CD. В GitHub Actions это Deployment API, в GitLab — environments, в Argo CD — события синхронизации. Не выводите деплой из тегов git: теги ставят руками и забывают.
  • CHANGE.first_commit_at — из истории git по коммитам, попавшим в деплой (git log previous_sha..current_sha). Именно первый коммит ветки, не последний: иначе метрика будет игнорировать время, проведённое веткой в ожидании ревью.
  • INCIDENT — из PagerDuty / OpsGenie / Jira. Поле caused_by_deploy_id заполняется на разборе инцидента — это ручной шаг, и он неустраним.

Минимальный сбор в CI выглядит так:

# .github/workflows/deploy.yml — фиксация события деплоя после успешной выкатки
- name: Записать событие деплоя
  if: success()
  run: |
    # previous_sha берём из последнего успешного деплоя этого сервиса
    PREV=$(curl -sf "$METRICS_API/last-success?service=$SERVICE" | jq -r .commit_sha)
    # список коммитов, впервые доехавших до прода этой выкаткой
    COMMITS=$(git log --format='%H %aI' "$PREV..${{ github.sha }}")
    curl -sf -X POST "$METRICS_API/deployments" \
      -H 'content-type: application/json' \
      -d "$(jq -n \
            --arg svc "$SERVICE" \
            --arg sha "${{ github.sha }}" \
            --arg at  "$(date -u +%FT%TZ)" \
            --arg commits "$COMMITS" \
            '{service_id:$svc, commit_sha:$sha, deployed_at:$at,
              succeeded:true, commits:$commits}')"

Расчёт на Python

"""Расчёт четырёх ключевых метрик DORA из потока событий.

Принципы, заложенные в код:
  * никаких средних там, где распределение с тяжёлым хвостом — только процентили;
  * период агрегации фиксирован и закрыт (окно наблюдения истекло для всех деплоев);
  * метрики считаются на уровне сервиса, а не команды или организации.
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timedelta
from math import ceil


@dataclass(frozen=True)
class Deployment:
    deploy_id: str
    service_id: str
    deployed_at: datetime
    first_commit_at: datetime   # самый ранний коммит из вошедших в выкатку
    succeeded: bool


@dataclass(frozen=True)
class Incident:
    incident_id: str
    caused_by_deploy_id: str | None
    impact_started_at: datetime
    resolved_at: datetime | None


def percentile(values: list[float], p: float) -> float:
    """Процентиль методом ближайшего ранга. O(n log n) по времени, O(n) по памяти.

    Ближайший ранг выбран сознательно: он возвращает реально наблюдавшееся значение,
    а не интерполяцию между двумя измерениями, — так число проще защищать на ревью.
    """
    if not values:
        raise ValueError("пустая выборка: метрика не определена, а не равна нулю")
    ordered = sorted(values)
    rank = max(1, ceil(p / 100 * len(ordered)))
    return ordered[rank - 1]


def deployment_frequency(deploys: list[Deployment], days: int) -> float:
    """Деплоев в сутки. O(n).

    Возвращаем частоту, а не «сколько раз в неделю»: сравнивать периоды разной
    длины иначе невозможно. Для отчёта число потом переводят в диапазон DORA.
    """
    successful = sum(1 for d in deploys if d.succeeded)
    return successful / days


def lead_time_hours(deploys: list[Deployment], p: float = 50) -> float:
    """Процентиль lead time в часах. O(n log n).

    Медиана (p=50) отвечает на вопрос «как обычно», p=85 — «что мы можем обещать».
    Среднее не используем: одна ветка, провисевшая полгода, сдвинет его целиком.
    """
    durations = [
        (d.deployed_at - d.first_commit_at).total_seconds() / 3600
        for d in deploys
        if d.succeeded
    ]
    return percentile(durations, p)


def change_failure_rate(deploys: list[Deployment], incidents: list[Incident]) -> float:
    """Доля деплоев, вызвавших отказ. O(n + m).

    Считаем именно ДЕПЛОИ с отказом, а не число инцидентов: один плохой деплой,
    породивший три алерта, — это одна неудача, иначе CFR может превысить 100%.
    """
    successful = [d for d in deploys if d.succeeded]
    if not successful:
        raise ValueError("нет успешных деплоев — CFR не определён")
    ids = {d.deploy_id for d in successful}
    failed_ids = {
        i.caused_by_deploy_id
        for i in incidents
        if i.caused_by_deploy_id in ids
    }
    return len(failed_ids) / len(successful)


def recovery_times_hours(incidents: list[Incident]) -> list[float]:
    """Длительности восстановления после отказов, вызванных изменением. O(m).

    Незакрытые инциденты исключаем — но это цензурирование данных, и оно смещает
    оценку вниз. Если незакрытых много, любой процентиль по этой выборке — фикция.
    """
    return [
        (i.resolved_at - i.impact_started_at).total_seconds() / 3600
        for i in incidents
        if i.caused_by_deploy_id and i.resolved_at
    ]


def four_keys(
    deploys: list[Deployment],
    incidents: list[Incident],
    window: timedelta,
) -> dict[str, float | None]:
    """Сводка за окно. Суммарно O(n log n + m)."""
    days = window.days
    recoveries = recovery_times_hours(incidents)
    return {
        "deployment_frequency_per_day": deployment_frequency(deploys, days),
        "lead_time_p50_hours": lead_time_hours(deploys, 50),
        "lead_time_p85_hours": lead_time_hours(deploys, 85),
        "change_failure_rate": change_failure_rate(deploys, incidents),
        # None вместо нуля: отсутствие отказов — не «мгновенное восстановление»
        "recovery_p50_hours": percentile(recoveries, 50) if recoveries else None,
        "recovery_sample_size": len(recoveries),
    }

Три решения в этом коде стоят отдельного упоминания, потому что именно на них ломаются самодельные дашборды:

  • recovery_sample_size возвращается вместе с метрикой. Медиана по трём инцидентам — это не медиана, это три числа. Размер выборки должен быть виден на графике всегда.
  • None, а не 0, при отсутствии инцидентов. Ноль на дашборде читается как «восстанавливаемся мгновенно» и попадает в усреднение по организации, занижая всё вокруг.
  • Исключение — а не подмена нулём — незакрытых инцидентов. Это классическое цензурирование справа: активные, самые долгие инциденты систематически выпадают из выборки.

То же самое на SQL

-- Четыре ключевые метрики за 90 дней, по сервисам.
-- Окно наблюдения (24 ч) вычтено, чтобы не считать деплои, чей исход ещё неизвестен.
WITH win AS (
    SELECT now() - interval '90 days' AS from_ts,
           now() - interval '24 hours' AS to_ts
),
deploys AS (
    SELECT d.service_id,
           d.deploy_id,
           d.deployed_at,
           -- начало lead time: самый ранний коммит, впервые доехавший этой выкаткой
           MIN(c.first_commit_at) AS started_at
    FROM deployment d
    JOIN change c ON c.deploy_id = d.deploy_id
    CROSS JOIN win
    WHERE d.succeeded
      AND d.deployed_at BETWEEN win.from_ts AND win.to_ts
    GROUP BY d.service_id, d.deploy_id, d.deployed_at
),
failed AS (
    SELECT DISTINCT caused_by_deploy_id AS deploy_id
    FROM incident
    WHERE caused_by_deploy_id IS NOT NULL
)
SELECT
    d.service_id,
    COUNT(*) / 90.0                                        AS deploys_per_day,
    PERCENTILE_CONT(0.50) WITHIN GROUP (
        ORDER BY EXTRACT(EPOCH FROM d.deployed_at - d.started_at) / 3600
    )                                                      AS lead_time_p50_h,
    PERCENTILE_CONT(0.85) WITHIN GROUP (
        ORDER BY EXTRACT(EPOCH FROM d.deployed_at - d.started_at) / 3600
    )                                                      AS lead_time_p85_h,
    COUNT(f.deploy_id)::numeric / COUNT(*)                 AS change_failure_rate,
    COUNT(*)                                               AS sample_size
FROM deploys d
LEFT JOIN failed f USING (deploy_id)
GROUP BY d.service_id
HAVING COUNT(*) >= 10   -- меньше десяти деплоев: показывать метрику нечестно
ORDER BY lead_time_p50_h DESC;

Готовая референсная реализация от Google Cloud — открытый проект dora-team/fourkeys: парсеры вебхуков GitHub/GitLab, BigQuery-схема и дашборд. Читать его полезно даже если брать не будете — там аккуратно решены именно вопросы связывания событий.

Часть 5. Статистика, о которой обычно молчат

Почему среднее время восстановления бесполезно

Длительности инцидентов распределены с очень тяжёлым хвостом: большинство чинится за минуты, единицы тянутся часами. У таких распределений выборочное среднее нестабильно — оно скачет от единичных наблюдений, — а квартальных инцидентов у команды обычно 5–20 штук.

Штепан Давидович в работе Google «Incident Metrics in SRE: Critically Evaluating MTTR and Friends» проделал прямой эксперимент: сгенерировал две популяции инцидентов, одна из которых по построению на 10% лучше другой, и проверил, отличит ли их MTTR на реалистичных объёмах данных. Результат: при типичном числе инцидентов улучшение неотличимо от шума; чтобы уверенно поймать разницу, нужны сотни наблюдений. Практические выводы:

  • Не ставьте цель «снизить MTTR на 20% за квартал» — вы не сможете доказать, что достигли её.
  • Смотрите на распределение целиком (диаграмма разброса длительностей), а не на одну цифру.
  • Считайте более грубые, но устойчивые вещи: доля инцидентов дольше часа, число инцидентов, доля восстановлений через автоматический откат.

Та же логика применима к lead time и вообще ко всему в этой статье: работайте с процентилями и показывайте размер выборки. Техника та же, что для cycle time в Kanban.

Сколько данных нужно, чтобы заметить изменение

Простая прикидка через бутстрап, которая экономит квартал бессмысленных споров на ретро:

import random
import statistics


def bootstrap_ci(sample: list[float], p: float = 50,
                 iterations: int = 10_000, alpha: float = 0.05
                 ) -> tuple[float, float]:
    """Доверительный интервал для процентиля методом бутстрапа.

    Время: O(iterations * n log n). Память: O(iterations).
    Практический смысл: если интервалы двух кварталов пересекаются,
    заявлять об улучшении нельзя — как бы ни хотелось на демо.
    """
    n = len(sample)
    estimates = []
    for _ in range(iterations):
        resample = [random.choice(sample) for _ in range(n)]
        resample.sort()
        idx = max(0, round(p / 100 * n) - 1)
        estimates.append(resample[idx])
    estimates.sort()
    lo = estimates[int(alpha / 2 * iterations)]
    hi = estimates[int((1 - alpha / 2) * iterations)]
    return lo, hi


# Типичный результат для 12 инцидентов за квартал:
# медиана 42 мин, интервал [18, 130] — то есть «стало лучше на 15%» недоказуемо.
incidents_minutes = [12, 18, 25, 31, 38, 42, 47, 61, 95, 130, 210, 480]
print(statistics.median(incidents_minutes), bootstrap_ci(incidents_minutes))

Часть 6. Как метрики ломаются: закон Гудхарта в действии

«Когда мера становится целью, она перестаёт быть хорошей мерой». Формулировка Мэрилин Стратерн по мотивам Чарльза Гудхарта.

Метрики DORA спроектированы как диагностические, но их постоянно назначают целевыми — и каждая из них ломается предсказуемым способом.

Метрика Как её накручивают Что видно в системе
Deployment frequency пустые деплои, разбиение одного изменения на пять выкаток, no-op пайплайны растёт DF при неизменном lead time и объёме изменений
Lead time коммит делается перед самым мерджем; долгоживущая ветка «переоткрывается» p50 падает, p85 и p95 стоят на месте
Change failure rate инцидент не заводят, а «правят по-тихому»; понижают серьёзность CFR падает вместе с числом заведённых инцидентов
Recovery time таймер инцидента стартует в момент, когда починка уже почти готова сокращается время, но растёт разрыв между началом воздействия и заведением

Отсюда два практических правила.

Правило первое: никогда не сравнивайте команды по DORA. Метрики зависят от домена (платёжное ядро против внутреннего дашборда), архитектуры (монолит против сервисов) и регуляторики. Сравнение команд между собой создаёт стимул оптимизировать число, а не систему. Легитимное сравнение только одно — команда с собой во времени.

Правило второе: DORA не входит в оценку людей. Как только метрика попадает в перформанс-ревью или в бонус, качество данных умирает: инциденты перестают заводить, деплои начинают дробить. Обсуждение того, почему индивидуальные метрики производительности не работают в разработке, см. в ответе Кента Бека и Гергея Ороша на нашумевший материал McKinsey — «Measuring developer productivity? A response to McKinsey». Про мотивацию и то, что происходит с командой под измерением, — в статье Команда, коммуникации, конфликты и мотивация.

Ещё три типичные ошибки внедрения:

  1. Метрики без capability-модели. Четыре числа — это термометр. Лечат не термометром: в модели DORA есть более двух десятков практик-предикторов (транковая разработка, непрерывное тестирование, слабосвязанная архитектура, автоматизация деплоя, наблюдаемость, генеративная культура по типологии Уэструма). Улучшение метрик — следствие внедрения практик, а не самостоятельное действие.
  2. Агрегация по организации. Средний lead time по сорока командам — число, которое не может ни вырасти, ни упасть осмысленно. Агрегировать можно распределение («доля сервисов с p85 < 1 дня»), но не сами длительности.
  3. Дашборд вместо разговора. Метрика полезна ровно в тот момент, когда команда смотрит на неё и спрашивает «почему?». Панель, на которую никто не смотрит на ретро, — это затраты без эффекта.

На этой схеме: lead time — интервал B → H, deployment frequency считает события G, CFR — доля G, из которых есть переход в I, FDRT — длительность петли I → G. Всё, что слева от B, в метрики DORA не входит — и это регулярный источник спора с продуктом, который справедливо считает свои очереди частью срока.

Часть 7. За пределами четырёх метрик

DORA измеряет систему доставки. Она принципиально ничего не говорит о том, делаете ли вы нужный продукт и в каком состоянии находятся люди. Закрывать эти пробелы призваны два более поздних фреймворка.

SPACE

«The SPACE of Developer Productivity» (Форсгрен, Стори, Маддила, Циммерманн, Хоук, Батлер; ACM Queue, 2021) утверждает, что продуктивность многомерна, и предлагает пять измерений:

  • Satisfaction and well-being — удовлетворённость, выгорание;
  • Performance — результат (не активность): надёжность, влияние на пользователя;
  • Activity — счётчики: коммиты, PR, деплои;
  • Communication and collaboration — скорость ревью, качество документации, «bus factor»;
  • Efficiency and flow — прерывания, время в потоке, длина очередей.

Главная рекомендация статьи операционна: брать метрики минимум из трёх измерений и обязательно включать хотя бы одну перцептивную (то есть из опроса). Комбинация «Activity в одиночку» — ровно тот антипаттерн, с которого начиналась эта статья.

DevEx

«DevEx: What Actually Drives Productivity» (Нода, Стори, Форсгрен, Грайлер; ACM Queue, 2023) идёт дальше и описывает три фактора разработческого опыта:

  • петли обратной связи — сколько ждать ответа от системы (сборка, тесты, ревью, деплой);
  • когнитивная нагрузка — сколько нужно держать в голове, чтобы внести изменение;
  • состояние потока — как часто удаётся работать без прерываний.

Это самый практичный из трёх фреймворков для конкретных инженерных решений: он прямо указывает, что сокращение сборки с 20 до 4 минут — не «инфраструктурная задача низкого приоритета», а изменение петли обратной связи с измеримым эффектом.

Практический синтез, который работает у большинства команд: DORA + одна перцептивная метрика + одна продуктовая. Например: четыре ключевые метрики с дашборда CI, ежеквартальный короткий опрос о трении в разработке (5–7 вопросов) и одна метрика влияния на пользователя. Три источника разной природы дают триангуляцию, которую невозможно накрутить с одной стороны.

Часть 8. Как внедрять: порядок шагов

  1. Начните с одного сервиса и одной команды. Не с организации. Цель первого месяца — не улучшить метрики, а получить данные, которым команда верит.
  2. Автоматизируйте сбор деплоев и коммитов, инциденты размечайте руками. Автоматическая привязка инцидента к деплою — заманчивая, но почти всегда ошибочная идея: причинность устанавливает человек на разборе.
  3. Покажите распределения, а не одно число. Диаграмма разброса lead time с линиями p50/p85 — более полезный артефакт, чем большая цифра на дашборде.
  4. Смотрите метрики на ретроспективе, а не на статус-встрече с руководством. Вопрос, который надо задавать: «что в этом квартале было самой длинной очередью и что мы с ней сделаем?»
  5. Выберите одну capability на квартал. Например: сократить время сборки вдвое; ввести транковую разработку; сделать откат в один клик. Метрика — способ проверить гипотезу, а не сама работа.
  6. Явно зафиксируйте, что метрики не используются для оценки людей — и соблюдайте это. Один нарушенный случай стоит года доверия к данным.

Полезный стартовый инструмент — DORA Quick Check: короткая самооценка, которая ставит команду относительно выборки и подсказывает, какие практики подтягивать.

Отдельное замечание про 2024–2025 годы: отчёты DORA зафиксировали, что рост внедрения ИИ-ассистентов увеличивает объём и скорость производимых изменений, но сам по себе не улучшает стабильность доставки — узкое место просто смещается ниже по конвейеру (ревью, тестирование, выкатка). Это ровно тот случай, когда метрики полезны: они показывают, где именно образовалась новая очередь, вместо общего ощущения «стало быстрее».

Мини-итог

  • Четыре ключевые метрики — это 2×2: скорость и стабильность, каждая как частота событий и как длительность. Смотреть их поодиночке нельзя: пары уравновешивают друг друга.
  • Определения должны быть операционными до уровня SQL. Самый тонкий момент — связь инцидента с деплоем; без неё CFR и время восстановления не считаются честно.
  • Всегда процентили, никогда среднее; всегда с размером выборки. MTTR по десятку инцидентов статистически неотличим от шума — цели на его снижение недостижимы в принципе.
  • Скорость и стабильность в данных положительно коррелируют. Механизм — размер партии.
  • Уровни Elite/High/Medium/Low — описание выборки конкретного года, а не список целей.
  • Метрики диагностические, а не целевые. В перформанс-ревью они попадать не должны — иначе данные испортятся быстрее, чем улучшится процесс.
  • DORA не покрывает людей и продукт: дополняйте перцептивной метрикой (SPACE, DevEx) и хотя бы одной продуктовой.

Источники


Что дальше

Мы научились измерять систему доставки одной команды и увидели, что почти все ответы прячутся в очередях между этапами. Когда команд становится не одна, а тридцать, очереди возникают уже между командами — и отрасль отвечает на это готовыми фреймворками масштабирования, у каждого из которых своя цена. Об этом — следующая статья:

Масштабирование процессов: SAFe, LeSS, Spotify-модель и их критика

Если нужен фундамент по потоку и процентилям, на котором стоит вся эта статья — см. Kanban и управление потоком; про релизный конвейер и Definition of Done — Качество, Definition of Done и релизный менеджмент; общая карта трека — Управление проектами в IT.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов