Цифра, о которой никто не хочет говорить
Неформальное code review выявляет от 15 до 30 процентов дефектов, присутствующих в проверяемом коде. Это не мнение. Это вывод, который был воспроизведён на протяжении четырёх десятилетий, в нескольких компаниях и в десятках исследований.
Майкл Фейган задокументировал это в IBM в 1976 году. Исследование 1987 года в AT&T Bell Labs обнаружило 20 процентов. Исследование HP 1996 года обнаружило 25 процентов. Статья Microsoft Research 2013 года о современном code review нашла примерно тот же диапазон. Инструменты изменились с перфокарт на GitHub, но кривая человеческой эффективности не сдвинулась.
Если ваша команда считает, что pull request review — это ваш основной барьер качества, данные говорят, что вы находите примерно каждый четвёртый баг. Остальные три уходят в продакшн.
Откуда берутся эти цифры
Изначальная методология Фейгана была простой и жёсткой. Он внедрял известные дефекты в код, запускал процесс ревью и считал, сколько из них нашли ревьюеры. Затем он сравнивал это с общим числом дефектов, которые команда в итоге обнаружила через тестирование, инциденты в продакшне и отчёты клиентов. Отношение числа найденных-на-ревью к общему-числу-дефектов стало removal rate.
Ключевой инсайт в том, что знаменатель имеет значение. Ревью, которое находит десять дефектов, звучит неплохо, пока вы не узнаете, что в файле было пятьдесят. Фейган измерял полный знаменатель. Большинство команд сегодня этого не делают.
Последующие исследования использовали схожие подходы. Исследователи внедряли дефекты, сравнивали типы ревью или отслеживали дефекты в обратном направлении от продакшна, чтобы увидеть, где они могли быть пойманы. Результаты группируются плотно:
| Тип ревью | Defect removal rate | Ключевые исследования |
|---|---|---|
| Без ревью | 0% | Базовый уровень |
| Неформальное / PR review | 15-30% | Fagan 1976, Porter 1995, Microsoft 2013 |
| Структурированный walkthrough | 30-50% | Yourdon 1979, Weller 1993 |
| Fagan inspection | 60-90% | Fagan 1976, Russell 1991, NASA 2002 |
Разрыв между неформальным и структурированным ревью невелик. Это 3-кратная разница в defect escape rate.
Почему PR review работает так плохо
Проблема не в том, что ревьюеры плохо справляются со своей работой. Проблема в том, что pull request review не предназначен для поиска дефектов. Он предназначен для того, чтобы два человека согласились, что код можно смерджить.
Вот что на самом деле происходит в типичном PR review. Ревьюер открывает diff. Читает описание, просматривает добавления, проверяет, проходят ли тесты, и ищет что-то явно неправильное. Это занимает от пяти до пятнадцати минут. Затем он аппрувит.
Процесс оптимизирован на скорость, а не на тщательность. Нет времени на подготовку. Ревьюер не читал окружающий код, не отслеживал поток данных и не строил ментальную модель изменения. Он реагирует на diff на экране, а человеческий мозг ужасно находит баги в таком формате.
Исследование 2015 года Баккелли и Бёрда в Microsoft показало, что самые распространённые категории комментариев к ревью вообще не были связаны с дефектами. Верхние категории — вопросы о намерениях, запросы на уточнение и предложения по улучшению. Фактические находки дефектов составляли меньшинство комментариев. Инструмент функционировал как канал коммуникации, а не как quality gate.
Это нормально, если вы знаете, что делает инструмент. Это опасно, если вы думаете, что он делает что-то другое.
Чем структурированное ревью отличается
Fagan inspections и другие методы структурированного ревью достигают более высоких removal rates, изменяя дизайн процесса, а не людей.
Самый большой рычаг — индивидуальная подготовка. На Fagan inspection каждый ревьюер тратит сфокусированное время на материал до любой групповой дискуссии. Фейган обнаружил, что подготовленные инспекторы находили примерно вдвое больше дефектов, чем те, кто приходил с ходу. Модель PR по умолчанию — это модель прихода-с-ходу.
Второй рычаг — темп. Фейган рекомендовал 100–125 строк кода в час ревью. Большинство PR-ревьюеров обрабатывают в десять раз больший темп. Скорость убивает обнаружение. Ваш мозг заполняет ожидаемые паттерны вместо того, чтобы читать то, что на самом деле там есть.
Третий рычаг — фокус. Встречи Фейгана имеют единственную цель: логировать дефекты. Никаких дизайн-дебатов. Никаких предложений решений. Никаких обсуждений стиля. PR-треды рутинно уходят в архитектурные мнения, что потребляет тот же когнитивный бюджет, который мог бы найти null dereference.
Четвёртый рычаг — роль читателя. Когда кто-то, кто не писал код, пересказывает его вслух, это заставляет группу обрабатывать на скорости понимания, а не скорости сканирования. Diff на экране позволяет глазам пропускать скучные части. Человек, который говорит, не пропускает.
Аргумент о стоимости перевернут вверх ногами
Обычное возражение против структурированного ревью — стоимость. Fagan inspection требует четырёх–шести человеко-часов на несколько сотен строк кода. PR review занимает пятнадцать минут времени одного человека. Подсчёт кажется очевидным.
Он очевиден, и он неверен.
Фейган также измерял стоимость нахождения дефектов на разных этапах. Дефект, найденный во время inspection, стоил примерно в десять раз меньше, чем найденный во время тестирования. Когда он уходил в продакшн, соотношение росло до двадцати или тридцати к одному. Исследование NASA 2002 года показало, что каждый час, потраченный на inspection, предотвращал в среднем 33 часа сопроводительной работы позже.
Экономия невидима. Вы не можете измерить баг, который предотвратили. Стоимость встречи немедленна и очевидна. Поэтому организации оптимизируют видимую скорость вместо невидимого качества, даже когда данные говорят, что в итоге это обходится им дороже.
Данно-ориентированный компромисс
Вам не нужна культура встреч IBM, чтобы получить большую часть выгоды. Вам нужно позаимствовать те черты процесса, которые реально двигают цифру, и отбросить те, которые не двигают.
Вот что данные говорят, что имеет значение:
-
Время подготовки. Требуйте, чтобы ревьюеры тратили время на код до комментирования. Даже десять минут сфокусированного чтения лучше, чем беглый просмотр.
-
Ограничения темпа. Для критических файлов вводите максимальную скорость ревью. Если ревьюер аппрувит изменение в 500 строк за пять минут, это данные, а не diligence.
-
Фокус только на дефектах. Отделяйте feedback по стилю и архитектуре от охоты на дефекты. Используйте автоматические форматтеры для первого. Резервируйте человеческое внимание для второго.
-
Чек-листы. Фейган обнаружил, что ревьюеры, работающие по чек-листу, находили дефекты, мимо которых проходили ревьюеры, работающие по интуиции. Чек-лист существует потому, что экспертиза создаёт слепые зоны.
Вот лёгкий скрипт, который измеряет глубину ревью из истории Git. Он оценивает, было ли у ревью достаточно времени, чтобы быть тщательным:
#!/usr/bin/env python3
"""Estimate review depth from git history."""
import subprocess
import sys
from datetime import datetime, timezone
def get_commit_info(commit_hash: str) -> dict:
"""Return author, committer, and timestamps for a commit."""
fmt = "%H|%an|%cn|%ad|%cd"
result = subprocess.run(
["git", "log", "-1", f"--format={fmt}", commit_hash],
capture_output=True,
text=True,
check=True,
)
parts = result.stdout.strip().split("|")
return {
"hash": parts[0],
"author": parts[1],
"committer": parts[2],
"author_date": datetime.strptime(parts[3], "%a %b %d %H:%M:%S %Y %z"),
"commit_date": datetime.strptime(parts[4], "%a %b %d %H:%M:%S %Y %z"),
}
def get_lines_changed(commit_hash: str) -> int:
"""Count total lines added + deleted in a commit."""
result = subprocess.run(
["git", "diff", f"{commit_hash}^", commit_hash, "--stat"],
capture_output=True,
text=True,
check=True,
)
# Last line of --stat contains totals like "3 files changed, 42 insertions(+), 7 deletions(-)"
for line in reversed(result.stdout.strip().splitlines()):
line = line.strip()
if "insertions" in line or "deletions" in line:
# Extract numbers roughly
parts = line.split(",")
total = 0
for part in parts:
digits = "".join(ch for ch in part if ch.isdigit())
if digits:
total += int(digits)
return total
return 0
def estimate_review_depth(commit_hash: str) -> dict:
"""Estimate whether a commit had time for thorough review.
Returns lines changed, time between author and commit dates
(a rough proxy for review duration), and a verdict.
"""
info = get_commit_info(commit_hash)
lines = get_lines_changed(commit_hash)
# Time between author date and commit date is a proxy for review time
# In many workflows, commit date reflects when the merge happened
review_seconds = (info["commit_date"] - info["author_date"]).total_seconds()
review_hours = review_seconds / 3600
# Fagan recommended 100-125 lines/hour for thorough review
fagan_rate = 125
needed_hours = lines / fagan_rate if lines else 0
verdict = "insufficient"
if review_hours >= needed_hours:
verdict = "adequate"
if review_hours >= needed_hours * 2:
verdict = "thorough"
return {
"hash": commit_hash[:8],
"lines": lines,
"review_hours": round(review_hours, 2),
"needed_hours": round(needed_hours, 2),
"verdict": verdict,
}
if __name__ == "__main__":
commit = sys.argv[1] if len(sys.argv) > 1 else "HEAD"
result = estimate_review_depth(commit)
print(f"Commit: {result['hash']}")
print(f"Lines: {result['lines']}")
print(f"Review time: {result['review_hours']} hours")
print(f"Fagan time: {result['needed_hours']} hours")
print(f"Verdict: {result['verdict']}")
Сохраните как review_depth.py и запустите:
python review_depth.py abc1234
Вывод скажет вам, был ли у коммита достаточно времени ревью, чтобы быть тщательным по стандарту Фейгана. Большинство коммитов скажут insufficient. В этом и суть. Данные говорили нам это десятилетиями, а мы продолжаем строить более быстрые пайплайны вместо лучших.
Что это значит для вашей команды
Pull request review не бесполезен. Он строит общий контекст, распространяет знания и ловит очевидные ошибки. Но данные однозначны в том, чего он не делает. Он не ловит большинство дефектов.
Если ваша стратегия качества полагается на PR review как на основной фильтр, вы фильтруете ситом. Removal rate в 15–30 % — это не провал ваших ревьюеров. Это свойство процесса.
Команды, которые превосходят эту цифру, не нанимают более умных людей. Они меняют процесс. Добавляют время подготовки, вводят ограничения темпа, отделяют поиск дефектов от обсуждения дизайна и используют чек-листы, чтобы направлять внимание туда, где интуиция промахивается.
Вам не нужна полная Fagan inspection для каждого diff. Вам нужно знать, чего на самом деле достигает ваш текущий процесс, и перестать притворяться, что он достигает большего.
FAQ
Что данные говорят об эффективности pull request review?
Множество исследований в IBM, AT&T, HP и Microsoft последовательно находят, что неформальное code review выявляет 15–30 % дефектов, присутствующих в коде. Этот диапазон оставался стабильным с 1970-х годов до современных исследований рабочих процессов на базе GitHub.
Почему pull request reviews находят так мало дефектов?
PR review оптимизирован на скорость и аппрув мержа, а не на систематическое обнаружение дефектов. Ревьюеры обычно тратят 5–15 минут на ревью, не имеют времени на подготовку, обрабатывают код в 10 раз быстрее рекомендуемого исследованиями темпа, а формат поощряет беглый просмотр вместо глубокого анализа.
Насколько лучше структурированные ревью, такие как Fagan inspections?
Fagan inspections последовательно сообщают о defect removal rate в 60–90 %, примерно в 3–4 раза лучше, чем неформальное ревью. Разница исходит от индивидуальной подготовки, принудительных ограничений темпа, разделения ролей, фокуса на основе чек-листа и встреч, которые логируют дефекты вместо обсуждения решений.
Какой самый дешёвый способ улучшить эффективность PR review?
Требуйте индивидуальной подготовки перед комментированием, используйте чек-листы, адаптированные к типичным типам дефектов вашей команды, отделяйте feedback по стилю от охоты на дефекты и ограничивайте скорость ревью для критических файлов. Даже небольшие изменения в подготовке и фокусе могут значительно сдвинуть цифру без добавления оверхеда встреч.
Как измерить фактический defect removal rate моей команды?
Отслеживайте дефекты, найденные на ревью, против дефектов, найденных позже в тестировании или продакшне. Отношение рано-найденных к общему-числу-найденных — это ваш removal rate. Большинство команд этого не отслеживают, поэтому они переоценивают эффективность ревью. Начните логировать, где был найден каждый дефект, затем считайте отношение ежемесячно.