Технический долг в деньгах: как считать ROI рефакторинга легаси-системы

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

Обложка: Технический долг в деньгах: как считать ROI рефакторинга легаси-системы

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

В статье разберём этот эффект на примере легаси-систем, с которыми работает Centicore Group. Компания занимается модернизацией устаревшего кода, баз данных и инфраструктуры, а также системным и бизнес-анализом, разработкой ПО и контролем качества.

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

Что считаем техдолгом

Чтобы посчитать ROI рефакторинга, сначала нужно определить, что именно команда считает техническим долгом. Без этого в один список попадут баги, старые библиотеки, слабые тесты или желание сменить стек. У таких задач разные причины, цена и источники бюджета, поэтому их нельзя приоритизировать как одну категорию.

Термин «технический долг» ввёл программист Уорд Каннингем в 1992 году. Он использовал финансовую метафору: команда может выбрать более быстрое техническое решение, получить выгоду сейчас, а позже заплатить за это доработкой и дополнительными расходами.

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

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

  1. Баг — это ошибка в реализации. Техдолга здесь нет: никто не принимал осознанного решения сделать хуже. Поэтому баги относятся к дефект-менеджменту и финансируются из бюджета на поддержку, а не из бюджета на рефакторинг.
  2. Желание применить новую технологию — это новое требование к продукту. Команда хочет заменить PostgreSQL на что-то другое или перейти с REST на gRPC. Если это обосновано и полезно — отлично, но это отдельная задача со своей оценкой и своим источником финансирования. 
  3. Устаревший код без измеримых последствий — тоже не долг. Если модуль написан пять лет назад, но команда работает с ним без дополнительных затрат времени, он никого не тормозит и не создаёт рисков — переписывать его ради переписывания не нужно. Техдолг возникает в момент, когда команда признаёт: вот это решение замедляет нас, и мы можем точно сказать, как именно.

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

Как техдолг превращается в расходы

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

Объём работ — это всё, что нужно сделать, чтобы вернуться в целевое состояние. Раздробить монолитный модуль, заменить самописную библиотеку на поддерживаемую, переписать слой работы с БД. Объём работ оценивается в часах или стори-поинтах — это конечная сумма, которую команда выплатит один раз.

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

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

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

Какие задачи по техдолгу считать через ROI

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

Через ROI стоит считать задачи, которые конкурируют с фичами за время команды. У таких задач есть цена, влияние на скорость разработки и понятный конфликт с продуктовым планом. Например:

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

Отдельно стоят системные решения — их нельзя решать на уровне одного спринта. Для них нужна отдельная оценка, архитектурное решение и согласование с бизнесом. Например:

  • вывод старых сервисов из эксплуатации, 
  • изменение требований к отказоустойчивости, 
  • обновление платформы,
  • пересмотр архитектурных ограничений. 

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

Где искать расходы в легаси перед расчётом ROI

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

  • Сначала проверяют зоны частых изменений. Если команда регулярно дорабатывает один и тот же модуль, а перед каждой задачей тратит время на разбор старой логики, такой долг стоит учитывать в первую очередь, потому что он возвращается в каждой новой задаче.
  • Затем оценивают поддержку и эксплуатацию. Старые решения могут увеличивать время на инциденты, ручные операции, деплой, проверку интеграций и передачу знаний внутри команды. Эти затраты не всегда явно видны в разработке фич, но они каждый раз сокращают общий ресурс команды.
  • Отдельно смотрят ограничения для будущего развития. Если текущая архитектура мешает новым интеграциям, требованиям к безопасности, масштабированию или изменению бизнес-логики, долг влияет уже не только на поддержку, но и на продуктовый план.

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

Как считать ROI

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

Шаг 1. Посчитайте текущие потери. Зафиксируйте, сколько дополнительного времени команда тратит из-за конкретной проблемы прямо сейчас. Медленная сборка — сколько минут в день суммарно по команде. Сложный для понимания модуль — сколько часов уходит на оценку задач в нём. Отсутствие тестов — сколько времени занимает ручная проверка перед каждым релизом. Переведите это в деньги через стоимость часа работы команды.

Шаг 2. Оцените объём работ. Сколько займёт исправление — в часах, с буфером на непредвиденное. Это тоже деньги: стоимость часа умножить на объём.

Шаг 3. Посчитайте горизонт окупаемости. Если текущие потери составляют 20 000 рублей в неделю, а объём работ стоит 80 000 рублей, рефакторинг окупается за четыре недели. Дальше каждая неделя — чистая экономия. Если горизонт окупаемости укладывается в срок, на который планируется развитие продукта, рефакторинг финансово обоснован.

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

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

Как обосновать рефакторинг бизнесу

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

Аргумент «там плохой код» не работает, потому что для бизнеса это не проблема — проблема это замедление или остановка поставки фич. Поэтому аргументы должны быть на том же языке.

  1. Скорость поставки. Покажите данные из трекера: сколько времени уходило на задачи в конкретном модуле полгода назад и сколько уходит сейчас. Если время выросло в полтора раза при сопоставимой сложности задач, это измеримое замедление с понятной причиной.
  2. Предсказуемость оценок. Команда с накопленным техдолгом не может точно эстимировать: задача на три дня превращается в две недели, потому что в процессе обнаруживаются связанные проблемы. Для бизнеса это означает срыв сроков и невозможность планировать релизы. Покажите расхождение между оценками и фактом за последние несколько спринтов.
  3. Риски. Неподдерживаемые зависимости, отсутствие покрытия тестами на критических участках, архитектура, которая не выдержит роста нагрузки при следующем масштабировании. Риски переводятся в деньги через вероятность и стоимость инцидента.

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

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

Итого

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

По факту, это обычное инженерно-финансовое решение: либо команда закрывает объём долга сейчас, либо продолжает оплачивать его через более дорогие фичи, поддержку, регресс и риски в проде.