К списку вопросов · К архитектуре
На собеседовании важно назвать не только примеры плохого кода или временных решений, но и критерий обнаружения технического долга.
Технический долг — это не наличие костылей само по себе. Он возникает тогда, когда стоимость изменения системы начинает расти из-за прошлых технических решений.
Главный симптом:
Любое новое изменение становится дороже, медленнее или опаснее, чем должно быть.
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")) {
// Всё больше специальных сценариев.
}
После этого появляются сотни строк условий для отдельных клиентов и случаев. Система перестаёт развиваться через общую предметную модель и начинает развиваться через набор исключений.
Само временное решение ещё не означает наличие долга. Долг появляется, когда:
- решение не имеет срока удаления или владельца;
- временный код становится зависимостью для новых функций;
- стоимость устранения решения постоянно растёт;
- исключение начинает определять архитектуру системы.
Как отличить технический долг от разумного компромисса
Компромисс может быть оправдан, если команда сознательно выбирает более быстрое решение, понимает его последствия и контролирует дальнейшие действия.
Полезно проверить:
- зафиксирована ли причина решения;
- понятна ли стоимость его сохранения;
- определены ли владелец и срок пересмотра;
- есть ли наблюдаемые последствия;
- можно ли локально заменить решение без перестройки всей системы.
Если последствия неизвестны, решение бесконтрольно распространяется, а стоимость его устранения растёт, компромисс превращается в технический долг.
Короткий ответ для собеседования
Я определяю технический долг не по количеству костылей, а по последствиям. Если стоимость изменений растёт, скорость разработки падает, команда боится менять код, регрессии возникают в неожиданных местах, а архитектурные ограничения регулярно обходятся временными решениями, значит технический долг уже влияет на систему.
Костыль иногда может быть оправданным инженерным решением. Объективный симптом долга — устойчивое замедление и повышение риска развития системы из-за ранее принятых технических решений.