DEV Community

Denis Toropov
Denis Toropov

Posted on

Технический долг: причины появления и как с ним работать системно

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

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

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

Что такое технический долг в реальной разработке

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

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

Хороший маркер техдолга — фразы, которые в команде начинают звучать слишком часто:

  • «этот модуль лучше не трогать»;
  • «релизим аккуратно, там нет тестов»;
  • «да, библиотека старая, но обновлять страшно»;
  • «это известный баг, просто обходите его так»;
  • «если что, спросите у Сергея, только он знает, как это работает».

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

1. Устаревшие версии библиотек

Один из самых частых источников технического долга — старые зависимости и framework-версии, которые годами никто не трогает, потому что «и так работает».

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

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

Отдельный риск — накопление скрытых ограничений. Команда уже не может взять новый observability-агент, новый JDBC driver, новую версию Kafka client или security patch без цепочки зависимых обновлений. Формально причина одна — старая библиотека. Фактически это уже блокер для развития системы.

2. Плохая производительность

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

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

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

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

3. Отсутствие тестов

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

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

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

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

В итоге без тестов команда не просто «хуже контролирует качество». Она теряет возможность безопасно менять систему. А это уже прямое влияние на delivery.

4. Ошибки и известные дефекты

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

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

Особенно опасны дефекты, к которым «все привыкли». Их перестают воспринимать как приоритет, потому что они не выглядят как авария. Но именно такие ошибки съедают время годами: support обрабатывает однотипные кейсы, аналитики учитывают ограничения в новых процессах, разработчики помнят, что «в этом месте надо быть осторожнее».

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

5. Документация

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

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

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

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

Это не «бумажная работа». Это инструмент передачи контекста и снижения bus factor.

6. Руководство пользователя

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

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

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

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

Почему технический долг нужно оцифровывать

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

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

Чтобы долг начал управляться, его нужно переводить в конкретные сущности. Минимальный набор обычно такой:

Поле Что фиксировать
Область зависимости, производительность, тесты, баги, документация, user guide
Проблема что именно не так
Последствие на что это влияет: релизы, SLA, support, скорость команды, риски
Риск низкий / средний / высокий
Оценка часы, дни, story points
Метрика результата как понять, что стало лучше

Например, запись «надо обновить стек» бесполезна. Запись «Spring Boot 2.x больше не поддерживается, часть security patches недоступна, новые версии библиотек не поднимаются; оценка — 8 рабочих дней; результат — переход на поддерживаемый baseline и снятие ограничений по обновлению зависимостей» — уже пригодна для планирования.

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

Отдельный бэклог технического долга

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

Поэтому на практике лучше работает отдельный backlog технического долга. Не изолированный от общего planning-процесса, а встроенный в него.

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

Критерий Вопрос
Business impact влияет ли это на клиента, операционные процессы, SLA
Delivery impact мешает ли это выпускать изменения быстрее и безопаснее
Risk может ли это привести к инциденту, дефектам, уязвимостям, потере данных
Cost of delay насколько дороже будет исправлять через квартал
Effort сколько реально стоит исправление

Такой подход полезен ещё и тем, что убирает эмоциональность из обсуждения. Вместо «нам всем неприятно на это смотреть» появляется разговор в терминах риска, стоимости и эффекта.

Пример бэклога технического долга

Ниже — пример того, как это может выглядеть в рабочем виде.

Задача Причина Приоритет Оценка Ожидаемый эффект
Обновить критические библиотеки и framework накопились security и compatibility риски High 8 дней снижение числа уязвимостей, упрощение поддержки
Оптимизировать 5 самых медленных SQL-запросов p95 выше целевого SLA High 5 дней ускорение ключевых операций, снижение нагрузки на БД
Добавить интеграционные тесты на критический бизнес-поток регрессии всплывают в релизе High 6 дней выше предсказуемость релизов
Исправить известные дефекты с ручными обходами support и бизнес компенсируют проблему процессом Medium 4 дня снижение ручных операций и количества обращений
Описать ключевые архитектурные решения и интеграции высокая зависимость от нескольких инженеров Medium 3 дня быстрее онбординг и проще разбирать инциденты
Подготовить руководство пользователя по top-5 сценариям высокая нагрузка на поддержку Medium 3 дня меньше однотипных вопросов и ошибок в работе пользователей

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

Почему имеет смысл брать техдолг в каждый спринт по 20%

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

Поэтому резервировать часть спринта под техдолг — рабочая практика. Значение 20% обычно выглядит разумным стартом: не слишком мало, чтобы ничего не менять, и не слишком много, чтобы остановить поставку продукта.

У такого подхода есть несколько плюсов.

Во-первых, он делает работу предсказуемой. Не нужно каждый спринт заново доказывать, почему техдолг вообще достоин времени команды.

Во-вторых, он снижает риск накопления. Мелкие регулярные улучшения почти всегда дешевле, чем редкие крупные кампании по расчистке legacy.

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

Конечно, 20% — не универсальная магическая цифра. Для одной команды может быть достаточно 10–15%, для другой в период тяжёлой миграции понадобится 25–30%. Но сама идея постоянной квоты работает лучше, чем модель «исправим потом, когда будет окно».

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

Схема достаточно простая.

Сначала нужно собрать известные долги в единый список. Не пытаться сразу всё чинить, а просто зафиксировать. Потом — для каждой записи указать последствия, риск и оценку. После этого задачи ранжируются по приоритету, и в спринт попадает объём примерно на 20% ёмкости команды.

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

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

Top comments (0)