Масштабирование процессов: SAFe, LeSS, Spotify-модель и их критика
Одна команда из семи человек умеет всё: договориться за пять минут, выкатить релиз до обеда, переспросить у соседа вместо тикета. Пятнадцать таких команд, работающих над одним продуктом, внезапно тратят половину времени на согласования, а фича, которую раньше делали за неделю, теперь «в интеграции» третий месяц.
Естественная реакция руководства — купить фреймворк масштабирования. Появляются новые роли, общие планирования на два дня, каскад досок и портфельный слой. Иногда становится лучше. Часто — только заметнее, куда уходит время.
Эта статья — про то, почему масштабирование вообще ломается (это физика, а не недостаток дисциплины), что конкретно предлагают SAFe, LeSS, Nexus, Scrum@Scale и Spotify-модель, где каждый из них честно работает, а где превращается в театр, и какие рычаги дают эффект независимо от выбранного фреймворка.
Это последняя статья трека, и она собирает воедино почти всё предыдущее: очереди и WIP из Kanban, вероятностное планирование из оценки, структуру команд из коммуникаций и измерения из DORA.
Часть 1. Физика: почему N команд никогда не дают N× результата
1.1. Два разных налога на размер
Интуиция «два человека сделают вдвое больше» ломается по двум независимым причинам.
Первый налог — сериализация (contention). Есть вещи, которые нельзя делать параллельно: одно архитектурное решение, одна общая база, один релизный поезд, одно решение продакт-директора. Пока команда ждёт своей очереди к общему ресурсу, она не работает. Это классический закон Амдала: если доля последовательной работы равна α, ускорение никогда не превысит 1/α. Двадцать команд при α = 0,1 дадут максимум десятикратный выход, сколько бы ещё команд вы ни наняли.
Второй налог — когерентность (coherency). Команды должны согласовывать состояние между собой: контракты API, схемы данных, сроки, семантику полей. Число потенциальных пар связей — N(N−1)/2. Это ровно тот механизм, который Фред Брукс описал в «The Mythical Man-Month»: добавление людей в опаздывающий проект задерживает его ещё сильнее, потому что затраты на коммуникацию растут квадратично, а полезная работа — линейно.
Обе поправки объединяет универсальный закон масштабируемости Нила Гантера (Universal Scalability Law), изначально выведенный для многопроцессорных систем, но применимый к любой системе с общими ресурсами и взаимными согласованиями:
$$C(N) = \frac{N}{1 + \alpha(N-1) + \beta N(N-1)}$$
где C(N) — выход системы из N единиц, α — доля сериализации, β — цена когерентности. Ключевое следствие: при β > 0 функция имеет максимум, а дальше идёт вниз.
$$N^\ast = \sqrt{\frac{1-\alpha}{\beta}}$$
То есть существует число команд, после которого следующая команда уменьшает поставку. Не замедляет рост — именно уменьшает. Это не метафора, это арифметика.
import math
def usl(n: int, alpha: float, beta: float) -> float:
"""Выход системы из n команд в единицах «одна изолированная команда»."""
return n / (1 + alpha * (n - 1) + beta * n * (n - 1))
def peak(alpha: float, beta: float) -> float:
"""Число команд, после которого добавление следующей снижает поставку."""
return math.inf if beta <= 0 else math.sqrt((1 - alpha) / beta)
for name, a, b in [("команды по компонентам/слоям", 0.05, 0.010),
("команды по потокам ценности", 0.03, 0.001)]:
print(f"{name}: пик N* = {peak(a, b):.1f}")
for n in (1, 4, 8, 12, 20):
c = usl(n, a, b)
print(f" N={n:>3} выход={c:5.2f}x КПД={c / n:5.1%}")
команды по компонентам/слоям: пик N* = 9.7
N= 1 выход= 1.00x КПД=100.0%
N= 4 выход= 3.15x КПД=78.7%
N= 8 выход= 4.19x КПД=52.4%
N= 12 выход= 4.18x КПД=34.8%
N= 20 выход= 3.48x КПД=17.4%
команды по потокам ценности: пик N* = 31.1
N= 1 выход= 1.00x КПД=100.0%
N= 4 выход= 3.63x КПД=90.7%
N= 8 выход= 6.32x КПД=79.0%
N= 12 выход= 8.21x КПД=68.4%
N= 20 выход=10.26x КПД=51.3%
Сложность вычисления — O(1) на точку; никакой симуляции не нужно, вся содержательная работа в честной оценке α и β. Их можно получить эмпирически: снимите throughput при разном числе команд за последние два года и подгоните кривую методом наименьших квадратов (у Гантера описана процедура через квадратичную регрессию по эффективности).
1.2. Главный вывод, который определяет всё остальное
Посмотрите на два набора параметров выше. Разница между «пик на 10 командах» и «пик на 31» даётся не выбором фреймворка. β — это плотность межкомандных зависимостей, то есть следствие архитектуры системы и границ команд. Никакая церемония не снижает β; она лишь делает жизнь на кривой более предсказуемой.
Фреймворк масштабирования управляет симптомами связности. Архитектура и границы команд управляют самой связностью.
Это единственная мысль, ради которой стоит читать дальше. Всё остальное — детали.
1.3. Синхронизация: почему кросс-командная фича медленная даже без потери ёмкости
Есть отдельный, часто незамечаемый эффект. Предположим, ёмкость не теряется вообще: каждая команда работает на полную. Но фича требует вклада k команд и готова, когда закончила последняя. Время ожидания в очереди каждой команды — случайная величина. Значит, срок фичи — это максимум из k случайных величин, а максимум растёт с ростом k.
Если ожидания примерно экспоненциальны (разумное приближение для очереди M/M/1 при высокой загрузке — см. статью про Kanban) со средним W, то
$$E[\max(X_1,\dots,X_k)] = W \cdot H_k = W\left(1 + \tfrac12 + \tfrac13 + \dots + \tfrac1k\right)$$
где H_k — гармоническое число. Рост логарифмический — казалось бы, мягкий. Но множитель большой: при загрузке 80 % и среднем времени работы 3 дня W = 12 дней.
import random, statistics
def team_wait(rng, util: float, service: float) -> float:
"""Ожидание в очереди одной команды — экспоненциальное приближение M/M/1."""
return rng.expovariate(1.0 / (util / (1 - util) * service))
def feature_time(rng, k: int, util: float, service: float) -> float:
"""Фича готова, когда закончила последняя из k команд."""
return max(team_wait(rng, util, service) for _ in range(k)) + service
def simulate(k: int, util=0.8, service=3.0, trials=50_000, seed=7):
rng = random.Random(seed)
xs = sorted(feature_time(rng, k, util, service) for _ in range(trials))
return statistics.mean(xs), xs[int(0.85 * trials)]
W = 0.8 / (1 - 0.8) * 3.0 # среднее ожидание в очереди = 12 дней
print(f"{'k':>3} {'E[T] сим':>10} {'E[T] теор':>10} {'p85':>8}")
H = 0.0
for k in range(1, 9):
H += 1 / k
m, p85 = simulate(k)
print(f"{k:>3} {m:>10.1f} {W * H + 3.0:>10.1f} {p85:>8.1f}")
k E[T] сим E[T] теор p85
1 15.0 15.0 25.8
2 21.0 21.0 33.6
3 25.0 25.0 38.3
4 28.0 28.0 41.7
5 30.4 30.4 44.4
6 32.4 32.4 46.5
7 34.1 34.1 48.3
8 35.6 35.6 49.9
Симуляция сходится к аналитической формуле — хороший знак, что модель написана правильно. Сложность симуляции — O(trials · k) по времени, O(trials) по памяти (сортировка ради процентиля; при желании считать только среднее — O(1) памяти).
Читаем результат. Фича, которую делает одна команда, — 15 дней. Та же по объёму работы фича, размазанная на четыре команды, — 28 дней. Ёмкость не потеряна ни на грамм: просто теперь нужно, чтобы четыре очереди сошлись. Именно поэтому «сделаем быстрее, подключив ещё три команды» почти всегда даёт обратный эффект.
Две важные оговорки:
- Если хвосты ожиданий тяжелее экспоненциальных (а в реальных командах они тяжелее — отпуска,
инциденты, приоритетные вбросы), максимум растёт не как ln k, а как степень k. Замените в
коде
expovariateна распределение Парето — и увидите куда более резкую картину. - Если очереди коррелированы (общий квартальный дедлайн, общий релизный поезд), эффект максимума частично исчезает — это, кстати, честный аргумент в пользу синхронизированной каденции вроде PI в SAFe.
Часть 2. Карта фреймворков
разработки)) Наращивание процесса SAFe Essential / Large Solution / Portfolio / Full ART, PI Planning, RTE, WSJF Disciplined Agile набор «способов работы» от PMI Scrum@Scale цикл Scrum-мастера + цикл владельца продукта EAT и EMS Минимальное расширение Scrum Nexus 3-9 команд, Nexus Integration Team единый интегрированный инкремент LeSS один PO, один бэклог, один спринт LeSS Huge: Requirement Areas Организационный дизайн Team Topologies 4 типа команд, 3 режима взаимодействия когнитивная нагрузка как ограничение Spotify-модель squad, tribe, chapter, guild снимок, а не рецепт unFIX библиотека паттернов, без предписаний Отказ от масштабирования Descaling меньше зависимостей вместо больше координации Модульная архитектура автономный деплой, контрактное тестирование
Полезно держать в голове, что фреймворки отвечают на разные вопросы:
| Фреймворк | На какой вопрос отвечает | Что требует изменить |
|---|---|---|
| SAFe | как синхронизировать много команд и связать их со стратегией | процессы, роли, планирование; структуру — опционально |
| LeSS | как остаться Scrum’ом, когда команд стало восемь | структуру организации, роли менеджеров, архитектуру |
| Nexus | как интегрировать инкременты 3–9 команд | почти ничего, только добавить интеграционную команду |
| Scrum@Scale | как построить масштабируемую систему из мелких блоков | контур принятия решений на уровне руководства |
| Spotify-модель | как выглядит автономная организация | структуру и культуру (и то не факт) |
| Team Topologies | какие границы команд правильные | архитектуру и границы команд |
Верхняя половина квадранта — там, где эффект реален, но политически дорог. Правый нижний угол — самая частая реальность: купили регламент, структуру не тронули, получили ритуалы.
Часть 3. SAFe: как он устроен на самом деле
3.1. Анатомия
Scaled Agile Framework Дина Леффингвелла (первая версия — 2011, актуальная линейка — SAFe 6.x) — самый распространённый и самый критикуемый фреймворк. Он существует в четырёх конфигурациях: Essential, Large Solution, Portfolio, Full.
Ядро — Agile Release Train (ART): виртуальная организация из 5–12 команд, 50–125 человек, работающих в одной каденции над одним решением. Все команды ART имеют один ритм итераций, одну общую Definition of Done и один общий System Demo.
Ключевые элементы:
- Planning Interval (PI) — 8–12 недель, обычно пять двухнедельных итераций, последняя из которых — Innovation & Planning (буфер, техдолг, обучение, подготовка следующего PI). До SAFe 6.0 назывался Program Increment.
- PI Planning — два дня, весь поезд в одном помещении (или в одном Zoom). Команды строят планы итераций, вывешивают межкомандные зависимости на ART Planning Board, называют риски, голосуют «уверенностью» (fist of five) за реалистичность плана.
- Роли: Release Train Engineer (RTE — по сути главный Scrum-мастер поезда), Product Management (владельцы фичей на уровне ART), System Architect, Business Owners.
- WSJF — приоритизация по «взвешенной кратчайшей работе».
- Lean Portfolio Management — эпики, Lean Business Case, портфельный канбан, финансирование потоков ценности вместо проектов.
3.2. WSJF — единственная часть SAFe с математическим обоснованием
WSJF (Weighted Shortest Job First) считается так:
$$\text{WSJF} = \frac{\text{Cost of Delay}}{\text{Job Duration}}, \quad \text{CoD} = \text{Business Value} + \text{Time Criticality} + \text{Risk Reduction / Opportunity Enablement}$$
Это не эвристика «из практики», а правило Смита (WSPT, Smith 1956) — доказуемо оптимальная последовательность для задачи 1‖Σw_jC_j: минимизации суммы взвешенных времён завершения на одном ресурсе. Идея у Рейнертсена в «Principles of Product Development Flow» — та же.
from dataclasses import dataclass
@dataclass
class Feature:
name: str
business_value: int # шкала модифицированного Фибоначчи: 1,2,3,5,8,13,20
time_criticality: int
risk_reduction: int
job_size: int
@property
def cost_of_delay(self) -> int:
return self.business_value + self.time_criticality + self.risk_reduction
@property
def wsjf(self) -> float:
return self.cost_of_delay / self.job_size
backlog = [
Feature("Оплата через СБП", business_value=13, time_criticality=13, risk_reduction=3, job_size=8),
Feature("Редизайн личного кабинета", business_value=8, time_criticality=2, risk_reduction=1, job_size=13),
Feature("Миграция на новую КМС", business_value=3, time_criticality=1, risk_reduction=13, job_size=20),
Feature("Экспорт отчётов в XLSX", business_value=5, time_criticality=3, risk_reduction=1, job_size=2),
]
def total_delay_cost(order) -> int:
"""Суммарная цена задержки: каждая фича «капает» CoD до момента завершения."""
t, cost = 0, 0
for f in order:
t += f.job_size
cost += f.cost_of_delay * t
return cost
by_wsjf = sorted(backlog, key=lambda f: -f.wsjf)
by_value = sorted(backlog, key=lambda f: -f.business_value)
for f in by_wsjf:
print(f"{f.wsjf:5.2f} CoD={f.cost_of_delay:>3} size={f.job_size:>3} {f.name}")
print("цена задержки, порядок WSJF :", total_delay_cost(by_wsjf))
print("цена задержки, порядок «по ценности» :", total_delay_cost(by_value))
4.50 CoD= 9 size= 2 Экспорт отчётов в XLSX
3.62 CoD= 29 size= 8 Оплата через СБП
0.85 CoD= 17 size= 20 Миграция на новую КМС
0.85 CoD= 11 size= 13 Редизайн личного кабинета
цена задержки, порядок WSJF : 1291
цена задержки, порядок «по ценности» : 1401
Сложность — O(n log n) на сортировку. Выигрыш здесь скромный (8 %), потому что бэклог маленький; на портфеле из сотни эпиков разница между «сначала самое ценное» и «сначала самое ценное на единицу времени» достигает десятков процентов.
Где WSJF ломается на практике:
- Оптимальность правила Смита доказана для одного ресурса без зависимостей. Как только фичи связаны предшествованием или делят несколько команд, оптимальность теряется — задача становится NP-трудной.
- Три слагаемых CoD складываются в одинаковых «попугаях», хотя измеряют разное. Компоненты начинают завышать («ну это же критично»), и через два квартала у всего CoD = 30.
- Job Duration подменяют Job Size (объёмом работ). Это разные вещи: работа на 3 дня, которая ждёт согласования две недели, имеет duration 17 дней.
- Настоящая цена задержки измеряется в деньгах в единицу времени и часто вычислима: упущенная выручка, штрафы, стоимость поддержки старой системы. Там, где это возможно, считайте деньги, а не баллы.
3.3. Честная критика SAFe
Что SAFe реально даёт. В крупной организации с регуляторикой, аппаратной частью, десятками зависимостей и менеджментом, который никогда не слышал про поток, SAFe даёт три вещи: общий словарь, обязательную двухдневную встречу, где зависимости становятся видимыми, и легитимный способ говорить с финансами о финансировании потоков вместо проектов. Это немало.
Что вызывает возражения.
-
Большой батч планирования. Весь трек мы говорили, что размер партии — главный враг потока. PI Planning — это партия планирования на 10 недель. SAFe отвечает, что план — не обязательство и корректируется каждую итерацию. На практике «PI-коммитменты» быстро превращаются в контракт, по которому отчитываются, и команда, узнавшая на третьей неделе что-то важное, не меняет план, а «доносит его до конца PI».
-
Сложность как продукт. Полная схема SAFe — это сотня взаимосвязанных элементов. Кен Швабер, соавтор Scrum, написал в 2013 «unSAFe at any speed», назвав его возвратом к RUP под новой обложкой. Рон Джеффрис в «SAFe – Good But Not Good Enough» аргументирует мягче: SAFe улучшает ситуацию в среднем плохой организации, но выстраивает потолок, выше которого она уже не поднимется.
-
Экономика сертификации. Мартин Фаулер в докладе «The State of Agile Software in 2018» называет это «Agile Industrial Complex»: индустрия зарабатывает на продаже процесса, а не на результате, и главным потребителем становится менеджмент, а не команды. Это не опровергает пользу фреймворка, но объясняет, почему он так распространён.
-
Слабая независимая доказательная база. Кейсы на сайте Scaled Agile («время вывода на рынок минус 30–75 %») — самоотчёты внедривших компаний, без контрольных групп. Ни одного рандомизированного или квазиэкспериментального исследования эффективности SAFe не опубликовано. Сравните с DORA, где за выводами стоят многолетние опросы с психометрической валидацией конструктов.
-
Структура остаётся прежней. Самое важное: SAFe можно внедрить, не меняя границ команд. Компонентные команды остаются компонентными, β из формулы USL не падает, а на доску зависимостей вешают двести красных ниточек. Двести ниточек — это диагноз, а не план. Их наличие означает, что архитектура и оргструктура не совпадают с потоками ценности.
Часть 4. LeSS: масштабирование через «расмасштабирование»
Large-Scale Scrum Крэйга Лармана и Баса Водде исходит из противоположной посылки: если Scrum перестал работать на восьми командах, добавлять сущности — ошибка, надо убирать причины сложности.
Правила LeSS (2–8 команд) намеренно минималистичны:
- один Product Owner на весь продукт;
- один Product Backlog;
- один Sprint, общий для всех команд, с общим Sprint Review;
- одна Definition of Done и один потенциально поставляемый инкремент;
- feature teams вместо компонентных: команда делает клиентоценную функциональность целиком, через все слои;
- никаких новых ролей: нет RTE, нет «менеджера программы», нет промежуточных владельцев продукта.
LeSS Huge (8+ команд, до нескольких тысяч человек) добавляет ровно одну конструкцию — Requirement Areas: продукт делится на области по клиентской перспективе (не по компонентам!), у каждой есть Area Product Owner и своя часть бэклога, но продукт, спринт и DoD остаются общими.
События масштабируются экономно: Sprint Planning One (представители всех команд договариваются, кто что берёт) → Sprint Planning Two (по командам) → общий Sprint Review → Overall Retrospective плюс командные ретро. Координация — не через отдельный слой менеджеров, а через «just talk», общие компоненты-сообщества, странствующих людей и открытые Scrum-of-Scrums по необходимости.
4.1. Законы Лармана — почему LeSS почти не внедряют
Ларман сформулировал «Larman’s Laws of Organizational Behavior», и первый из них объясняет судьбу большинства трансформаций:
Организации неявно оптимизированы так, чтобы избегать изменения статус-кво в позициях и властных структурах менеджеров среднего и первого звена и «специалистов».
Из чего следует второй закон: любая инициатива по изменению будет переопределена и выхолощена так, чтобы сохранить существующий порядок. И третий: любая такая инициатива будет объявлена «прагматичным подходом с учётом нашей специфики».
LeSS честно требует: убрать слой менеджеров-координаторов, распустить компонентные команды, поставить одного PO над продуктом, которым сейчас управляют семь человек. Это прямая атака на статус-кво. Поэтому LeSS применяют реже, а там, где применяют, эффект глубже.
4.2. Ограничения LeSS
- Один PO на восемь команд — узкое место по определению. Ларман настаивает, что PO не должен писать требования, только приоритизировать, но на практике роль перегружена.
- Требует зрелых инженерных практик. Общий спринт и общий инкремент невозможны без непрерывной интеграции, транкового разработки, автотестов и фича-флагов — всего, что описано в статье про качество и релизы. Без них общий Sprint Review превращается в неделю ручной интеграции.
- Провал компетенций реален. Когда компонентные команды становятся feature-командами, производительность падает на квартал-два, пока люди осваивают чужие слои. Это надо запланировать и защитить перед бизнесом (см. управление рисками).
- Мало инструментов и консультантов. Экосистема на порядок меньше SAFe — это реальный фактор для корпоративной закупки.
Часть 5. Nexus и Scrum@Scale — коротко и по делу
Nexus (Кен Швабер, Scrum.org, 2015) — самое скромное расширение: 3–9 Scrum-команд, работающих над одним продуктом. Добавляется ровно одна сущность — Nexus Integration Team (обычно PO, Scrum-мастер и несколько инженеров из команд), которая отвечает за то, чтобы интегрированный инкремент существовал в конце каждого спринта. Добавляются события: Nexus Sprint Planning, Nexus Daily (про интеграцию, не про статусы), Nexus Sprint Review, Nexus Sprint Retrospective, и, критично, Refinement с явной задачей выявить зависимости до планирования.
Nexus хорош, когда проблема действительно в интеграции, а не в стратегии. Он ничего не говорит про портфель, финансирование и продуктовую иерархию — если ваша боль там, Nexus не поможет.
Scrum@Scale (Джефф Сазерленд) построен на идее «scale-free» архитектуры: минимальный жизнеспособный набор компонентов, которые рекурсивно повторяются. Два переплетённых цикла: Scrum Master Cycle (как — устранение препятствий, каденция, непрерывное улучшение) и Product Owner Cycle (что — стратегическое видение, бэклог, приоритизация, обратная связь). Их связывают Scrum of Scrums (и SoSoS) со стороны «как», MetaScrum — со стороны «что». Наверху — Executive Action Team (снимает организационные препятствия) и Executive MetaScrum (владеет стратегическими приоритетами).
Сильная сторона Scrum@Scale — единственный из мейнстримных фреймворков, который явно делает руководство частью системы с обязательствами и каденцией. Слабая — крайне абстрактен: даёт контур, но почти не даёт практик, поэтому качество внедрения полностью определяется зрелостью внедряющих.
Часть 6. Spotify-модель: самый популярный фреймворк, которого не существует
6.1. Что было в оригинале
В октябре 2012 года Хенрик Книберг и Андерс Иварссон опубликовали 13-страничный whitepaper «Scaling Agile @ Spotify with Tribes, Squads, Chapters & Guilds», позже — два видео об инженерной культуре. Оттуда весь мир унёс схему:
- Squad — автономная команда ~8 человек с собственной миссией, «как мини-стартап»; сама выбирает, как работать (Scrum, Kanban, что угодно), и владеет своим куском продукта;
- Tribe — объединение сквадов в одной области, до ~100 человек (явная отсылка к числу Данбара);
- Chapter — люди одной специальности внутри трайба; глава чаптера — линейный руководитель;
- Guild — сквозное сообщество по интересу через всю компанию, добровольное;
- позднее — Trio и Alliance для координации между трайбами.
Ключевые лозунги: «alignment enables autonomy», «loosely coupled, tightly aligned squads», «fail fast, recover faster», «культура важнее процесса».
6.2. Что с этим не так
Первое. На первой же странице whitepaper написано, что это снимок текущего состояния, а не рецепт: «мы всё ещё в пути», «это не работающая модель, а моментальный кадр». Книберг многократно повторял, что «Spotify-модели» не существует и что Spotify сам так не работает. Мир проигнорировал дисклеймер и растиражировал схему.
Второе. В 2020 году Джеремайя Ли, работавший в Spotify, опубликовал «Spotify Doesn’t Use “the Spotify Model”» — пожалуй, самый ценный документ по теме. Кратко его наблюдения:
- Матрица squad/chapter создавала классический конфликт двух начальников: chapter lead отвечал за карьеру человека, product owner — за его работу.
- Автономия была декларирована, но не подкреплена: сквады могли выбирать «как», но не имели полномочий менять то, от чего зависели.
- Коллаборация была известной внутренней проблемой — по внутренним опросам сотрудничество между сквадами оценивалось низко на протяжении лет.
- Agile-коучи, которых копировщики считали двигателем модели, в реальности были распределены тонким слоем и не имели мандата.
- Модель не была спроектирована — она выросла, и то, что работало для 30 команд музыкального стриминга в 2012-м, не было универсальным.
Третье, главное. Spotify-модель — единственный «фреймворк» в этом списке, который стал популярен как организационная схема. А организационная схема — это результат, а не метод. Скопировав названия коробочек, вы не копируете ни архитектуру, ни инженерную культуру, ни десятилетие эволюции, которая к этой схеме привела. Это карго-культ в чистом виде: аэродром построен, самолёты не прилетают.
Ценное из Spotify стоит забирать не схемой, а принципами: автономия команды имеет смысл только когда она техническая (можно задеплоить, не спрашивая никого), выравнивание достигается общим контекстом и метриками, а сообщество практиков (guild) — дешёвый и действительно рабочий механизм распространения знаний.
Часть 7. Настоящий рычаг: границы команд и зависимости
7.1. Закон Конвея и обратный манёвр
Мелвин Конвей в 1968 году («How Do Committees Invent?») сформулировал: организации проектируют системы, которые копируют структуру коммуникаций этих организаций. Гипотеза «зеркалирования» была эмпирически подтверждена — см. MacCormack, Baldwin, Rusnak, «Exploring the Duality Between Product and Organizational Architectures» (2012), где сравнение продуктов, созданных распределёнными open-source сообществами и колокированными коммерческими командами, показало систематически более модульную архитектуру у первых.
Обратный манёвр Конвея (inverse Conway maneuver): если структура организации всё равно проявится в архитектуре — спроектируйте организацию под ту архитектуру, которую хотите получить.
Именно это делает β управляемой величиной.
7.2. Считаем зависимости как задачу о разрезе графа
Формализуем. Компоненты системы — вершины графа, вес ребра — интенсивность взаимодействия (совместные изменения в коммитах, вызовы API, кросс-командные тикеты). Нарезка на команды — разбиение вершин. Цель — минимизировать вес разреза (сумму весов межкомандных рёбер) при ограничении на размер команды (это и есть когнитивная нагрузка из Team Topologies).
Это задача о сбалансированном разбиении графа, NP-трудная в общем случае. Но нам не нужен оптимум — нам нужно «сильно лучше, чем сейчас», а для этого достаточно локального поиска в духе Кернигана–Лина.
from collections import defaultdict
from itertools import combinations
def build_adj(edges: dict) -> dict:
"""Ненаправленный граф связности компонентов: {(u, v): вес} -> {u: {v: вес}}."""
adj = defaultdict(dict)
for (u, v), w in edges.items():
adj[u][v] = adj[u].get(v, 0) + w
adj[v][u] = adj[v].get(u, 0) + w
return adj
def cross_team_weight(edges: dict, team_of: dict) -> int:
"""Целевая функция: суммарный вес зависимостей, пересекающих границы команд."""
return sum(w for (u, v), w in edges.items() if team_of[u] != team_of[v])
def try_single_moves(nodes, adj, team_of, size_of, max_load):
"""Точечные перемещения: переносим компонент к соседям, если это уменьшает разрез.
Ограничение max_load — когнитивная ёмкость команды. Сложность прохода: O(V*T + E)."""
team_of = dict(team_of)
teams = sorted(set(team_of.values()))
changed = True
while changed:
changed = False
load = defaultdict(int)
for n in nodes:
load[team_of[n]] += size_of[n]
for n in nodes:
cur = team_of[n]
pull = defaultdict(int) # вес связей узла с каждой командой
for m, w in adj[n].items():
pull[team_of[m]] += w
best, best_gain = cur, 0
for t in teams:
if t == cur or load[t] + size_of[n] > max_load:
continue # команда переполнена — ход запрещён
gain = pull[t] - pull[cur]
if gain > best_gain:
best, best_gain = t, gain
if best != cur:
load[cur] -= size_of[n]
load[best] += size_of[n]
team_of[n] = best
changed = True
return team_of
def try_swaps(nodes, adj, team_of):
"""Обмены парами (шаг Кернигана-Лина): меняем местами два компонента из разных команд.
Размер команд сохраняется автоматически. Сложность поиска лучшей пары: O(V^2 * d)."""
team_of = dict(team_of)
while True:
best_pair, best_gain = None, 0
for a, b in combinations(nodes, 2):
ta, tb = team_of[a], team_of[b]
if ta == tb:
continue
w = lambda x, t: sum(v for m, v in adj[x].items() if team_of[m] == t)
# выигрыш = насколько сильнее каждый узел тянется к чужой команде,
# минус двойной вес прямой связи между ними (иначе учтём её дважды)
gain = (w(a, tb) - w(a, ta)) + (w(b, ta) - w(b, tb)) - 2 * adj[a].get(b, 0)
if gain > best_gain:
best_pair, best_gain = (a, b), gain
if not best_pair:
return team_of
a, b = best_pair
team_of[a], team_of[b] = team_of[b], team_of[a]
nodes = list("ABCDEFGHI")
size_of = {n: 1 for n in nodes}
edges = {("A", "B"): 8, ("A", "C"): 6, ("B", "C"): 9, # кластер «онбординг»
("D", "E"): 7, ("E", "F"): 8, ("D", "F"): 5, # кластер «платежи»
("G", "H"): 9, ("H", "I"): 6, ("G", "I"): 7, # кластер «отчёты»
("C", "D"): 2, ("F", "G"): 1} # слабые связи между кластерами
adj = build_adj(edges)
by_layer = {"A": 0, "D": 0, "G": 0, # frontend
"B": 1, "E": 1, "H": 1, # backend
"C": 2, "F": 2, "I": 2} # данные
print("нарезка по слоям :", cross_team_weight(edges, by_layer))
moved = try_single_moves(nodes, adj, by_layer, size_of, max_load=3)
print("после точечных перемещений :", cross_team_weight(edges, moved))
swapped = try_swaps(nodes, adj, moved)
print("после обменов (реорг) :", cross_team_weight(edges, swapped))
groups = defaultdict(list)
for n, t in swapped.items():
groups[t].append(n)
print("итоговые границы:", [sorted(v) for _, v in sorted(groups.items())])
нарезка по слоям : 68
после точечных перемещений : 68
после обменов (реорг) : 3
итоговые границы: [['G', 'H', 'I'], ['A', 'B', 'C'], ['D', 'E', 'F']]
Это самый важный результат статьи, и он не про графы.
Точечные перемещения не улучшили ничего. Все команды заполнены, ни одного разрешённого одиночного хода не существует — алгоритм застрял в локальном оптимуме со значением 68. Одновременный обмен двух компонентов сразу уронил разрез до 3, в 22 раза.
Организационный смысл прямой: постепенная оптимизация внутри существующих границ не выводит из локального оптимума. Пока вы переставляете по одному человеку, «оптимизируете передачи» и «улучшаете коммуникацию между фронтом и бэком», вы остаётесь в точке 68. Выход требует одновременного изменения нескольких границ — то есть настоящей реорганизации.
Именно поэтому LeSS требует структурных изменений, а SAFe, который можно надеть поверх старой структуры, часто фиксирует организацию в её локальном оптимуме: делает точку 68 комфортной и наблюдаемой, а значит — стабильной.
Оценки сложности: try_single_moves — O(P·(E + V·T)), где P — число проходов;
try_swaps — O(S·V²·d̄), где S — число выполненных обменов, d̄ — средняя степень вершины.
Для реальных размеров (V — сотни компонентов) это секунды. Память — O(V + E).
7.3. Где взять веса рёбер
Не выдумывайте их на воркшопе. Данные уже есть:
-- Совместная изменяемость: как часто два компонента правятся в одном PR.
-- Высокий co-change — сильная логическая связь, даже если в коде её "нет".
WITH pr_components AS (
SELECT DISTINCT pr_id, component
FROM changed_files
JOIN file_to_component USING (path)
WHERE merged_at >= now() - interval '180 days'
)
SELECT a.component AS comp_a,
b.component AS comp_b,
count(*) AS co_changes
FROM pr_components a
JOIN pr_components b ON a.pr_id = b.pr_id AND a.component < b.component
GROUP BY 1, 2
HAVING count(*) >= 5
ORDER BY co_changes DESC;
-- Межкомандные блокировки: сколько задач ждали другую команду и сколько это стоило дней.
SELECT blocker.team AS blocking_team,
blocked.team AS waiting_team,
count(*) AS n_blocks,
round(avg(blocked.blocked_days)::numeric, 1) AS avg_wait_days,
sum(blocked.blocked_days) AS total_days_lost
FROM issue_links l
JOIN issues blocked ON blocked.id = l.blocked_id
JOIN issues blocker ON blocker.id = l.blocker_id
WHERE l.link_type = 'blocks'
AND blocked.resolved_at >= now() - interval '90 days'
AND blocker.team <> blocked.team
GROUP BY 1, 2
ORDER BY total_days_lost DESC;
Вторая выборка — самая полезная таблица во всей теме масштабирования. Она даёт ответ на вопрос «где именно у нас болит», и обычно 80 % потерянных дней приходятся на 3–5 пар команд. Эти пары и есть кандидаты на слияние, на явный контракт с SLA или на вынесение общего куска в платформу.
7.4. Team Topologies как язык целевого состояния
Team Topologies Мэттью Скелтона и Мануэля Пайса описывает не процесс, а целевую конструкцию. Четыре типа команд:
- stream-aligned — основной тип, выровнена на поток ценности, владеет им от идеи до эксплуатации; таких команд должно быть большинство;
- platform — предоставляет внутренний продукт (не «услугу по заявке»), которым stream-команды пользуются самообслуживанием;
- enabling — временно помогает stream-командам освоить новую способность и уходит;
- complicated-subsystem — там, где нужна редкая глубокая экспертиза (кодеки, риск-движки, вычислительная геометрия).
Три режима взаимодействия: collaboration (высокий обмен, дорого, только временно, чтобы нащупать границу), X-as-a-Service (дешёвый режим по умолчанию для стабильных границ), facilitating (помощь в обучении). Постоянная коллаборация двух команд — сигнал, что граница между ними проведена неверно.
Ограничение, вокруг которого всё построено, — когнитивная нагрузка: команда может владеть
только тем объёмом систем, который помещается в голову. Это ровно max_load из кода выше.
и co-change компонентов] --> B B -- да --> C{80% потерь — межкомандные ожидания?} C -- нет --> D{Потери внутри команд?} D -- да --> D1[Проблема не в масштабе:
WIP, DoD, тесты, ревью] --> D2[См. статьи про Kanban,
качество и DORA] D -- нет --> D3[Потери в приоритизации и стратегии:
WSJF, портфельный канбан, EMS] C -- да --> E{Можно перенарезать команды
по потокам ценности?} E -- да --> F[Обратный манёвр Конвея:
feature teams, платформа, X-as-a-Service] F --> G{Команд после перенарезки ≤ 8?} G -- да --> H[LeSS или просто Scrum/Kanban
с общим DoD и общим бэклогом] G -- нет --> I[LeSS Huge: Requirement Areas
или несколько автономных потоков] E -- нет --> J{Почему нельзя?} J -- монолит и общий релиз --> K[Сначала архитектура:
модули, контрактные тесты, фича-флаги] K --> E J -- регуляторика, железо,
внешние поставщики --> L[Нужна синхронизирующая каденция] L --> M[SAFe Essential или Nexus:
общий ритм, видимые зависимости] M --> N[Держите как временную меру:
считайте зависимости каждый PI и сокращайте] J -- политика --> O[Смотри законы Лармана:
честно назовите ограничение
и не обещайте эффекта от церемоний] style F fill:#46a758,fill-opacity:0.18 style K fill:#e0902f,fill-opacity:0.18 style O fill:#d8504a,fill-opacity:0.18
Часть 8. Что измерять при масштабировании
Метрики масштабирования — это метрики из статьи о DORA, но снятые на уровне системы, а не команды. Ключевой набор:
| Метрика | Как считать | О чём говорит |
|---|---|---|
| Доля фичей, сделанных одной командой | фичи без межкомандных задач / все фичи | прямой прокси β; цель — рост к 80 %+ |
| Cross-team blocked days | сумма дней ожидания чужой команды | абсолютная цена связности в днях |
| Cycle time от идеи до прода, p85 | на уровне фичи, не задачи | реальный срок для бизнеса |
| Deployment frequency по командам | распределение, не среднее | видно, кто не может релизиться самостоятельно |
| Доля независимых деплоев | деплои без координации с другими / все | техническая автономия, предиктор DORA |
| Стоимость координации | человеко-часы на синхроны, планирования, SoS | явный бюджет процесса |
| Планируемое vs поставленное за PI | % запланированных фичей, доехавших до прода | честность каденции планирования |
Про первую строку. В Accelerate и отчётах DORA устойчиво воспроизводится результат: сильнейший предиктор непрерывной поставки — слабо связанная архитектура и команды, операционализированные через вопросы вида «может ли команда изменить дизайн своей системы, не согласовывая с другими?» и «может ли она задеплоить независимо от других сервисов?». Не «использует ли команда фреймворк X».
Осторожно с последней строкой: «процент выполнения PI-плана» очень легко превращается в цель, и тогда команды начинают закладывать буферы и брать только безопасное. Это классический закон Гудхарта, подробно разобранный в статье про метрики. Смотрите на него как на диагностику калибровки, а не как на KPI.
Часть 9. Типичные ошибки
-
Масштабировать раньше, чем нужно. Если у вас пять команд и болит — почти наверняка проблема не в масштабе, а в WIP, ревью, тестах или отсутствии DoD. Фреймворк спрячет это под слоем церемоний.
-
Внедрить процесс, не тронув структуру. Самая частая и самая дорогая ошибка. Формула USL не знает, что вы купили сертификацию: β остаётся прежней.
-
Копировать оргсхему вместо принципов. Squad/tribe/chapter/guild — результат чужой десятилетней эволюции. См. часть 6.
-
Считать план обязательством. PI-план полезен как способ увидеть зависимости и риски. Как контракт на 10 недель он гарантирует, что новые знания не повлияют на работу.
-
Компонентные команды под видом feature-команд. Если «команда платежей» не может довести фичу до прода без «команды профиля» и «команды нотификаций» — это компонентная команда, как бы она ни называлась.
-
Платформа как сервис-деск. Платформенная команда, работающая по заявкам, — просто очередь. Платформа должна быть самообслуживаемым продуктом, иначе она добавляет α в формулу.
-
Игнорировать провал компетенций при перенарезке. Спад на квартал реален. Не заложив его, вы гарантированно откатите реорганизацию через шесть недель, когда velocity просядет.
-
Один бэклог на 15 команд без иерархии областей. Владелец продукта становится узким местом; появляются теневые приоритеты «по договорённости».
-
Синхронизировать каденцию, не сокращая зависимости. Общий ритм гасит дисперсию сроков, но не устраняет причину. Если через год число красных ниточек на доске не упало — трансформация не состоялась.
-
Гибридный франкенштейн без явного выбора. «Возьмём PI Planning из SAFe, feature teams из LeSS, guild из Spotify» — рабочая стратегия, только если вы понимаете, какую проблему решает каждый заимствованный элемент. Иначе получаются несовместимые обязательства.
Часть 10. Как это выглядит в проде
Продуктовая компания, 60 инженеров, 8 команд, SaaS. Обычно фреймворк не нужен вообще. Работает связка: команды выровнены по потокам ценности, общий DoD и общий транк, платформенная команда даёт CI/CD и observability как самообслуживание, раз в квартал — совместная сессия планирования на полдня (не два дня), еженедельный демо всего продукта. Зависимости решаются через контрактные тесты и фича-флаги, а не через встречи.
Банк или телеком, 400+ инженеров, регуляторика, легаси-ядро. Здесь SAFe Essential обычно побеждает — не потому, что лучше, а потому, что даёт язык, понятный аудиту и финансам, и принудительную точку синхронизации. Разумная стратегия: внедрить каденцию как временную опору и параллельно вести программу сокращения зависимостей с измеримой целью («доля фичей одной команды с 25 % до 60 % за год»). Если такой цели нет, через два года у вас будет очень дисциплинированная организация с прежней скоростью.
Стартап, выросший с 3 до 12 команд за год. Самый опасный случай: масштаб пришёл раньше архитектуры. Здесь бессмысленно выбирать фреймворк — нужно резать монолит по границам, которые совпадут с командами, вводить владение сервисами и on-call, и держать число команд на потоке минимальным. Обратный манёвр Конвея — единственное, что здесь работает.
Аутсорс/подрядная модель. Ни один фреймворк не масштабируется через контрактную границу, где у сторон разные экономические интересы. Прежде чем выбирать SAFe, разберитесь, как устроена приёмка и кто владеет DoD.
Мини-итог
- Проблема масштабирования — не в процессе, а в связности: α (общие ресурсы) и β (взаимные согласования) в законе универсальной масштабируемости. При β > 0 существует число команд, после которого поставка падает.
- Кросс-командная фича медленная даже при полной ёмкости: её срок — максимум из k очередей, растущий как W·H_k.
- SAFe даёт синхронизацию, общий словарь и связь со стратегией; риск — большой батч планирования и фиксация организации в локальном оптимуме без изменения структуры.
- LeSS атакует причину, требуя структурных изменений; поэтому даёт более глубокий эффект и почти не внедряется — см. законы Лармана.
- Nexus решает узкую задачу интеграции; Scrum@Scale — единственный, кто явно втягивает руководство в систему.
- Spotify-модели не существует: это снимок 2012 года, чей автор сам просил его не копировать, а внутренние наблюдения показывают, что коллаборация была известной проблемой.
- Настоящий рычаг — границы команд. Задача формализуется как минимизация разреза графа зависимостей при ограничении на когнитивную нагрузку.
- Точечные улучшения не выводят из локального оптимума: разрез 68 остался 68 после всех одиночных ходов и упал до 3 после одновременных обменов. Организационный перевод: нужна настоящая реорганизация, а не оптимизация передач.
- Измеряйте долю фичей, сделанных одной командой, и cross-team blocked days. Если за год они не улучшились, вы купили процесс, а не скорость.
Источники
- Gunther N., Universal Scalability Law — модель α/β и вывод точки максимума
- Amdahl G., «Validity of the single processor approach…», 1967
- Brooks F., «The Mythical Man-Month»
- Conway M., «How Do Committees Invent?», 1968
- MacCormack, Baldwin, Rusnak, «Exploring the Duality Between Product and Organizational Architectures», 2012
- Kernighan & Lin, «An Efficient Heuristic Procedure for Partitioning Graphs», 1970
- SAFe — официальный фреймворк
- Schwaber K., «unSAFe at any speed», 2013
- Jeffries R., «SAFe – Good But Not Good Enough»
- Fowler M., «The State of Agile Software in 2018»
- LeSS — официальный сайт и законы Лармана
- Nexus Guide, Scrum.org
- Scrum@Scale Guide
- Kniberg & Ivarsson, «Scaling Agile @ Spotify», 2012 (PDF)
- Lee J., «Spotify Doesn’t Use “the Spotify Model”», 2020
- Team Topologies — ключевые концепции
- DORA Research и книга Accelerate
- unFIX — библиотека паттернов организационного дизайна
- Книги без свободных ссылок: Larman & Vodde, «Large-Scale Scrum: More with LeSS»; Reinertsen D., «The Principles of Product Development Flow» (экономическое обоснование WSJF и стоимости очередей); Skelton & Pais, «Team Topologies»; Smith W. E., «Various optimizers for single-stage production», 1956 (доказательство оптимальности правила WSPT, лежащего в основе WSJF).
Что дальше
На этом трек по управлению проектами закончен. Мы прошли путь от ценностей Agile и механики Scrum через поток и очереди, оценку и риски, команду и качество, метрики — до того, что происходит, когда команд становится много. Если из всего трека остаётся одна мысль, пусть это будет она: процесс не создаёт скорость, он только перестаёт ей мешать; скорость создаётся архитектурой, границами команд и коротким циклом обратной связи.
Куда идти дальше:
- Вернуться к карте трека и пройти пропущенное — Управление проектами в IT: карта трека.
- Продуктовая сторона той же работы: приоритизация, гипотезы, метрики продукта — трек Product Management.
- Техническая сторона снижения β: границы модулей, контексты и связность — Domain-Driven Design и архитектурные паттерны.
- Фундамент, без которого не работает ничего из перечисленного — алгоритмы, структуры данных и принципы проектирования.
- Практика на конкретном стеке: Go, TypeScript, C#, Elixir.
- Общая карта портала и порядок изучения — Roadmap.