Перейти к основному содержимому

Неиспользуемый код и технический долг

Разработчику Архитектору Инженеру

Неиспользуемый код и технический долг

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


Мёртвый код

Мёртвый код — фрагменты программы, которые никогда не выполняются в процессе работы приложения.

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

Типы мёртвого кода:

ТипПримерПоследствия
Недостижимый кодКод после return или в ветке if (false)Загромождает кодовую базу
Неиспользуемые переменныеОбъявленные, но не читаемые переменныеВводит в заблуждение при чтении кода
Неиспользуемые методыМетоды без вызововУвеличивают размер сборки
Неиспользуемые классыКлассы без ссылокУсложняют навигацию по коду
Неиспользуемые импортыПодключённые, но не используемые модулиЗамедляют сборку и загрузку

Play ITЗагрузка интерактивного демо…

Статический анализ ищет фрагменты, до которых нельзя дойти по графу потока управления:

АЛГОРИТМ ОбработатьЗаказ(заказ)
ПроверитьЗаказ(заказ)
вернуть // досрочный выход
СохранитьЗаказ(заказ) // недостижимо — мёртвый код
ОтправитьПодтверждение(заказ)
КОНЕЦ
ПризнакПочему это мёртвый код
Строки после вернутьИсполнение сюда никогда не попадёт
Метод без вызовов в проектеМожет остаться после рефакторинга

Справочно на C#

Код ITЗагрузка примера кода…


Как его находить — статический анализ, coverage

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

Максимальный эффект достигается при комбинации инструментов:

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

Инструменты статического анализа:

ЯзыкИнструменты
C#Roslyn Analyzers, ReSharper, SonarQube
JavaSonarQube, PMD, SpotBugs
Pythonpylint, flake8, vulture
JavaScriptESLint, SonarJS
Gogo vet, staticcheck

Пример конфигурации ESLint для обнаружения неиспользуемого кода:

Код ITЗагрузка примера кода…

Покрытие кода (code coverage) — метрика, показывающая долю кода, выполненного при тестировании.

Инструменты измерения покрытия:

ПлатформаИнструмент
.NETcoverlet, dotCover
JavaJaCoCo, Cobertura
Pythoncoverage.py
JavaScriptIstanbul/nyc

Пример анализа покрытия в Python:

# Запуск тестов с измерением покрытия
coverage run -m pytest
coverage report --show-missing
coverage html # Генерация HTML-отчёта

Пример отчёта покрытия:

Name Stmts Miss Cover Missing
----------------------------------------------
order_service 50 5 90% 23-25, 42, 47
payment_gateway 30 15 50% 12-18, 25-32

Строки 23-25, 42, 47 в order_service и большая часть payment_gateway не покрыты тестами — возможный мёртвый код или недостаточное тестирование.


Последствия — усложнение поддержки, рост времени сборки

Последствия мёртвого кода:

  1. Усложнение понимания кода Разработчики тратят время на анализ кода, который не влияет на поведение системы.

  2. Рост размера сборки Каждый неиспользуемый метод и класс увеличивает размер итогового артефакта.

  3. Замедление сборки Компилятор обрабатывает весь код, включая мёртвые участки.

  4. Ложные срабатывания при рефакторинге Инструменты рефакторинга могут предлагать изменения в мёртвом коде.

  5. Риск случайной активации Мёртвый код может быть случайно задействован при изменении логики.

Пример влияния на размер сборки:

Код ITЗагрузка примера кода…


Удаление и комментирование

Комментирование мёртвого кода — практика закомментировать, а не удалить неиспользуемый код.

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

Пример плохой практики:

Код ITЗагрузка примера кода…

Проблемы закомментированного кода:

  1. Загрязнение кодовой базы Комментарии с кодом занимают место и отвлекают внимание.

  2. Неопределённость срока хранения "Временно" превращается в "навсегда". Через год никто не помнит, зачем код был закомментирован.

  3. Отсутствие версионного контроля Закомментированный код дублирует функциональность системы контроля версий (Git).

  4. Сложность поддержки При изменении сигнатуры методов закомментированный код становится некорректным, но остаётся в файле.

Правильный подход — удаление с сохранением в истории версий:

Код ITЗагрузка примера кода…

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

Закомментированный код допустим в редких случаях:

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

Во всех случаях требуется подробный комментарий с датой и причиной.


Технический долг

Технический долг (technical debt, "долг кодинга") — метафора накопленных в коде и архитектуре проблем, возникших из‑за компромиссов по качеству ради сроков. Пользователь продукта часто их не видит; страдают сопровождаемость, тестируемость, понятность, модифицируемость и переносимость.

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

Метафора Уорда Каннингема (1992)

Первый "временный" обходной путь — как взятие кредита — ускоряет поставку сейчас, но требует погашения (переписывание, рефакторинг, тесты). Если долг не гасить, растут "проценты" — срывы сроков, непредсказуемые оценки, простои команды из‑за хрупкой базы. Каннингем подчёркивал, что долг накапливается поминутно в "не совсем правильном" коде.

Связанный закон М. Лемана (1980):

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

Типичные причины

ПричинаПример
Давление сроков"Выпустить к пятнице", отложить тесты и документацию
Слабое сцепление с процессомБизнес не видит долг в бэклоге
Высокое зацепление модулейПравка в одном месте тянет десяток файлов
Нет автотестов"Костыли" в проде без сетки регрессии
Нет документации и обмена знаниямиОдин человек "знает, как оно работает"
Отложенный рефакторингЧем больше кода написано поверх слабой структуры, тем дороже погашение
Недостаток опытаАнтипаттерны закрепляются в ядре

Последствия и управление

Долг увеличивает неопределённость оценок: с каждым изменением в бэклог попадает "незавершённая работа" прошлых спринтов. Команды с предсказуемыми релизами ограничивают WIP и закладывают в план погашение долга — отдельные задачи, % ёмкости спринта, Definition of Done с тестами и ревью.

Погашение — это завершение отложенной работы — тесты, удаление TODO/FIXME, миграции схемы, рефакторинг, фиксация архитектурных решений (ADR). Мёртвый код из предыдущих разделов — частый вид долга с нулевой пользой и ненулевой ценой сопровождения.


Связанные материалы энциклопедии


Что запомнить

  • Мёртвый код увеличивает стоимость сопровождения без пользы для поведения системы.
  • История версий уже хранит удалённые решения, поэтому комментировать "на память" не нужно.
  • Статический анализ и покрытие дополняют друг друга.
  • Регулярная чистка кодовой базы ускоряет ревью и снижает риск ошибок.

Типичные ошибки

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

Мини-практика

  1. Возьмите один модуль и запустите анализатор неиспользуемого кода.
  2. Разделите находки на "удалить сразу" и "проверить с командой".
  3. Удалите безопасные элементы отдельным коммитом.
  4. Добавьте правило линтера или CI-проверку, чтобы проблема не повторялась.