«Этот код выглядит ужасно, но работает». Что чинить первым, а что лучше не трогать
«Худшая стратегическая ошибка, какую может совершить софтверная компания», — так в апреле 2000 года Джоэл Спольски, сооснователь Fog Creek Software, назвал решение Netscape переписать код с нуля. С выхода версии 4.0 прошло почти три года, а версии 5.0 так и не случилось. Соблазн снести «ужасный» модуль знаком каждому, кто открывал чужой код ради мелкой правки.
«Худшая стратегическая ошибка, какую может совершить софтверная компания», — так в апреле 2000 года Джоэл Спольски, сооснователь Fog Creek Software, назвал решение Netscape переписать код с нуля. С выхода версии 4.0 прошло почти три года, а версии 5.0 так и не случилось. Соблазн снести «ужасный» модуль знаком каждому, кто открывал чужой код ради мелкой правки.
Примечание Adviser
В статье есть ссылки партнеров. Это значит, что если вы что-то покупаете с нашей помощью — вы также поддерживаете dev.by. (Вот другой способ).
При этом редакция и авторы независимы в выборе темы, концепции материала, фокуса описания, подхода к услугам или товарам. Прежде чем что-то советовать, мы много читаем и смотрим по теме, говорим с экспертами.
Редакция может выражать свое мнение и пробовать всё на себе.
Если рекомендательный материал обновляется, мы указываем, что и когда поменялось, в самом начале.
Почему код, который так бесит, не всегда стоит спринта
Опасно оценивать масштаб техдолга на глаз. Код сам по себе статичен, а пометки «тут неаккуратно» и «здесь длинный метод» ничего не говорят о том, как живёт система: что в ней меняют и что годами работает без правок.
Аккуратный по всем правилам модуль без тестов — пороховая бочка: любая правка рискует что-нибудь сломать. А «уродливый», зато покрытый тестами и изолированный модуль может годами работать и никому не мешать.
Техдолг к тому же бывает разным. Мартин Фаулер, автор книги «Рефакторинг», делит его по двум критериям.
Намеренный или непреднамеренный. Команда либо берёт долг сознательно, например чтобы успеть к релизу, либо обнаруживает его позже, когда узнаёт больше и видит, как можно было сделать лучше.
Обдуманный или безрассудный. Обдуманный компромисс ради релиза иногда и гасить не стоит, если проценты по нему невелики. Безрассудный долг берут без веской причины.
Бороться с некрасивым кодом, не разобравшись, откуда он взялся и как себя ведёт, значит превратить работу в бесконечный косметический ремонт.
А цена вопроса может быть немалая. По опросу Stripe и Harris Poll 2018 года (разработчики и руководители из США, Великобритании, Франции, Германии и Сингапура) разработчики оценивают, что из 41 часа недели около 17 уходит на отладку, рефакторинг, плохой код и техдолг. Четыре часа на «плохой код» Stripe оценивает почти в $85 млрд упущенной выгоды в год.
По данным CISQ за 2022 год, накопленный техдолг в США достиг примерно $1,52 трлн.
Ресурсов на всё не хватит, поэтому нужен способ отделить настоящие риски от мнимых.
По какому правилу выбирать, что чинить первым
Чтобы спор о техдолге опирался на данные, можно взять простую формулу:
Частота касания (Git Churn). Правки в кодовой базе обычно распределены неравномерно: у этого распределения длинный хвост (long tail), и большинство файлов в репозитории почти не меняются. Если код неидеален, но его редко трогают, рефакторинг не окупится. Смысл есть там, где код меняют регулярно.
Цена отказа (Cost of Failure / Risk). Эта ось отвечает на вопрос, что будет, если модуль сломается. Её определяют вероятность падения продакшена и его цена, то, насколько код тормозит команду, и метрики сложности: показатель Code Health (метрика CodeScene), цикломатическая сложность по Маккейбу (число независимых путей выполнения), плотность дефектов.
Хотспоты, где они пересекаются. Если наложить частоту изменений на сложность, получатся хотспоты (hotspots). По словам Адама Торнхилла, основателя CodeScene, в докладе на SREcon 2024, они занимают всего 2–4% кодовой базы, но притягивают 20–70% всей активности разработки.
Как это выглядит на практике, Торнхилл показал в статье Prioritizing Technical Debt in React: он разобрал в CodeScene открытый демо-проект на кодовой базе React, около 130 000 строк. Главным хотспотом оказался файл ReactFiberBeginWork.js: больше 3000 строк, показатель Code Health за год упал с 4 до 2,41 (по шкале, где 10 лучший).
Переписывать такой файл целиком слишком рискованно, поэтому Торнхилл применил метод X-Ray, разбор хотспотов на уровне отдельных функций. Первым кандидатом на рефакторинг оказалась функция beginWork: она сложна, часто меняется, и сложность растёт.
Что чинить, а что лучше оставить в покое
Частота правок и риск дают четыре зоны, и для каждой свое решение.
Часто правят, и код рискованный: хотспот. Сложный, нестабильный код, который постоянно трогают. Чинить первым: отдача здесь выше всего. Проблемы в хотспоте, по словам Торнхилла, обходятся дороже всего: это долг с высокими процентами.
Часто правят, но код простой. Понятный код, в который регулярно добавляют фичи. Глубокий рефакторинг тут не нужен, хватит автотестов и проверок качества в CI/CD.
Правят редко, но код страшный. Сложный, «уродливый» legacy-код, который работает без правок. Лучше не трогать: менять такой код рискованно, а выгоды нет.
Правят редко, и код простой. Простые модули, которые почти не обновляются. Про них можно забыть.
Этот же вопрос обсуждают разработчики на Reddit тот же вопрос: когда код поддерживают, когда рефакторят, а когда переписывают
И немного о «замороженном» коде. Возьмём условный пример: модуль нарушает все паттерны, но пять лет работает без сбоев и доработок не требует. Любая правка тут рискует сломать работающую систему.
Сейчас соблазн снести такой модуль стал сильнее: кажется, что нейросеть перепишет его за вечер. Голо Роден, основатель и технический директор компании the native web, пишет в колонке на heise (май 2026 года): «Печатать — не то, что будет выматывать команды в ближайшие годы. Читать — вот что». Впрочем, Спольски сформулировал закон ещё в 2000 году: читать код труднее, чем писать. Там было сказано, что в старом коде многие баги уже найдены и исправлены.
То же с компонентами, которые выводят из эксплуатации (System End-of-Life): если сервис заменят через полгода, чинить долг сервиса нет смысла.
Когда в сложный legacy-модуль всё же нужно добавить фичу, но переписывать целиком опасно, Фезерс описывает для этого два приёма, у каждого по два варианта:
Sprout Method / Sprout Class: новую логику пишут в новом методе или классе, которые вызывает старый код.
Wrap Method / Wrap Class: старый метод или класс оборачивают новым, который добавляет поведение и передаёт вызов старому.
Как найти хотспоты без дорогих инструментов
Для начала хватит данных, которые уже есть в системе контроля версий: Git хранит бесплатный поведенческий лог того, как команда работает с кодом. Частоту изменений можно выгрузить из истории коммитов стандартными командами git log.
Инструменты с открытым кодом:
Skunk считает для Ruby-кода показатель SkunkScore по сложности, запахам кода и покрытию тестами и показывает самые проблемные модули.
Detective работает с TypeScript-проектами: находит по истории git хотспоты и change coupling, то есть неочевидную связанность файлов.
RubyCritic собирает отчёт о качестве Ruby-кода на основе статических анализаторов Reek, Flay и Flog.
Баг-трекер. Частоту правок стоит наложить на журнал инцидентов: алерты P1/P2, тикеты поддержки. Модуль, где и правок много, и баги находят регулярно, и есть хотспот номер один.
Как договориться с менеджером, которому не до рефакторинга
Матрица хотспотов хорошо работает на уровне отдельных модулей. Мелкие правки делают вместе с текущими задачами: автотесты и локальный рефакторинг, без отдельного времени на «уборку». Крупные изменения, вроде распила монолита на микросервисы или переезда на другой фреймворк, считают вместе с владельцем продукта: что они дадут бизнесу и когда.
О том, как вносить такой долг в план, когда дорожной картой управляет продукт, тоже спорят на Reddit.
Что почитать и пройти, если хочется копнуть глубже
Вот три материала, по одному на задачу.
Если нужно безопасно менять код без тестов
Книга Working Effectively with Legacy Code (Michael Feathers). Книга Фезерса о том, как менять системы без тестов: каталог приёмов разрыва зависимостей, чтобы код можно было покрыть тестами.
Если нужно находить проблемные места по истории git
Книга Software Design X-Rays: Fix Technical Debt with Behavioral Code Analysis (Adam Tornhill). Торнхилл показывает, как опираться на данные, которые уже есть в системе контроля версий: находить хотспоты и change coupling, следить за трендами сложности. Серебряной пули, по словам автора, метод не даёт.
Если нужны основы чистого кода и приёмов рефакторинга
Курс Clean Code and Refactoring Techniques (Coursera). Начальный курс от Edureka, примеры на Python: как находить запахи кода (code smells), рефакторить функции, условия и классы и безопасно менять унаследованный код с помощью автотестов.
С чего начать рефакторинг
Если коротко, порядок такой:
Не чините код только потому, что он бесит. Уродливый модуль, который стоит без правок и без багов, работает на вас.
Выгрузите из Git частоту изменений и наложите на карту инцидентов.
Ловушка сеньорити: что может быть важнее сильного кода для промо-комитета
Представьте ситуацию. Третий год подряд говорят на performance review, что вы переросли текущий грейд. Но как только дело доходит до промо-комитета, заявка возвращается с размытой формулировкой: «Неясен импакт за пределами вашей команды».
Объяснение на страницу: почему не читают документацию и что исправить в первую очередь
Вы только что обновили README, добавили схемы в Confluence и закрыли самые частые вопросы. Но через пару дней кто-то из коллег всё равно пишет: «А как правильно?» Обычно после такого предлагают лучше документировать — и начинают добавлять страницы. Есть другое решение.
Как не превратить пет-проект во вторую работу: правила безопасного размера
Вы садитесь за свой проект в десять вечера, потому что днём было некогда. Правите баг, замечаете второй, к полуночи переписываете модуль, который вообще-то работал. А наутро вспоминаете про дедлайн, который сами себе и поставили. Кажется, это уже не совсем хобби.
«Давайте внедрим AI до пятницы»: как разобраться, где он помогает, а где создаёт команде новые проблемы
Что будет, если AI ошибётся в вашей задаче, и сколько времени уйдёт, чтобы эту ошибку найти? Если начальство попросило «внедрить AI везде», начинать надо именно с этого вопроса.
Хотите сообщить важную новость? Свяжитесь с редакцией через страницу контактов
Главные события и полезные ссылки в нашем Telegram-канале
Обсуждение
Комментируйте без ограничений
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.