Неиспользуемый код и технический долг
Неиспользуемый код и технический долг
Эта тема касается качества кодовой базы в долгом цикле разработки. Мёртвый код редко ломает систему сразу, но постепенно ухудшает скорость изменений, усложняет ревью и увеличивает стоимость сопровождения.
Мёртвый код
Мёртвый код — фрагменты программы, которые никогда не выполняются в процессе работы приложения.
В рабочем проекте мёртвый код часто появляется после быстрых релизов, миграций и частичных рефакторингов. Поэтому его поиск стоит включать в регулярный цикл качества, а не оставлять "на потом".
Типы мёртвого кода:
| Тип | Пример | Последствия |
|---|---|---|
| Недостижимый код | Код после return или в ветке if (false) | Загромождает кодовую базу |
| Неиспользуемые переменные | Объявленные, но не читаемые переменные | Вводит в заблуждение при чтении кода |
| Неиспользуемые методы | Методы без вызовов | Увеличивают размер сборки |
| Неиспользуемые классы | Классы без ссылок | Усложняют навигацию по коду |
| Неиспользуемые импорты | Подключённые, но не используемые модули | Замедляют сборку и загрузку |
Play ITЗагрузка интерактивного демо…
Статический анализ ищет фрагменты, до которых нельзя дойти по графу потока управления:
АЛГОРИТМ ОбработатьЗаказ(заказ)
ПроверитьЗаказ(заказ)
вернуть // досрочный выход
СохранитьЗаказ(заказ) // недостижимо — мёртвый код
ОтправитьПодтверждение(заказ)
КОНЕЦ
| Признак | Почему это мёртвый код |
|---|---|
Строки после вернуть | Исполнение сюда никогда не попадёт |
| Метод без вызовов в проекте | Может остаться после рефакторинга |
Справочно на C#
Код ITЗагрузка примера кода…
Как его находить — статический анализ, coverage
Статический анализ — проверка кода без его выполнения для обнаружения потенциальных проблем.
Максимальный эффект достигается при комбинации инструментов:
- линтеры находят локальные неиспользуемые элементы;
- анализаторы архитектуры выявляют "висячие" модули;
- покрытие тестами показывает участки, которые никогда не проходят в сценариях выполнения.
Инструменты статического анализа:
| Язык | Инструменты |
|---|---|
| C# | Roslyn Analyzers, ReSharper, SonarQube |
| Java | SonarQube, PMD, SpotBugs |
| Python | pylint, flake8, vulture |
| JavaScript | ESLint, SonarJS |
| Go | go vet, staticcheck |
Пример конфигурации ESLint для обнаружения неиспользуемого кода:
Код ITЗагрузка примера кода…
Покрытие кода (code coverage) — метрика, показывающая долю кода, выполненного при тестировании.
Инструменты измерения покрытия:
| Платформа | Инструмент |
|---|---|
| .NET | coverlet, dotCover |
| Java | JaCoCo, Cobertura |
| Python | coverage.py |
| JavaScript | Istanbul/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 не покрыты тестами — возможный мёртвый код или недостаточное тестирование.
Последствия — усложнение поддержки, рост времени сборки
Последствия мёртвого кода:
-
Усложнение понимания кода Разработчики тратят время на анализ кода, который не влияет на поведение системы.
-
Рост размера сборки Каждый неиспользуемый метод и класс увеличивает размер итогового артефакта.
-
Замедление сборки Компилятор обрабатывает весь код, включая мёртвые участки.
-
Ложные срабатывания при рефакторинге Инструменты рефакторинга могут предлагать изменения в мёртвом коде.
-
Риск случайной активации Мёртвый код может быть случайно задействован при изменении логики.
Пример влияния на размер сборки:
Код ITЗагрузка примера кода…
Удаление и комментирование
Комментирование мёртвого кода — практика закомментировать, а не удалить неиспользуемый код.
С точки зрения инженерной дисциплины удаление предпочтительнее. История Git уже хранит прошлые версии, поэтому код всегда можно восстановить без засорения текущей рабочей ветки.
Пример плохой практики:
Код ITЗагрузка примера кода…
Проблемы закомментированного кода:
-
Загрязнение кодовой базы Комментарии с кодом занимают место и отвлекают внимание.
-
Неопределённость срока хранения "Временно" превращается в "навсегда". Через год никто не помнит, зачем код был закомментирован.
-
Отсутствие версионного контроля Закомментированный код дублирует функциональность системы контроля версий (Git).
-
Сложность поддержки При изменении сигнатуры методов закомментированный код становится некорректным, но остаётся в файле.
Правильный подход — удаление с сохранением в истории версий:
Код ITЗагрузка примера кода…
Когда допустимо оставлять закомментированный код
Закомментированный код допустим в редких случаях:
- временные отладочные фрагменты в процессе разработки (до коммита)
- альтернативные реализации с явным указанием причины выбора
- документирование намерений через примеры использования
Во всех случаях требуется подробный комментарий с датой и причиной.
Технический долг
Технический долг (technical debt, "долг кодинга") — метафора накопленных в коде и архитектуре проблем, возникших из‑за компромиссов по качеству ради сроков. Пользователь продукта часто их не видит; страдают сопровождаемость, тестируемость, понятность, модифицируемость и переносимость.
Плохой код сам по себе ещё не долг — "проценты" начисляются, когда приходится менять этот код — каждая правка дороже, чем могла бы быть. Термин иногда путают с легаси "чужого" качества; долг может накапливаться и в свежем репозитории.
Метафора Уорда Каннингема (1992)
Первый "временный" обходной путь — как взятие кредита — ускоряет поставку сейчас, но требует погашения (переписывание, рефакторинг, тесты). Если долг не гасить, растут "проценты" — срывы сроков, непредсказуемые оценки, простои команды из‑за хрупкой базы. Каннингем подчёркивал, что долг накапливается поминутно в "не совсем правильном" коде.
Связанный закон М. Лемана (1980):
- пока система развивается;
- её сложность растёт;
- если не вести постоянную работу по упорядочиванию структуры — рефакторинг;
- ревью;
- архитектурные записи.
Типичные причины
| Причина | Пример |
|---|---|
| Давление сроков | "Выпустить к пятнице", отложить тесты и документацию |
| Слабое сцепление с процессом | Бизнес не видит долг в бэклоге |
| Высокое зацепление модулей | Правка в одном месте тянет десяток файлов |
| Нет автотестов | "Костыли" в проде без сетки регрессии |
| Нет документации и обмена знаниями | Один человек "знает, как оно работает" |
| Отложенный рефакторинг | Чем больше кода написано поверх слабой структуры, тем дороже погашение |
| Недостаток опыта | Антипаттерны закрепляются в ядре |
Последствия и управление
Долг увеличивает неопределённость оценок: с каждым изменением в бэклог попадает "незавершённая работа" прошлых спринтов. Команды с предсказуемыми релизами ограничивают WIP и закладывают в план погашение долга — отдельные задачи, % ёмкости спринта, Definition of Done с тестами и ревью.
Погашение — это завершение отложенной работы — тесты, удаление TODO/FIXME, миграции схемы, рефакторинг, фиксация архитектурных решений (ADR). Мёртвый код из предыдущих разделов — частый вид долга с нулевой пользой и ненулевой ценой сопровождения.
Связанные материалы энциклопедии
- Для связи качества кода с производительностью — Архитектура выполнения программ
- Для анализа вызовов и сложных цепочек — Вызовы и иерархия
- Для снижения количества дефектов через тесты — Тестирование
- Для архитектурных решений по декомпозиции модулей — Проектирование и архитектура
Что запомнить
- Мёртвый код увеличивает стоимость сопровождения без пользы для поведения системы.
- История версий уже хранит удалённые решения, поэтому комментировать "на память" не нужно.
- Статический анализ и покрытие дополняют друг друга.
- Регулярная чистка кодовой базы ускоряет ревью и снижает риск ошибок.
Типичные ошибки
- Закомментированные блоки, которые остаются в репозитории месяцами.
- Игнорирование предупреждений линтера о неиспользуемых сущностях.
- Рефакторинг без удаления устаревших маршрутов и адаптеров.
- Отсутствие критериев, когда код считается техническим долгом.
Мини-практика
- Возьмите один модуль и запустите анализатор неиспользуемого кода.
- Разделите находки на "удалить сразу" и "проверить с командой".
- Удалите безопасные элементы отдельным коммитом.
- Добавьте правило линтера или CI-проверку, чтобы проблема не повторялась.