Начну с неоднозначного утверждения, которое, вероятно, вызовет возмущение: ревью кода, как их проводит большинство команд, — колоссальная трата времени. Прежде чем хвататься за вилы и факелы, выслушайте меня. Я был по обе стороны баррикад — и в роли ревьювера, погрязшего в diff, и в роли разработчика, который ждёт одобрения, пока его блестящий код устаревает.

Неудобная правда заключается в том, что мы превратили ревью кода в подобие карго-культа. Мы проводим их, потому что «лучшие практики» говорят, что так надо, а не потому, что они действительно улучшают наш код или делают наши команды продуктивнее. Пришло время честно поговорить об этой священной корове разработки ПО.

Скрытая экономика театра ревью кода

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

Рассмотрим типичный сценарий:

# Разработчик пишет код
time_to_write = 4  # часов
# Время на подготовку к ревью
time_to_prepare = 0.5  # часов (сообщения о коммитах, документация)
# Время ожидания ревью
time_waiting = 24  # часов (в среднем 1 день)
# Время ревьювера
time_to_review = 1  # час
# Время на устранение обратной связи
time_to_address = 2  # часа
# Общее календарное время
total_calendar_time = time_to_write + time_to_prepare + time_waiting + time_to_address
# Результат: 30,5 часов за 4 часа фактической работы над разработкой

Но подождите, это ещё не всё! Здесь не учитываются затраты на переключение контекста, умственные издержки на отслеживание множества ожидающих ревью или совокупные задержки, когда ревью порождают новые ревью.

graph TD A[Разработчик пишет код] --> B[Создаёт PR] B --> C[Ожидает ревьювера] C --> D{Результат ревью} D -->|Нужны изменения| E[Переключение контекста назад] E --> F[Вносит изменения] F --> C D -->|Одобрено| G[Слияние] C --> H[Переключение контекста ревьювера] H --> I[Время на ревью] I --> D style C fill:#ffcccc style H fill:#ffcccc style E fill:#ffcccc

Великий механизм прерываний

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

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

Дилемма разработчика:

  1. Немедленный ответ: бросить всё, потерять состояние потока, потратить 30 минут на ревью кода, затем ещё 30 минут на возвращение в исходный контекст.
  2. Отложенный ответ: продолжать работать, но с осознанием, что кто-то ждёт вашего ревью, постепенно накапливая чувство вины и давление.

Ни один из вариантов не хорош. Немедленный ответ уничтожает продуктивность. Отложенный ответ создаёт трения в команде и замедляет доставку.

Чума «LGTM»: когда ревью становятся штампами

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

Я видел эту схему бесчисленное количество раз:

// Исходная реализация
function calculateTotal(items) {
    let total = 0;
    for (let i = 0; i < items.length; i++) {
        total += items[i].price * items[i].quantity;
        // TODO: Добавить расчёт налога
        // TODO: Обработать скидки
        // TODO: Проверить ввод
    }
    return total;
}
// Комментарий ревью: "LGTM 👍"

Ревьювер потратил, может быть, 30 секунд, просматривая код, заметил, что он «выглядит работаспособным», и одобрил его. Он пропустил:

  • Неполную логику расчёта налогов.
  • Отсутствующую проверку ввода.
  • Отсутствие обработки ошибок для недопустимых значений цены/количества.
  • Последствия для производительности при больших массивах товаров.

Это не злонамеренность — это человеческая природа под давлением. Когда у вас свои сроки и ещё пять ревью в очереди, тщательность становится роскошью, которую вы не можете себе позволить.

Убывающая отдача от дополнительных ревьюверов

Здесь команды часто удваивают усилия в неверном направлении. Если один ревьювер не обнаруживает достаточно проблем, наверняка два ревьювера будут лучше, верно? А если два — хорошо, то три будет ещё лучше?

Спойлер: нет, не будет.

Исследования показывают, что один ревьювер находит примерно половину дефектов самостоятельно. Второй ревьювер находит примерно вдвое меньше новых проблем, чем первый. Дальше вы вступаете в область резко убывающей отдачи, где больше перекрытий, чем пользы.

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

# Математика эффективности ревьюверов
reviewers = [1, 2, 3, 4, 5]
defects_found = []
for num_reviewers in reviewers:
    if num_reviewers == 1:
        defects = 50  # Базовый случай: 50% дефектов найдено
    elif num_reviewers == 2:
        defects = 50 + 25  # Второй ревьювер находит на 25% больше
    else:
        # Убывающая отдача с социальным лодырничеством
        defects = 75 + (num_reviewers - 2) * 5
    defects_found.append(min(defects, 90))  # Ограничение на 90% эффективности
# Результат: [50, 75, 80, 85, 90]
# Стоимость за дополнительную найденную ошибку стремительно растёт после двух ревьюверов

Мираж асинхронности: когда «гибкие» ревью становятся узкими местами

Современные команды в основном отказались от формальных встреч по ревью кода в пользу асинхронных, инструментальных ревью. Это звучит отлично в теории — без конфликтов по расписанию, ревьюверы могут работать в удобное время, разработчики не сидят часами в конференц-залах.

Но асинхронные ревью создают свои проблемы:

Проблема очереди ревью:

  • Запросы на включение накапливаются в очередях ревьюверов.
  • Старые PR устаревают и требуют повторного базового слияния.
  • Со временем становится сложнее сохранять контекст.
  • Авторы забывают нюансы собственного кода.

Штраф за переключение контекста:

  • Ревьюверы объединяют ревью, чтобы минимизировать прерывания.
  • Но объединение приводит к задержкам.
  • Задержки приводят к давлению на более быстрые и менее тщательные ревью.

Когда ревью кода действительно работают (сюжетный поворот!)

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

Ревью кода наиболее эффективны, когда они:

  1. Обучающие, а не контрольные.
  2. Ориентированы на дизайн и архитектуру, а не на синтаксис.
  3. Выполняются нужными людьми в нужное время.
  4. Дополняют другие практики обеспечения качества, а не являются основным контрольным этапом.

Вот лучший подход:

# Вместо того чтобы рассматривать эту деталь реализации:
def process_user_data(user_id):
    user = get_user(user_id)
    if user is None:
        return {"error": "User not found"}
    # ... 50 ещё строк логики обработки
# Рассмотрите это дизайнерское решение:
"""
Архитектурное решение: конвейер обработки данных пользователя
Контекст: нам нужно обработать данные пользователя для новой системы рекомендаций
Решение: реализовать синхронный конвейер обработки с следующими этапами:
1. Валидация данных пользователя.
2. Извлечение предпочтений.
3. Генерация рекомендаций.
4. Кэширование результатов.
Рассмотренные альтернативы:
- Асинхронная обработка (отклонена из-за требований реального времени).
- Внешний сервис (отклонен из-за чувствительности данных).
Фокус ревью: правильно ли выбран архитектурный подход?
"""