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

К списку вопросов · К архитектуре

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

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

Главный симптом:

Любое новое изменение становится дороже, медленнее или опаснее, чем должно быть.

1. Изменения занимают непропорционально много времени

Нужно добавить одно поле в API. Ожидаемый срок — один день, но в действительности приходится:

  • менять 15 сервисов;
  • переписывать SQL-запросы;
  • обновлять 20 тестов;
  • править старый legacy-код.

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

2. Команда боится вносить изменения

Классический симптом — наличие модуля, работу которого никто полностью не понимает.

Не трогайте этот код, иначе всё упадёт.

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

3. Скорость разработки падает

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

Для диагностики полезно отслеживать:

  • lead time изменений;
  • время от начала разработки до production;
  • долю времени на исправление регрессий;
  • количество затрагиваемых компонентов;
  • время на тестирование и стабилизацию.

Устойчивое ухудшение этих показателей при сопоставимых задачах — объективный признак технического долга.

4. Изменения вызывают ошибки в неожиданных местах

Изменение экрана профиля ломает биллинг, а исправление биллинга нарушает отчётность.

Такие регрессии обычно указывают на:

  • чрезмерную связанность компонентов;
  • неявные зависимости;
  • отсутствие устойчивых контрактов;
  • недостаточную изоляцию модулей;
  • слабое автоматическое тестирование.

Чем сложнее заранее определить область воздействия изменения, тем дороже и опаснее становится развитие системы.

5. Архитектурные правила существуют только на бумаге

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

  • Service A читает базу данных Service B;
  • Service B обращается к Redis Service C;
  • Service C вызывает внутренний endpoint Service D.

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

6. Костыли начинают порождать новые костыли

Одно временное исключение может быть оправданным:

if (client.equals("A")) {
    // Временная обработка особого контракта.
}

Проблема начинается, когда исключения становятся основным способом развития системы:

if (client.equals("A")
        || client.equals("B")
        || client.equals("C")) {
    // Всё больше специальных сценариев.
}

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

Само временное решение ещё не означает наличие долга. Долг появляется, когда:

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

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

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

Полезно проверить:

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

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

Короткий ответ для собеседования

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

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

Прокрутить вверх