Когда инженер только становится тимлидом, у него появляется понятная цель: заставить всё вокруг работать максимально эффективно. Технический бэкграунд даёт о себе знать: привычка оптимизировать никуда не девается.
Поэтому полная загрузка каждого выглядит правильным и экономически оправданным решением. На своём опыте я увидел, как такая локальная оптимизация ухудшила поставку, перегрузила QA и создала больше багов. И как после этого мы всё-таки ускорили поставку примерно на 30% без новых людей.
Как мы ухудшили поставку
Однажды моей команде потребовалось нарастить поставку. Первая мысль была простой: нужно оптимизировать время работы программистов. Мы изменили процессы, снизили затраты времени, и разработчики действительно начали закрывать больше задач.
Круто же? Не круто. Поставка команды стала хуже.
Мы сделали то, что делают многие команды: ускорили один участок системы, не посмотрев на поток целиком. Разработчики начали быстрее передавать задачи в тестирование. QA было меньше, система была сложной, и ребята не успевали переваривать поступающую работу.
После ускорения разработки нагрузка выросла ещё сильнее. Тестировщики стали спешить, уставать и пропускать баги. Мы одновременно ухудшили скорость поставки и качество системы.
Система работает со скоростью ограничения
В незавершённом продукте нет ценности. Код бесполезен, пока не окажется в продакшене и не начнёт приносить пользу клиентам. До этого его нужно отревьюить, протестировать и выкатить.
Если ограничение находится не в разработке, нет смысла ускорять только разработчиков. Они перегрузят следующий этап, а незавершённой работы станет больше.
Эту модель Элияху Голдратт подробно объясняет в книге «Цель». Там же я взял практическую эвристику: перед бутылочным горлышком всегда скапливается работа.
Первым правильным решением было перестать копать. Мы отказались от идеи запихнуть в работу максимум фич и начали отталкиваться от загрузки QA. Тестировщики смогли спокойно делать свою работу, а у разработчиков появилось время на техдолг и изучение соседних областей.
Изначальную проблему это не решило: скорость поставки осталась прежней. Зато мы перестали её ухудшать и увидели настоящее ограничение.
Как мы искали бутылочное горлышко
На доске всё выглядело просто до боли: в Ready for QA висело в среднем по десять задач, периодически число вырастало до пятнадцати. Но одного скопления работы недостаточно, чтобы объявить этап ограничением.
Мы проверили гипотезу несколькими способами:
- Посмотрели динамику Ready for QA за несколько недель и cycle time для этого статуса. Он оказался самым большим в жизненном цикле задачи, а входящий поток был выше исходящего.
- Провели ретроспективы о трудностях в поставке. Тестировщики жаловались на нагрузку, разработчики — на то, что задачи долго висят в тестировании и из-за этого приходится разруливать конфликты.
- Послушали дейлики. Там регулярно звучали просьбы протестировать какую-то задачу в приоритете, потому что она блокирует следующую.
Сразу несколько сигналов указывали на один этап. Поэтому мы достаточно уверенно признали тестирование ограничением.
Первая гипотеза не всегда будет верной. Например, в другой моей команде самый долгий cycle time был у статуса Ready for release. Но это не делало частоту релизов ограничением. Долгий статус нужно сопоставлять с потоком работы и обратной связью команды.
Как ускорить ограничение
Первым делом я запросил дополнительных тестировщиков. Начальство ответило в стиле «денег нет, но вы там держитесь». Пришлось строить систему вокруг тестирования доступными способами.
Мы смотрели на четыре рычага:
- Не перегружать ограничение работой, которую оно всё равно не успеет сделать.
- Ускорить прохождение задач через него.
- Убрать с ограничения то, что могут сделать другие.
- Увеличить ресурс ограничения, если это станет доступно.
Мы уже перестали перегружать QA фичами. Теперь нужно было ускорить тестирование за счёт более свободного ресурса разработки.
У нас уже была автоматизация, но самих тестов не хватало. Тестировщикам постоянно не хватало времени на их написание. Параллельно мы прикинули, сколько задач возвращается из тестирования на доработку, и поняли, что повышение качества входа тоже может нас ускорить.
В итоге мы изменили процесс:
- в Definition of Ready добавили тест-кейсы, которые QA помогали описывать до начала разработки;
- в Definition of Done добавили автотесты на описанные в требованиях сценарии;
- завели для разработчиков задачи на рост общего покрытия приложения автотестами.
Гипотеза была такой: описанные тест-кейсы упростят работу программиста, автотесты снизят число возвратов из QA, а растущее покрытие уменьшит число багов в целом. Ресурс тестировщиков сместится в начало процесса, но это окупится на самом тестировании.
Что получилось
Пару спринтов команда привыкала к новому процессу. Потом появилась обратная связь: программисты радовались, что задачи реже возвращаются на доработку, тестировщики — что в задачах меньше багов и растёт покрытие.
Поставка ускорилась примерно на 30%. С опытом команды и ростом количества автотестов показатель продолжал расти. Бонусом баги в продакшене со временем снизились практически до нуля.
Главный вывод из этой истории не в том, что всем нужно срочно писать больше автотестов. У другой команды ограничение будет в другом месте. Смысл в том, чтобы не оптимизировать самый заметный или знакомый участок, а найти реальное ограничение и подчинить ему весь поток.
Ограничение переместилось
После ускорения QA мы действительно стали поставлять больше фич. И тут перестал справляться другой элемент системы: подготовка требований со стороны бизнеса.
Продакт работал с несколькими командами и привык к определённому темпу. Рост нашей производительности создал ему внезапную проблему. Пока он адаптировался, мы с довольными лицами разгребали техдолг и докручивали автоматизацию.
Так я на практике увидел ещё одну важную вещь: бутылочное горлышко в системе есть всегда. Когда мы расшиваем одно, ограничение перемещается в другое место. Поэтому ускорение потока не заканчивается одной победой. После каждого улучшения нужно снова смотреть на систему целиком.