03 мар. 2025 г.·6 мин чтения

Бюджет на рефакторинг в спринтах без постоянных споров о дорожной карте

Простой способ выделить бюджет на рефакторинг в спринтах, защитить доставку фич и прекратить превращение задач по очистке кода в еженедельные споры.

Бюджет на рефакторинг в спринтах без постоянных споров о дорожной карте

Почему это превращается в спор на планировании спринта

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

Работа по очистке редко выглядит так аккуратно. Переименование запутанного модуля, удаление дублирующегося кода, ремонт нестабильных тестов или распутывание медленного запроса к базе кажется необязательной, когда доска уже заполнена. Формально продукт может двигаться дальше и без этого, поэтому такие задачи откладывают.

Цена проявляется через несколько недель. То, что должно было занять полдня, занимает два дня, потому что код труднее изменить. Баг проходит незамеченным, потому что тестам не доверяют. Простой релиз требует лишних проверок, потому что старая часть системы ломается каждый раз. Небольшие дефекты накапливаются, и команда продолжает платить за них.

Именно поэтому этот спор быстро надоедает. Люди на самом деле спорят не о конкретном рефакторе или баге. Они спорят о времени, риске и о том, кто может потратить часть спринта на работу, которая ещё не выглядит срочной. Продукт хочет видимого прогресса. Инженеры хотят, чтобы кодовая база не становилась медленнее и более хрупкой. Оба права.

Гораздо большая проблема — отсутствие постоянного правила. Без него каждая задача по очистке должна выигрывать свою отдельную дискуссию. Команды в итоге сравнивают улучшение логина с чисткой тестов или запрос клиента с обновлением зависимости, одну за другой. Побеждает самый громкий пункт.

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

Что значит бюджет на рефакторинг

Бюджет на рефакторинг — это фиксированная доля каждого спринта, которую команда защищает для работ по очистке. Вы один раз выбираете эту долю и используете её в каждом цикле. Это может быть 10%, 15% или 20% вместимости. Конкретное число важнее как привычка, чем как точная величина.

Смысл прост: работы по очистке перестают сражаться с фичами тикет за тикетом. Вместо того чтобы снова поднимать тот же спор на каждом планировании, команда заранее соглашается, что часть времени на доставку предназначена для снятия трения.

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

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

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

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

Держите правило коротким, чтобы все его помнили. «Мы резервируем 15% емкости спринта для очистки» — этого достаточно. Если правило требует длинного объяснения, его будут игнорировать, когда спринт станет нагруженным.

Как выбрать правильный процент

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

Если базовой линии нет, начните с 10–20% емкости спринта. Этого обычно достаточно, чтобы исправить повторяющееся трение, не заморозив дорожную карту. В двухнедельном спринте это может означать, что один инженер тратит примерно день, или вся команда немного своего времени на очистку.

Начните ближе к 10%, если поставка фич уже почти всегда срывается. Большая доля очистки может добавить стресса, когда команда уже не успевает по обязательствам. Держите объём узким и докажите, что работа уменьшает будущие издержки.

Переходите к 15%, если команда продолжает выпускать фичи, но мелкие проблемы постоянно их тормозят. Идите к 20%, когда баги, переработки и хрупкий код каждую неделю съедают время фич. Многие команды думают, что не могут позволить себе очистку, а затем теряют то же время на правки тех же проблем.

После выбора числа держите его неизменным три–четыре спринта. Это даст достаточно времени, чтобы увидеть, помогает ли бюджет, или команда выбрала число, которое просто звучало хорошо на совещании. Еженедельные изменения скрывают результат.

Наблюдайте за несколькими простыми сигналами в пробном периоде. Меньше ли багов прерывают запланированную работу? Становятся ли оценки более стабильными? Тратит ли команда меньше времени на повторный заход в старый код, чтобы сделать одну фичу безопасной?

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

Как проводить это на планировании спринта

Начните с реальной вместимости, а не желаемой. Посчитайте, кто доступен, вычтите отпуск, встречи, поддержку и всё остальное, что съест рабочие часы. Если команда планирует в пунктах, используйте ту же систему, но опирайтесь на то, что команда реально может закончить в обычном спринте, а не на лучший спринт в истории.

Зарезервируйте долю под очистку до того, как кто-либо начнёт спорить за фичи. Порядок важен. Если сначала обсуждать элементы дорожной карты, очистка превращается в остаточный бакет и всегда проигрывает.

Планирование становится гораздо проще, если у команды перед встречей готов короткий, ранжированный список долгов. Пяти–десяти пунктов обычно достаточно. Если список растёт, никто не понимает, что важно сейчас, и планирование тормозит.

Каждый пункт должен иметь чёткое начало, чёткий конец и простое объяснение, зачем это делать. «Разбить рискованный сервис на два меньших модуля» может сработать, если команда может закончить это в одном спринте. «Улучшить архитектуру» — слишком расплывчато и должно оставаться в стороне.

Это правило важнее, чем думают. Если никто не может объяснить проблему, влияние и критерий «готово», задача не подготовлена. Команды часто проскальзывают неясные рефакторы в спринт, потому что они звучат умно. Обычно это заканчивается наполовину сделанной работой и новым спором на ревью.

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

Что должно входить в слот очистки

Аудит самых загруженных путей кода
Найдите модули, запросы и тесты, которые превращают простые изменения в рискованную работу.

Слот очистки должен платить за работу, которая убирает повторяющуюся боль, а не за то, что просто приятно отдраить.

Лучшие кандидаты уже стоят команде времени, внимания или спокойствия. Саппорт постоянно видит один и тот же баг. Разработчик лезет в старую область и тратит часы на побочные эффекты. День релиза всё ещё зависит от ручного шага, который часто падает. Это бизнес-издержки, а не побочный проект.

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

Тесты подходят сюда, когда они защищают занятые, рискованные области продукта. Покрытие вокруг логина, биллинга, чекаута или общего API может предотвратить баг, который съест половину спринта. Покрытие вокруг старой фичи, к которой никто не притрагивался год — может подождать.

Небольшие структурные исправления тоже хорошо вписываются. Если один сервис разросся настолько, что маленькое изменение занимает полдня, очистка может быстро окупиться. Разбейте огромную функцию. Удалите мёртвый код. Изолируйте нестабильную зависимость. Переименуйте запутанные части, чтобы следующий человек не гадал.

Релизная работа — ещё одна частая цель. Может быть, деплой падает из‑за скрипта, который ломается каждые три попытки, и разработчику приходится сидеть онлайн и перезапускать шаги вручную. Починка этого процесса — работа по очистке, и часто она экономит больше времени, чем ещё одна мелкая фича.

Важно также понимать, что не должно попадать в этот слот. Не прячьте большой перепроект в рутину очистки. Замена фреймворка, перестройка ключевого сервиса или переработка модели данных требуют отдельного плана и прямого решения.

Если команда может завершить работу в спринте и объяснить выгоду одной простой фразой — вероятно, это подходит.

Простой пример из продуктовой команды

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

Они изменили одно правило планирования. Вместо того чтобы спорить за каждую задачу по очистке, они зарезервировали примерно время одного человека в каждом спринте на обслуживание и рефакторинг. На практике это означало примерно 10 дней из 60‑дневной ёмкости спринта. Никому не нужно было доказывать, что каждое исправление важнее фичи. Команда уже согласилась, что этот кусок существует.

В одном спринте они потратили это время на две небольшие задачи, которые всех раздражали неделями. Сначала они починили нестабильный тест чекаута, который часто падал и тормозил релизы. Баг был не в чекауте: тест зависел от таймингов, которые менялись под нагрузкой, поэтому люди многократно перезапускали пайплайн и гадали, реален ли фейл.

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

Ничего драматического на дорожной карте этот спринт не добавил. У пользователей не появилось новой вкладки или анонса. Тем не менее следующий спринт начался в лучшем состоянии. Команда тратила меньше времени на перепроверку упавших тестов, меньше изменений касалось хрупкого кода, и планирование стало спокойнее, потому что оценки перестали так сильно скакать.

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

Ошибки, которые ломают план

Успокойте споры между дорожной картой и инженерией
Установите правила, которые защищают доставку и в то же время дают команде время убрать повторяющуюся боль.

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

Фиксированный слот помогает только если он остаётся фиксированным достаточно долго, чтобы иметь значение. Если команда постоянно переводит его с 20% на 10% и на 0% при росте давления, никто не сможет планировать исходя из него. Долг растёт, и следующий спринт становится ещё плотнее.

Наиболее распространённые ошибки легко заметить. Команды заполняют слот расплывчатыми работами вроде «чистка кода» или «улучшить архитектуру». Люди используют его для завершения оставшихся задач по фичам. Кого‑то проскальзывает перепроект, который слишком велик для одного спринта. Продукт и инженерия не соглашаются, что значит «готово». Потом команда забывает показать, что именно сэкономила эта работа.

Большие перепроекты заслуживают отдельного подозрения. Спринтовый слот очистки создан для снижения трения, а не для перестройки половины продукта. Если задача не укладывается в спринт и не даёт полезного результата к его концу, разберите её на части или не берите в спринт.

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

Если слот стабилен, мал и измерим — он перестаёт выглядеть как инженерный каприз. Он становится нормальной частью работы спринта.

Проверки перед тем, как закрыть спринт

Ужесточите ваш релизный процесс
Сократите ручные проверки и ненадёжные шаги, которые тянут фичи через финишную черту.

Спринт портится, когда задачи по очистке расплывчаты или слишком велики. Решение скучное, но работает.

Перед тем как команда зафиксирует спринт, убедитесь, что каждая задача по очистке может завершиться внутри спринта. Если задача перетечёт в следующий спринт, сократите её или оставьте. Назовите проблему, а не желание. «Сократить время выполнения тестов с 28 до 20 минут» — ясно. «Улучшить тестирование» — нет.

Защитите слот очистки до того, как работа над фичами разрастётся. Если ждать до конца планирования, срочные запросы съедят его. Требуйте одного видимого результата на задачу, даже если результат небольшой: меньше обращений в саппорт, исправлен медленный запрос, сборка сократилась на 10 минут или убран один фрагильный джоб.

Также полезно на месте отказывать расплывчатой формулировке. Если кто‑то предлагает «чистку кода», задайте один уточняющий вопрос: какая проблема исчезнет к концу спринта? Этот вопрос обычно показывает, реальна ли задача или это просто хорошее намерение.

Пересмотрите правило через три–четыре спринта. Если слот слишком мал, слишком велик или заполнен не тем типом работ — измените его один раз и попробуйте снова.

Что делать дальше

Начните с маленького правила и дайте ему время подработать. Первый цикл обычно чувствуется неловко, потому что люди всё ещё спорят, что туда относится. Прогоните правило три спринта, прежде чем судить. Один спринт может быть случайностью. Три дадут паттерн.

Держите список долгов коротким. Ранжируйте элементы по боли сегодня, а не по возрасту или по тому, насколько уродливо выглядит код. Работы, которые вызывают повторяющиеся баги, тормозят релизы или создают переработки, должны быть вверху. Старые идеи по очистке без текущей стоимости могут подождать.

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

На ревью спринта обсуждайте слот очистки простым языком. Что стало легче? Что ещё болит? Если та же грязная область продолжает прерывать фичи, работайте над ней, пока боль не упадёт. Это лучшее планирование технического долга, чем выбор случайных рефакторов, потому что они кажутся просроченными.

Если споры на планировании всё ещё крадут время, внешний технический лидер может помочь установить правило и держать команду честной по масштабу. Oleg Sotnikov на oleg.is работает со стартапами и малыми компаниями как fractional CTO по архитектуре продукта, процессу доставки, инфраструктуре и AI‑первому развитию. Внешний взгляд помогает, когда все соглашаются с болью, но никто не может принять чистое решение на планировании.

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

Часто задаваемые вопросы

Why should we reserve sprint time for refactoring at all?

Потому что работы по очистке уже стоят команде времени, даже если вы их заранее не планируете. Фиксированный слот перестаёт превращать каждый рефактор, исправление тестов или релизный фикс в отдельный спор с фичами и даёт команде устойчивый способ убирать тормоза, пока они ещё небольшие.

How much sprint capacity should we set aside for cleanup?

Большинство команд хорошо работают с 10–20% емкости спринта. Выберите одно число, держите его на протяжении трёх–четырёх спринтов и оценивайте по реальным результатам, а не меняйте каждую неделю.

When should we choose 10% versus 20%?

Начните ближе к 10%, если доставка уже часто срывается и нужен небольшой безопасный тест. Переходите к 15–20%, когда повторяющиеся баги, хрупкий код и переработки постоянно отъедают время фич.

What kind of work belongs in the cleanup slice?

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

What should stay out of the refactor budget?

Большие перезапуски не должны прятаться в этом бюджете. Замена фреймворка, переработка модели данных или перепроектирование ядра — это отдельный проект, требующий собственного плана и решения.

How do we plan cleanup without losing feature delivery?

Зарезервируйте слот для очистки перед тем, как распределять фичи. Если ждать до конца планирования, запросы на фичи съедят этот ресурс, и очистка снова станет остаточным временем, которого никогда не бывает.

How do we know a refactor task is ready for a sprint?

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

How long should we keep the same budget before adjusting it?

Дайте правилу три–четыре спринта, прежде чем менять его. Это окно позволяет увидеть реальные паттерны: реже ли баги прерывают работу, стабильнее ли оценки, меньше ли возвратов в старый код.

What should we measure to see if the budget is working?

Следите за простыми сигналами, понятными всей команде: повторяющиеся баги в одной области, переработки после QA, время сборки или тестов, провалы релиза и перенос задач в следующий спринт. Они быстро покажут, помогает ли очистка.

What mistakes usually ruin a refactor budget?

Обычно план рушится, когда команда отказывается от слота под давлением дедлайнов, наполняет его расплывчатыми задачами или прячет в нём слишком большой рефакторинг. Держите слот стабильным, объём малым и показывайте хотя бы один ясный результат по каждой задаче.