Время ожидания pull request и настоящая причина медленной поставки
Время ожидания pull request часто скрывает настоящую причину медленной поставки. Узнайте, что измерять в ревью, повторных прогонах тестов и согласовании релизов.

Почему команды выпускают медленно, даже если код пишут быстро
Команда может быстро писать код и всё равно медленно выпускать изменения. Обычно тормоз начинается уже после того, как работа уходит с экрана разработчика. Задача переходит в ревью во вторник, ждёт до четверга, в пятницу наталкивается на нестабильный тест и уходит на следующий релиз уже на следующей неделе.
Вот почему поставка кажется медленной, даже когда никто не печатает медленно. Люди делают свою часть, но работа встаёт в очередь. Основатели и менеджеры видят усилия, стендапы и слитые ветки, а пользователи всё равно слишком долго ждут изменений.
Ревью часто становится первой скрытой очередью. Инженер открывает pull request за 20 минут, а потом день ждёт, пока кто-то его посмотрит. Если у reviewer'а встречи, поддержка или куча другой работы, код перестаёт двигаться. В этот момент время ожидания pull request важнее, чем скорость программирования.
Потом появляется ещё одна очередь — тестирование. Неудачная проверка не всегда означает, что код плохой. Иногда падает один нестабильный тест, кто-то перезапускает весь набор, и команда теряет ещё два часа. В это время ничего нового не создаётся. Работа просто ждёт зелёных проверок.
Затем появляется релизный барьер. Многие команды считают задачу завершённой, как только код слит. Пользователям этот момент не важен. Им важно, когда изменение попадает в production, а задержки на согласование релиза могут сильно отодвинуть этот момент от настоящего завершения.
Такие задержки легко пропустить, потому что они не выглядят драматично. Ничего не лежит. Никто явно не заблокирован. Команда просто проводит день в ожидании reviewer'а, ожидании повторного прогона тестов, ожидании окна релиза или ожидании последнего согласования.
Небольшая продуктовая команда может терять так дни и даже не замечать этого. Два разработчика могут закрыть пять задач за неделю, но если каждая из них ждёт по шесть часов в ревью и полдня на релиз, поток поставки всё равно ощущается тяжёлым.
Поэтому обвинять команду — обычно не лучший первый шаг. Нанимать ещё инженеров часто только увеличивает очередь, если реальная проблема начинается уже после написания кода. Прежде чем называть команду медленной, посмотрите на ожидания между «готово в коде» и «вышло для пользователей».
Где теряется время после открытия pull request
Pull request может выглядеть почти готовым часами или даже днями, пока к нему никто не прикасается. Если считать только время программирования, вы пропускаете ту часть, которая тормозит всю команду.
Первая задержка обычно начинается ещё до первого ревью. Разработчик заканчивает работу, открывает pull request и ждёт. Если reviewer'ы на встречах, работают в другом часовом поясе или уже завалены очередью, изменение никуда не движется. На бумаге команда выглядит занятой. На деле работа просто припаркована.
Затем появляется вторая очередь. Автор быстро исправляет комментарии, но обновлённый pull request всё равно нужно посмотреть ещё раз. Команды часто пропускают эту задержку, потому что код уже менялся один раз, и кажется, что прогресс идёт. Иногда так и есть. Иногда исправление лежит без движения до следующего дня.
Тесты добавляют ещё один слой ожидания. Многие команды теряют часы из-за нестабильных проверок, медленных пайплайнов или повторных запусков «на всякий случай». Один нестабильный тест может держать маленькое изменение открытым намного дольше, чем оно того заслуживает.
Последнее ожидание часто связано с тем, кто даёт разрешение на релиз. Pull request может быть одобрен и всё равно не попасть в релиз, потому что окончательное согласование зависит от одного человека. Если он занят, спит или не уверен, уже одобренная работа снова попадает в очередь.
Такой сценарий постоянно встречается в небольших стартапах. Два разработчика могут закончить работу к обеду, но если ревью, повторные прогоны и согласование релиза всё тормозят, в этот день ничего не выйдет.
Что измерять, прежде чем обвинять команду
Большинство команд обвиняют скорость программирования, потому что её видно. Но более медленная часть обычно — это ожидание. Если вы хотите честно оценить поставку, измеряйте промежутки между действиями, а не только время, которое люди тратят на написание кода.
Начните с пяти показателей:
- Время от открытия pull request до первого человеческого ревью
- Время от запроса изменений до следующего ревью
- Количество повторных запусков тестов на один pull request
- Время от merge до согласования релиза
- Доля pull request, которые простаивают больше одного дня на любом этапе
Эти цифры рассказывают более понятную историю, чем story points или строки кода. Разработчик может закончить изменение за два часа, а потом потерять целый день в ожидании ревью, ещё один день в ожидании повторного просмотра и дополнительное время после merge, пока тормозит согласование релиза.
Смотрите на медиану, а не только на среднее значение. Один pull request, который висел неделю, может скрыть обычный повседневный шаблон. Ещё полезно разделять показатели по команде, репозиторию или размеру изменения. Небольшой багфикс не должен ждать так же, как рискованное инфраструктурное изменение.
Простой пример хорошо показывает суть. Если первое ревью занимает 14 часов, повторное ещё 11, тесты перезапускаются три раза, а согласование релиза добавляет ещё 9 часов, у команды нет проблемы со скоростью программирования. У неё проблема с очередями.
Это различие важно. Больше инженеров не исправит медленные ревью, нестабильные тесты или шаг согласования, с которым по пятницам справляется только один человек. Эти цифры делают задержку видимой, а когда её видно, можно исправить именно то место, где она возникает.
Как собрать эти цифры за одну неделю
Возьмите одну команду и один недавний спринт. Не берите сразу всю компанию. Одна команда даст чистую картину, а один спринт — достаточно данных, чтобы увидеть закономерность, не превращая всё это в месячный проект.
Начните с уже имеющихся временных меток. Выгрузите время открытия pull request, время первого ревью, время одобрения, время merge, время начала и окончания pipeline, неудачные запуски, повторные запуски и временные метки согласования релиза. Если команда работает в GitLab или похожем инструменте, большая часть этих данных уже есть в репозитории и CI-логах.
Разместите каждый pull request в одной строке таблицы. Отслеживайте несколько простых моментов: когда открылся pull request, когда было первое человеческое ревью, когда автор отправил изменения после комментариев, когда pull request одобрили и слит, и что происходило в pipeline до релиза.
Такой простой формат делает ожидания видимыми. Если pull request лежал без движения 19 часов, это и есть время ожидания. Если разработчик дважды отправлял исправления после комментариев, это уже доработка. Если тесты три раза не проходили до успешного запуска, это тоже не время программирования.
Оставляйте подписи простыми. Помечайте каждый промежуток как ожидание, доработка или согласование. Идеальная таксономия не нужна. Нужна достаточная структура, чтобы перестать смешивать всё прошедшее время в одно размытое число.
Потом разберите таблицу вместе с командой, которая выполняла работу. Не превращайте её в оценочную ведомость для руководства до разговора с людьми, которые в этом участвовали. Задавайте простые вопросы. Какие ожидания были нормальными? Какие казались лишними? Почему тесты так часто перезапускались? Почему согласование релиза зависло до пятницы после обеда?
Этот разговор обычно важнее самой таблицы. Цифры показывают, куда ушло время. Команда объясняет, почему.
Простой пример из небольшой продуктовой команды
Представьте команду из трёх инженеров, которая делает SaaS-продукт. Они выпускают код каждый день, и никто не выглядит без дела. На бумаге output кажется нормальным. На практике релизы всё время сдвигаются сначала на пару дней, потом на неделю и дальше.
Команда открывает примерно шесть pull request в день. Большинство из них часами ждут, пока их кто-то посмотрит, потому что один senior engineer отвечает почти за все ревью. Этот же инженер чинит production-проблемы и отвечает на вопросы продукта, поэтому очередь ревью быстро растёт. Сам код может писаться два часа, но время ожидания pull request растягивается до целого дня.
Следующая задержка приходит из тестов. Pull request падает, кто-то перезапускает pipeline и снова ждёт. Иногда тесты проходят со второй попытки. Иногда падают в другом месте. После двух или трёх повторных запусков инженер переключается на другую задачу, теряет контекст и возвращается позже. Время программирования не изменилось, но день распался на мелкие куски.
Релизы добавляют ещё одну очередь. Менеджер утверждает production-релизы только дважды в неделю, потому что так кажется безопаснее и проще контролировать. Поэтому даже после того, как pull request просмотрели, исправили, слити и повторно протестировали, он всё ещё может ждать следующего окна согласования. Изменение, завершённое во вторник днём, может не попасть к пользователям до пятницы.
К концу недели команда чувствует себя медленной и перегруженной. Это чувство реально, но причину легко не заметить. Никто не пишет меньше кода. Никто внезапно не стал хуже выполнять работу. Задержка возникает из-за ожидания.
Вот почему узкие места в code review часто выглядят как проблема скорости инженерной команды, хотя на самом деле это проблема потока работы. Если бы эта команда измеряла время ожидания ревью, число повторных запусков и задержку согласования в течение одной недели, она бы увидела, где именно поставка буксует.
Что на самом деле говорят повторные запуски тестов
Когда время ожидания pull request растёт, повторные запуски тестов часто объясняют больше, чем сам diff. Повторный запуск не всегда означает, что кто-то написал плохой код. Многие повторные запуски случаются потому, что тестовый набор, runner или окружение ведут себя по-разному от одного запуска к другому.
Нестабильные тесты — самый очевидный пример. Код остаётся тем же, но один запуск падает, а следующий проходит. Команды быстро привыкают к плохой привычке: сначала перезапустить, потом разбираться. Это может сэкономить пять минут один раз, но за спринт оно учит всех игнорировать красные сборки.
Длинные тестовые наборы создают более тихую проблему. Небольшая правка может ждать 20 или 30 минут полного pipeline. Если одна проверка падает ближе к концу, эта маленькая задача остаётся в ревью ещё дольше. Люди переключаются, теряют контекст и возвращаются медленнее.
Ручные повторные запуски часто указывают на нестабильное окружение. Общая тестовая база может сохранять старые данные между заданиями. У runner'а может заканчиваться диск или память. Сетевой вызов к другому сервису может тайм-аутиться без всякой продуктовой причины. Если одна и та же ветка проходит со второй попытки, сначала проверьте окружение, а уже потом обвиняйте разработчика.
Постоянно повторяются несколько шаблонов. Один rerun на множество веток обычно означает нестабильные тесты. Ошибки в одной и той же стадии часто указывают на слабое место в pipeline. Зелёный результат только после ручных повторов обычно означает, что процесс прячет шум вместо того, чтобы давать чистый сигнал. А когда команды видят повторяющиеся циклы fail-pass-fail, они перестают доверять результату тестов.
Отслеживайте, как часто люди перезапускают задания, сколько времени добавляет каждая попытка и какие проверки падают чаще всего. Эти цифры обычно приводят к реальному исправлению: сократить набор, изолировать нестабильные тесты или привести в порядок окружение, чтобы первый результат что-то значил.
Почему согласование релиза создаёт вторую очередь
Pull request может быть слит к полудню и всё равно не попасть в релиз ещё два дня. Код уже готов, тесты прошли, ревью закрыто, а пользователи всё ещё ничего не получают. Этот промежуток — вторая очередь.
Проблема становится хуже, когда согласовать релиз могут только один-два человека. Если они на встречах, в поездке или чинят другую проблему, все ждут. Изменение, которое делали один день, может выходить три дня, даже если после merge к коду никто не прикасался. Обычный пример — merge в пятницу, который ждёт согласования до понедельника.
Ручные проверки релиза тоже тратят время, когда они просто повторяют уже проделанную работу. Кто-то ещё раз читает задачу, перепроверяет те же результаты тестов, снова просит скриншоты или запрашивает свежую сводку в чате. Это может казаться безопасным, но часто это дублирующее ревью под новым названием.
Это обычно видно довольно быстро. Слитые pull request накапливаются перед окном релиза. Согласующие тратят время на сбор контекста вместо того, чтобы просто сказать да или нет. Команды начинают проталкивать «срочные» изменения мимо обычного пути релиза.
Когда это происходит часто, люди перестают доверять процессу. Разработчики держат изменения у себя дольше, потому что заранее считают, что релиз всё равно будет медленным. Продуктовая команда перестаёт верить срокам. Согласующие чувствуют давление и начинают играть роль страховочной сетки для всего подряд, поэтому просят ещё больше ручных подтверждений.
Лучший этап согласования должен быть коротким и конкретным. Решите, что требует человеческого подтверждения, а что нет. Если ревью, автоматические тесты и проверки rollout уже закрывают риск, не проверяйте это ещё раз на этапе релиза. Измеряйте время от merge до согласования, а не только время ожидания pull request.
Ошибки, из-за которых цифры бесполезны
Плохие метрики приводят к плохим решениям. Команда может выглядеть медленной на бумаге, хотя код движется нормально, а настоящая задержка сидит в очередях ревью, нестабильных тестах или на релизном шаге, который никто не отслеживает.
Story points и строки кода — частые ловушки. Story points показывают, как человек оценил работу. Строки кода показывают, сколько текста изменилось. Ни то ни другое не говорит, сколько pull request ждал в очереди, сколько раз CI запускался снова или сколько времени согласование лежало у одного человека.
Ещё одна ошибка — смешивать срочные hotfix'ы с обычной работой над фичами. Hotfix'ы часто смотрят за минуты, потому что все бросают текущие дела. Фичи такой роскоши не получают. Если объединить всё в один отчёт, цифры будут выглядеть лучше, чем реальный ежедневный опыт.
Храните обычные pull request по фичам, срочные исправления production и изменения только для релиза в разных группах. Иначе быстрые исключения скрывают медленную рутину.
Средние значения тоже скрывают боль. Если пять pull request проходят за два часа, а один ждёт три дня, среднее всё ещё может выглядеть нормально. Команда, которая ждала три дня, с этим не согласится. Отслеживайте медиану, но смотрите и на медленный край диапазона, например 90-й перцентиль или самые долгие ожидания за неделю.
С повторными запусками тестов нужна та же осторожность. Руководители часто видят повторные CI-запуски и считают, что разработчик написал плохой код. Иногда это правда. Но часто — нет. Повторный запуск может быть связан с нестабильным тестом, медленным общим runner'ом, проблемой тайминга или окружением, которое один раз падает, а на следующей попытке проходит. Если каждый rerun считать ошибкой разработчика, вы наказываете не ту проблему и пропускаете исправление.
Самая дорогая ошибка — нанять людей до того, как вы исправите очередь. Больше инженеров создают больше pull request. Они не делают ревью быстрее, не улучшают тестовую инфраструктуру и не ускоряют согласование релиза. В небольшой продуктовой команде один перегруженный reviewer или один ручной шаг согласования может блокировать всех.
Хорошие данные разделены по типу работы, ясно показывают длинные ожидания и отделяют человеческую задержку от системной. Если ваши цифры не умеют этого делать, они будут уводить вас к неверному решению.
Быстрая проверка для следующего спринта
Возьмите в качестве выборки один спринт. Пока ничего не меняйте в процессе. Просто отслеживайте, где работа стоит без движения.
Начните с pull request, которые дошли до merge в прошлом спринте. Для каждого отметьте пять моментов: когда pull request открылся, когда началось первое ревью, когда тесты прошли, когда он был слит и когда кто-то одобрил его для релиза. Так вы получите реальное значение времени ожидания pull request, а не догадку.
Потом посчитайте несколько простых вещей:
- Сколько pull request ждали первого ревью больше 24 часов
- Сколько раз один pull request проходил полный повторный прогон тестов больше одного раза
- Сколько времени заняло согласование от merge до релиза
Не спешите слишком быстро переходить к средним значениям. Один pull request, который лежал без движения 40 часов, может сказать больше, чем пять, прошедших за несколько минут. Смотрите на самый длинный простой на всём пути. Если программирование заняло 6 часов, ревью ждало 28, а согласование релиза — 18, вы уже знаете, где живёт тормоз.
Небольшая команда может сделать это в таблице меньше чем за час. Если вам нужно одно простое правило на следующий спринт, выберите то, которое сокращает самый большой блок ожидания, а не самую громкую жалобу. Например: каждый pull request получает первое ревью в тот же рабочий день, каждый второй полный повторный прогон тестов требует короткой причины в pull request, или низкорисковые изменения выходят без отдельного совещания на согласование.
Выберите одно правило, а не пять. Применяйте его один спринт, а потом снова проверьте те же цифры. Если ожидание сократилось, оставляйте правило. Если нет, меняйте правило и проверяйте следующий узкий участок.
Что делать дальше
Выберите один продукт, один репозиторий и один путь релиза. Неделя чистых данных по одному workflow скажет вам больше, чем месяц мнений от всей компании.
Потом исправляйте самое медленное звено сначала. Если программирование занимает два часа, а pull request полдня лежит до первого ревью, проблема не в численности команды. Если тесты падают, перезапускаются и снова падают по обычным причинам, такое ожидание часто дешевле исправить, чем нанимать ещё одного инженера.
Простые правила обслуживания помогают, потому что убирают догадки. Задайте понятный лимит времени на первое ревью для обычных pull request. Задайте отдельный лимит на согласование релиза для низкорисковых изменений. Решите, кто отвечает за нестабильные повторные запуски тестов и как быстро он должен реагировать. Проверяйте одни и те же цифры каждую неделю, используя одни и те же определения.
Держите правила маленькими и реалистичными. Первое ревью в течение четырёх рабочих часов лучше, чем расплывчатое обещание посмотреть «скоро». Согласование релиза в тот же день для рутинных изменений лучше, чем ожидание менеджера, который может вообще не понадобиться.
Если вы измеряете время ожидания pull request, метрики повторных запусков тестов и задержки согласования релиза вместе, закономерности становятся видны быстро. Одной команде может понадобиться очередь ревью. Другой — меньше обязательных согласующих. Третьей — исправить один нестабильный тестовый набор, который блокирует каждый релиз.
Не запускайте изменение процесса на всю компанию после нескольких плохих дней. Проведите один эксперимент, наблюдайте за цифрами два спринта и оставьте только то, что действительно сокращает ожидание.
Если хотите посмотреть на это со стороны, Oleg Sotnikov на oleg.is работает как Fractional CTO и startup advisor для стартапов и малого бизнеса. Он помогает командам улучшать поток поставки, инфраструктуру и практичное внедрение AI без преждевременного найма или бесконечной смены инструментов.
Часто задаваемые вопросы
Почему поставка кажется медленной, если инженеры быстро пишут код?
Обычно потому, что код перестаёт двигаться после того, как автор открывает pull request. Очереди на ревью, повторные запуски нестабильных тестов и согласование релиза создают простои, из-за которых занятая команда выглядит медленной.
Что такое время ожидания pull request?
Это время ожидания от открытия pull request до первого человеческого ревью, а затем время ожидания после того, как автор отвечает на комментарии. Так видно, сколько работа действительно стоит без движения.
Что нам измерять в первую очередь?
Сначала посмотрите на время от открытия до первого ревью, затем на время от запроса изменений до следующего ревью, количество повторных запусков тестов, время от merge до согласования релиза и долю pull request, которые простаивают больше дня. Эти цифры лучше показывают поток работы, чем story points или строки кода.
Почему лучше использовать медиану, а не среднее?
Используйте медиану, потому что один простой на три дня может потеряться в неплохом среднем значении. Ещё важно смотреть на медленный конец диапазона, чтобы видеть задержки, которые команда чувствует каждую неделю.
Означают ли повторные запуски тестов, что разработчик написал плохой код?
Не сами по себе. Повторные запуски часто связаны с нестабильными тестами, медленными runner'ами, общими тестовыми данными или проблемами окружения. Если одна и та же ветка проходит со второй попытки, сначала проверьте пайплайн, а уже потом обвиняйте автора.
Почему релизы всё равно тормозят после merge?
Потому что merge завершает только этап программирования. Один человек, фиксированные окна релиза или лишние ручные проверки могут удерживать готовую работу на дни. Отдельно отслеживайте время от merge до согласования, иначе вы не увидите эту вторую очередь.
Поможет ли найм новых инженеров решить проблему медленной поставки?
Обычно нет. Больше инженеров создают больше pull request, но не ускоряют ревью, не улучшают CI и не делают согласование быстрее. Сначала исправьте очередь, а потом решайте, нужны ли ещё люди.
Как маленькой команде проверить это за один спринт?
Выберите одну команду и один недавний спринт. Разместите каждый pull request в одной строке: время открытия, первое ревью, обновление от автора, согласование, merge, запуски тестов и согласование релиза. Самый большой простой видно меньше чем за час.
Какое простое правило можно попробовать в следующем спринте?
Начните с самого большого блока ожидания, а не с самой громкой жалобы. Если ревью запаздывают, поставьте цель на первое ревью в течение четырёх рабочих часов. Если мешают повторные прогоны, просите короткую причину после второго полного rerun.
Когда есть смысл обратиться за помощью к fractional CTO?
Подключайте внешнюю помощь, когда команда спорит о том, где именно проблема — в программировании, CI или релизе, либо когда founders хотят нанимать людей, не имея данных по времени. Fractional CTO вроде Oleg Sotnikov может разобрать поток, найти настоящее узкое место и оставить решение небольшим.