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

Откуда взялся термин

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

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

Как проявляется технический долг

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

На практике это выглядит так:

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

Виды технического долга

По происхождению

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

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

По области

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

Чем долг отличается от ошибок

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

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

Как оценить объем долга

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

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

Как управлять техническим долгом

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

  1. Фиксировать долг. Если команда сознательно идет на упрощение, его записывают в бэклог вместе с причиной и ожидаемыми последствиями. Неучтенный долг невозможно планировать.
  2. Оценивать влияние. В первую очередь погашают долг в частях системы, которые часто меняются или отвечают за критичные функции.
  3. Выделять время регулярно. Распространенная практика — отводить часть каждой итерации на рефакторинг и обновление зависимостей, а не копить все до «большой переделки».
  4. Улучшать код попутно. Работая над задачей, разработчик приводит в порядок затронутый участок, и долг сокращается без отдельных проектов.
  5. Автоматизировать контроль. Тесты, статический анализ и проверки в CI помогают ловить проблемы до того, как изменения попадут в основную ветку.

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

Когда брать долг допустимо

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

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