Перейти к основному содержимому

Engineering capacity planning: математика Q3 roadmap

· 8 мин. чтения
Artur Pan
CTO & Co-Founder at PanDev

Команда из 6 инженеров, 60 рабочих дней, по 8 часов. PM приходит на планирование со слайдом «2 880 dev-часов capacity». Q3 roadmap влезает в 2 400. Комфортный буфер. Через три месяца 40% roadmap не успели, а в постмортеме пишут «scope creep».

Никакого scope creep не было. Цифра capacity была неверной с первого дня. Стэнфордский экономист Джон Пенкавель в исследовании по часам и продуктивности показал, что output-per-hour начинает падать после 49 часов в неделю, задолго до 60. Microsoft Research и UC Irvine с Глорией Марк добавили второе лезвие: каждое прерывание стоит в среднем 23 минуты 15 секунд на восстановление фокуса. Сложите эти два факта поверх любого 8-часового календаря и вы получите заметно меньше 8 продуктивных часов.

Формула, которую никто не записывает​

Большинство планирования относится к capacity как к арифметике: team × hours × days. На самом деле это умножение:

effective_hours = team_size
× FTE_utilization
× calendar_hours
× velocity_multiplier
× (1 - context_switching_tax)

Каждый множитель между 0 и 1 режет первоначальную цифру. Команды с 100% утилизацией, 100% velocity и нулевым context-tax не существует ни в одном датасете, который мы видели, включая собственный. Арифметическое допущение раздувает capacity на 35-55%, что совпадает с данными Atlassian State of Teams: средний sprint коммитится примерно на 1.4× от того, что доезжает до релиза.

Эта статья проходит математику и подставляет реальные числа из 6-человечной команды, чтобы показать, как 960 календарных часов схлопываются до 520 реальных.

Четыре множителя, источник за источником​

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

Множитель 1: FTE utilization​

Календарные часы предполагают, что разработчик у клавиатуры 160 часов в месяц. Реальная IDE-телеметрия говорит иначе. Мы разобрали это в статье FTE utilization vs hours logged: медиана по нашему B2B-датасету сидит на 65-75% sustainable utilization в окне 90 дней. После 90% вы уже покупаете burnout, а не capacity.

В PanDev Metrics это идёт из f_mv_activity_total_user_daily_today(), функции, которая возвращает live FTE для каждого человека против его CustomEmployeeWorkingTime baseline (так контрактник на 4-дневной неделе считается против 128h, а не 160). Для departement-планирования усредняем mv_activity_total_user_daily по последним 90 дням — это и есть вход в формулу.

Множитель 2: Velocity (onboarding-налог)​

Senior-инженер, который выкатил две фичи в прошлом квартале, имеет velocity 1.0. Новый сотрудник на второй неделе — нет. DORA в State of DevOps 2024 измерила 6-9-месячную ramp-кривую, прежде чем новые инженеры догонят team-mean throughput по changes-per-week. Мы трактуем это как скользящий множитель:

Состояние инженераVelocity multiplier
Новичок, неделя 1-40.30
Onboarding, месяц 2-30.60
Ramping, месяц 4-60.85
Steady-state senior1.00
10+ лет на этой кодовой базе1.05

Это взвешенное среднее. 5 сеньоров и 1 новичок в команде из 6 дают (5 × 1.0 + 1 × 0.6) / 6 = 0.93. Звучит безобидно, пока не комбинируется со следующими двумя множителями.

Множитель 3: Context-switching tax​

Лабораторные данные Mark — это пол; production хуже. Microsoft в Worklab study показал, что в 2022 у knowledge-workers сложился «triple peak» рабочий день, а блоки sustained focus сжимаются квартал за кварталом. Мы измеряем context switches через project-tag transitions в IDE heartbeats. Медианный project-switching tax в датасете лежит в диапазоне 15-22%, добираясь до 30%+ на командах с 4+ параллельными фичами.

Используйте эти диапазоны:

Параллельных фич в работеContext tax
15%
212%
318%
425%
5+32%

Тот же сюжет под углом focus time разобран в статье Deep work schedules for developers: обратный взгляд на ту же цифру.

Множитель 4: Calendar hours​

Самый честный. 160 часов в месяц — это FTE-конвенция; ваш реальный календарь отличается в месяцах с государственными праздниками, отпусками по уходу за ребёнком, плановым PTO и on-call ротациями. Кастомные календари в PanDev Metrics живут в CustomEmployeeWorkingTime, индексированы по userId × month, чтобы планировщик мог достать 6 индивидуальных baseline и просуммировать их вместо плоского headcount × 160 на слайде. В типичном Q3 на командах, которые мы измеряем, одна только календарная вариация (один декрет, две недели накопленного PTO, один кластер региональных праздников) срезает 8-12% headline-цифры до того, как сработают остальные множители.

Декомпозиция​

Та же 6-человечная команда. Один квартал. Цифры из нашего датасета, округлённые:

ШагЧто вычитаетсяЧасов остаётся
Календарная headline6 × 160h × 1 месяц960h
− FTE utilization× 0.71 (90-day avg)682h
− Velocity weighting× 0.93 (один новичок)634h
− Context-switching tax× 0.82 (4 параллельные фичи)520h
Реальная monthly capacity520h (54% от календаря)

520 часов. Headline говорил 960. Этот разрыв в 440 часов и есть то, что ломает roadmap-коммиты. Не scope creep. Не «команда не справилась». Математика, которая была неверной с первого слайда.

Bar chart: 960 календарных часов схлопываются до 520 после FTE utilization, velocity и context-switching deductions Та же команда, тот же месяц. Календарь показывает 960h. Реальная capacity после 4 множителей: 520h.

Три квартала одной команды: prediction vs actual​

Где формула отрабатывает себя — это сравнение прогноза с реальной поставкой. Та же команда из 6 человек по Q1, Q2, Q3 2026:

КварталКалендарьПрогноз формулыДоехалоVariance
Q1 (steady)2 880h1 580h1 610h+1.9%
Q2 (новичок 6 неделя, 5 фич в работе)2 880h1 290h1 240h−3.9%
Q3 (полная команда, фокус на 2 фичи)2 880h1 790h1 750h−2.2%

Variance меньше 5% в каждом квартале. PM, который пользовался календарной цифрой (team × 160h × 3 = 2 880h), коммитился бы на 1.6-1.8× от того, что доезжало каждый квартал. PM с four-multiplier формулой коммитился практически ровно на то, что отгружалось.

Это та же входная телеметрия, которая питает employee utilization heatmap и delivery index без LOC: разные виды, один источник.

Где формула живёт в PanDev Metrics​

Три места, где входы уже на экране:

  1. /dashboard/finances/employee/:userId → виджет utilization показывает FTE-историю человека с тем же 90-day окном, что и формула.
  2. Department view → агрегирует mv_activity_total_user_daily по подчинённым. Это и есть то, что вы суммируете в team-level multiplier.
  3. CustomEmployeeWorkingTime → override-слой. Декрет 50%, региональные праздники в Алматы, sabbatical: всё считается против скорректированной baseline, а не плоских 160h. Без этого слоя FTE-multiplier тихо врёт на любом нестандартном расписании. Мы прочувствовали это на собственном on-prem rollout: инженер на 50% расписании читался как 95% utilization на heatmap, потому что знаменатель не был переопределён, и планировщик чуть не перераспределил работу из-за overload, которого не было.

Это не product pitch. Четыре числа, которые вам нужны (utilization, velocity weighting, context tax, custom calendar), считаются непрерывно из одного и того же IDE heartbeat-стрима, поэтому формула — не квартальная гадалка. Это SELECT запрос.

Что это меняет в планировании​

Три конкретных сдвига, которые выпадают из математики.

Перестаньте коммититься на медиану​

Цифра 520h — это медиана. В половине случаев команда отгрузит меньше. Atlassian показывает variance commitment-to-delivery порядка ±15% даже на стабильных командах, так что коммит на 520h означает поставку 442-598h в зависимости от месяца. Roadmap, закоммиченный по медиане, промахивается примерно в половине случаев. Планируйте против медианы минус 15-20% буфер на неизвестное: больничные, on-call пожары, задержки зависимостей, production-инцидент на 11-й день, который выедает двух сеньоров на неделю.

Капайте параллельные фичи на 3​

Context-switching tax прыгает с 18% на 25% при переходе с 3 на 4 параллельные фичи. Это примерно 70 часов capacity испаряются — больше полной недели одного инженера — в обмен на «мы работаем над большим количеством вещей». Математика говорит «не надо». Контр-интуитивный вывод: команда, которая отгружает 3 фичи параллельно, обгонит ту же команду на 5 фичах на полную неделю capacity в месяц, хотя вторая команда в standup'е выглядит «занятой».

Пересчитывайте формулу каждый месяц​

FTE utilization дрейфует. Velocity меняется, когда уходят сеньоры. Context tax скачет, когда outage добавляет внеплановый firefight-стрим. Four-multiplier формула — не плановая константа; это monthly recompute. Команды, которые пересчитывают в начале каждого квартала, стабильно попадают в 5% от плана; команды, которые ставят формулу один раз и замораживают, едят variance потом проваленными датами.

Честный лимит: capacity — это распределение, а не число​

Даже со всеми четырьмя множителями вы получаете медиану, не прогноз. Буфер не опционален. Цифры выше идут с выборки в 30+ B2B инженерных команд в нашем датасете: распределённые команды в финтех, SaaS и on-prem regulated. Solo-разработчики и крупные open-source проекты ведут себя иначе. У нас по ним нет сигнала, и множители выше скорее всего перестрелят или недостреляют в зависимости от ритма.

Самое сильное утверждение: если вы коммититесь на медианную capacity без буфера, вы будете промахиваться по roadmap примерно в 50% случаев. Добавьте 15-20% и вы перекроете большую часть variance большую часть времени. Любой, кто продаёт вам более узкий прогноз, либо везучий, либо лжёт.

Что сделать завтра​

Подтяните FTE utilization за последние 90 дней по своей команде. Умножьте на velocity-weighting таблицу. Вычтите context-switching tax bracket по числу параллельных фич. Сравните с календарной цифрой, против которой планируете.

Если разрыв больше 35%, ваш roadmap сейчас коммитится на capacity, которой нет. Чинится не «работаем больше». Чинится коммитом на цифру, которую данные действительно поддерживают, и защитой её на планировании four-multiplier таблицей.

Готовы увидеть метрики своей команды?

30-минутная персональная демонстрация. Покажем как PanDev Metrics решает задачи именно вашей команды.

Забронировать демо