«Работает — не трогай!» — знакомая фраза? Звучит как девиз стабильности. Но в наше время все меняется со слишком большой скоростью, и такой подход может стать настоящей ловушкой Джокера. Оставленный без внимания проект рискует превратиться из мощного инструмента решения проблем в неподъемный багаж, неспособный соответствовать новым требованиям.
Как же понять, когда «не трогать» становится опаснее, чем «поменять»? Как определить момент, когда старый код начинает замедлять развитие, а не поддерживать его? Меня зовут Филипп Щербанич, я работаю backend разработчиком уже много лет, и сегодня я хочу поговорить с вами о рефакторинге — о том, как найти баланс между работоспособностью и необходимостью изменений, как сохранить проект конкурентоспособным и жизнеспособным, и как, наконец, сделать этот самый рефакторинг.
Развенчиваем мифы
Согласно статистике, полученной на основе анализа более 7 млн. проектов с открытым исходным кодом, около 70% open-source инструментов и библиотек, на которые опирается современное программное обеспечение, либо не поддерживаются, либо находятся в неудовлетворительном состоянии. Подобной информации о закрытом ПО я не нашел, но по моим наблюдениям, ситуация тут не намного лучше. Это может касаться не всего коммерческого проекта, а каких-то его частей или модулей, которые остаются без должного внимания. Причины такого положения дел могут быть совершенно разными, но результат один – код заброшен и постепенно устаревает, разрастаясь и отравляя весь проект. Это самая наглядная иллюстрация того, к чему приводит злоупотребление лозунгом “работает – не трогай”. В его основе лежат три ключевых мифа, которые я сейчас попробую развеять.
«Правки — это слишком долго, дорого и нудно».
На первый взгляд, рефакторинг действительно может показаться затратным процессом. Требуется время на анализ, внесение изменений, переписывание и создание тестов. Но отложите перемены на потом – и вы рискуете заплатить еще дороже, причем речь не только о деньгах. С увеличением технического долга разрабатывать новый функционал становится все сложнее, растут затраты на поддержку устаревающей системы. Спустя какое-то время, накопленные ошибки в плохо поддерживаемом коде могут привести к огромным потерям, подчас несоизмеримым с инвестициями в его улучшение. На основании собственного опыта, могу утверждать: рефакторинг – это инвестиция, а не трата. Компании, которые им пренебрегают, часто сталкиваются с лавинообразным ростом проблем: время на устранение багов увеличивается, команда теряет мотивацию, а пользователи – доверие к продукту.
«Проще потом все переписать с нуля».
Звучит заманчиво. Начать с чистого листа, без устаревших ограничений – кто не мечтал о таком и кто только не пробовал это осуществить? Однако, на практике «переписывание с нуля» редко оказывается эффективным. Ведь старая система – это не только код, но и накопленный опыт, исправленные ошибки и понимание реальных требований пользователей. Переписывание практически всегда сопряжено с потерей критически важных деталей, срывом сроков и новыми нежданными багами. Да и, будем честны, оно далеко не всегда доводится до конца. Другое дело рефакторинг — он позволяет улучшить систему, не теряя ее основного функционала. Вот почему даже Ной сделал бэкап всей фауны. Эволюционный подход позволяет все сделать постепенно, без разрушения устоявшегося фундамента и повышенных рисков.
«Рефакторинг нужен только старому коду. Мой проект еще слишком молод».
Многие ошибочно считают, что рефакторинг — это нечто вроде капитального ремонта для «ветхих» систем, которые разрабатывались 2, 5 или даже 10 лет назад. Однако, даже самые молодые проекты со свежим кодом могут страдать от технического долга, особенно если они разрабатывались в спешке или без должного внимания к архитектуре. И если об этом забывать, проблемы накопятся как снежный ком. Рефакторинг — это не только исправление ошибок прошлого, но и способ строить будущее (как коммунизм, только лучше). Это процесс, который помогает постепенно адаптировать систему к новым требованиям, повысить ее производительность и удобство поддержки.
Но бывает же, что трогать и правда не надо?
Разумеется, бывает. Например, когда речь заходит об оголенном проводе. Или о программисте, который сейчас очень занят. С рефакторингом то же самое: иногда делать его не только не нужно, но даже опасно. Вот некоторые признаки того, что рефакторинг может навредить, а стабильность проекта может быть нарушена:
Система изолирована и не интегрирована с другими системами, а значит не подвержена ошибкам от изменений в этих системах.
Пользователи и владельцы полностью удовлетворены текущей функциональностью, изменения не планируются в будущем (а значит, не потребуется работать с устаревшим кодом).
Риск изменений значительно превышает потенциальную выгоду.
Проект на стадии завершения или уже запланирован его переход на новую систему. Скажем, если компания планирует заменить устаревшую CRM-систему через полгода, будет куда целесообразней минимизировать поддержку текущей системы и направить силы на создание новой, чем пытаться «подлатать» то, что уже устарело и скоро будет не нужно.
Если ни один из этих признаков в вашем проекте не обнаружен, продолжаем анализ. Необходимо честно и правильно ответить на следующие вопросы:
Есть ли конкретные проблемы, которые нужно решить?
Перевешивает ли польза от потенциальных улучшений возможные риски?
Соответствует ли рефакторинг текущим приоритетам компании и команды?
Важно уметь различать ситуации, когда действительно стоит оставить все как есть, а когда отказ от изменений приведет к накоплению проблем в будущем. Как это сделать? Объективно оценить кодовую базу и состояние проекта с точки зрения бизнеса.
Оцениваем код и тесты
Понять, что рефакторинг действительно необходим, часто можно через объективный анализ кода и наблюдение за системой (тревожные сигналы – замедление и усложнение вашей работы, рост количества багов и ошибок при добавлении даже небольших изменений). Но не следует бросаться на любой старый код и рефакторить все подряд. Как правило, проект пишут разные люди с разным опытом в разное время, и в некоторых случаях «плохой» с вашей точки зрения код может не являться таковым на самом деле. Поэтому решение о рефакторинге, особенно если он серьезный и касается важных частей проекта, должно быть командным.
Существуют инструменты, способные определять качество кодовой базы в автоматическом режиме. Например SonarQube – платформа с открытым исходным кодом для непрерывного анализа и измерения качества кода. Она поддерживает все популярные языки программирования, от Python, JS и PHP до Swift, Java и C. У этого проекта есть шикарная документация и он может быть использован даже с популярными IDE.
Другое распространенное решение – Qodana от компании JetBrains. Оно тоже поддерживает все популярные языки программирования и интегрируется с разными средами разработки и CI/CD-пайплайнами. Qodana может по заданными политикам проверять на совместимость лицензии зависимостей, используемых на проекте, и агрегировать репорты от других анализаторов. В общем, инструмент крутой, но функционал бесплатной версии сильно урезан. Впрочем, платный вариант стоит всего 5$ в месяц за разработчика в Ultimate версии. Весьма достойный выбор, особенно если вы используете IDE от JetBrains.
Существует и масса более простых бесплатных альтернатив. Например, можно применить open-source статические анализаторы кода: для PHP хорошим выбором будет PHPStan или Psalm, для Python подойдут pylint или flake8, а для Golang, кроме go vet, можно взять какой-нибудь staticcheck. Все эти решения настраиваются через конфигурационные файлы, позволяя сканировать не весь проект, а только его части. Таким образом вы сможете выискивать проблемные места для рефакторинга постепенно, шаг за шагом.
Показать потребность рефакторинга могут и решения вроде PHPMD (PHP Mess Detector) для PHP или PMD (Programming Mistake Detector) для Java, JavaScript, Kotlin, Swift и других языков. Эти инструменты анализируют код, выявляя избыточную сложность, дублирование, устаревшие конструкции и потенциальные ошибки. Они помогают увидеть проблемные места, которые требуют внимания, и дают рекомендации для их исправления.
Помимо качества самого кода, важно следить и за покрытием проекта тестами. Отсутствие тестов – это тоже показание к рефакторингу и работе с техническим долгом. Для оценки покрытия можно использовать такие инструменты, как php-code-coverage для PHP, Coverage.py для Python, или Istanbul Code Coverage для JavaScript. Подобные инструменты существуют для любого популярного языка. Они генерируют отчеты о том, какие участки кода уже покрыты тестами, а какие еще требуют внимания, что определенно поможет улучшить качество тестирования вашего проекта. Кстати, SonarQube и Qodana тоже могут анализировать покрытие кода тестами. В общем, подбирайте наиболее подходящий вам инструмент, выбор действительно огромный.
Оцениваем бизнес-задачи
Несомненно, кодовая база важна, но ее ценность всегда определяется тем, какую задачу она решает. Код – это инструмент для достижения целей проекта, и нельзя забывать о конечных пользователях и их потребностях. Если структура проекта, его архитектура или качество кодовой базы начинают препятствовать достижению этих целей, исправления становятся неизбежными. Итак, что же может говорить о необходимости рефакторинга с точки зрения бизнеса? Конечно же, собранные метрики! Давайте определим некоторые метрики проекта, которые нам следует начать измерять для принятия дальнейших решений:
Стабильность работы системы, в том числе в пиковые часы. Не секрет, что плохо работающая система может стать причиной потери ее пользователей. Причем в пиковые часы вам грозят наибольшие потери, если ваша система начнет слишком сильно глючить.
Число багов и скорость их исправления. Баги на то и баги, чтобы ломать ожидаемое поведение системы, а, значит, они могут влиять и на доходы компании. Необходимо научиться измерять их количество и среднее время, требуемое на их исправление. Если показатели начнут меняться в худшую сторону, значит, в системе пора что-то менять.
Скорость отклика бэкенда и фронтенда. Было проведено немало исследований, которые показывали корреляцию между скоростью работы приложения или программы и количеством сделанных в ней покупок. Так что для бизнеса медленная система может быть явным сигналом к ее рефакторингу.
Продуктовые метрики. Очень важно научиться собирать основные метрики, которые показывают эффективность системы с точки зрения бизнеса: количество регистраций, ретеншн, средний чек, время, проведенное на сайте или в приложении и так далее. Работая в самых разнообразных компаниях, я не раз сталкивался с тем, что деградация кодовой базы начинала существенно влиять на эти показатели. В больших системах мы не всегда можем наверняка сказать, что именно повлияло на показатели бизнеса, но не учитывать такие важные индикаторы – это непростительная ошибка. Метрики, конечно, будут уникальными для разных типов проектов, поэтому их следует тщательно проработать, тогда в будущем они начнут давать полезные сигналы о необходимости перемен.
После начала сбора данных и их анализа, вы наверняка сможете сказать, нужно ли вам вкладывать дополнительные усилия в переработку того, что уже было создано ранее. А еще эти метрики станут отличным обоснованием в потребности работ для вашего руководства. Поэтому, лично я никогда не игнорирую сбор метрик работы системы. И никому не советую.
Приступаем к рефакторингу
Анализ показал, что рефакторинг все-таки необходим вашему проекту, и вы готовы к нему приступить? Отлично, проект скажет вам спасибо. Но только если вы все сделаете правильно. Главное – работать постепенно. Не пытайтесь охватить весь проект сразу: разбейте рефакторинг на небольшие, независимые задачи, которые можно выполнять последовательно. Лучше всего будет начать с самых проблемных участков кода (так называемых hot spots), которые чаще всего вызывают сбои или затрудняют разработку. А вот советы, которые помогут организовать этот процесс.
Заранее определите общий стиль и правила написания нового кода. Старайтесь использовать общепринятые стандарты. Например, в PHP применяйте PSR, в JS можно использовать Airbnb JavaScript Style Guide (в этом языке нет единственно правильного варианта), а в Python следуйте PEP8. Не забудьте заранее обсудить выбранные стандарты с командой, чтобы у всех было единое понимание. Чтобы новый код всегда соответствовал этим стандартам, добавьте линтеры: настройте автоматическую проверку перед пушем или PR. Если в проекте используется Git, вы можете создать pre-commit хук, который автоматически запускает статический анализ перед фиксацией изменений и запрещает комит, если что-то пошло не так. Новый код уже должен соответствовать новым правилам, чтобы не увеличивать объем кода под рефакторинг в будущем. После того, как новый код начал соответствовать правилам, постепенно расширяйте проверки на небольшие старые участки проекта. Старайтесь начинать с наименее сложного. Тут отлично работает правило Парето: 20% простого рефакторинга даст вам улучшение кодовой базы на 80%. Не стесняйтесь использовать инструменты автоматизированного рефакторинга вроде PHPRector, Rope, Jscodeshift или gopatch (такой инструмент нетрудно найти для любого языка программирования). Они легко помогут привести код к ожидаемому виду – их нужно лишь правильно настроить.
Пишите больше тестов. Тесты должны сопровождать реализацию рефакторинга. Мы ведь пытаемся улучшить код, который существует уже довольно давно. Поэтому он, скорее всего, глубоко встроен в бизнес-логику и правки могут сломать проект в самых неожиданных местах. Поэтому тут не обойтись без хорошего тестирования. Понятно, что в процессе рефакторинга часть старых тестов может сломаться или устареть из-за серьезного изменения функционала, но верхнеуровневые и среднеуровневые тесты вас подстрахуют. Не забывайте рефакторить и сами тесты – они тоже важная часть вашего проекта.
Следите за метриками вашего проекта. Метрики мы уже обсуждали выше, но важно понимать разницу. В первом случае метрики помогают диагностировать проблемы, решаемые при помощи рефакторинга. Сейчас же речь о другом, хотя метрики в основном остаются теми же. В процессе рефакторинга важно следить как за системными (скорость ответа системы, использование ресурсов, количество ошибок), так и за продуктовыми метриками (вроде конверсий или ретеншена). Эти данные помогут понять, улучшается ли экосистема проекта после рефакторинга с точки зрения пользователя, и где еще остаются узкие места. Если вы проигнорировали совет из этой и из предыдущей главы, и у вас все еще нет никаких метрик, организуйте хотя бы сбор системных данных, прежде чем что-то сделать, и подождите какое-то время, пока у вас наберется достаточно полная статистика.
Старайтесь использовать больше готовых библиотек и фреймворков. По возможности старайтесь минимизировать количество "изобретенных велосипедов" в вашем проекте, выбирая проверенные решения, выработанные и одобренные сообществом. Перед подключением решений убедитесь, что библиотека активно поддерживается, лицензия подходит вашему проекту, а версия зафиксирована в менеджере зависимостей. Кстати, разумным решением будет сохранять код библиотек: тогда, если с оригиналами что-то случится, ваш проект не сломается при обновлении зависимостей. Для этого вы можете хранить зависимости в рабочем репозитории или использовать решения, позволяющие их кешировать вроде composer/satis для PHP или Verdaccio для JS. И да, еще зависимости нужно не забывать регулярно обновлять, чтобы и внутри проект был "свежим". Периодически проводите их аудит, и если используемое решение было заброшено создателями, подумайте о его замене. Эти проверки также можно автоматизировать с помощью npm-check, composer audit и множества других решений доступных для любого популярного языка программирования.
Улучшая код, важно не забывать улучшать и старую документацию! Да, документация – тоже часть вашего проекта, которой никогда нельзя пренебрегать. И если рефакторинг ее только ломает, польза от него резко сокращается: ведь, исправив одну проблему, вы лишь создадите себе другую.
Без проверок на наличие уязвимостей и секретов в коде не обойтись. Для этого существует множество инструментов, таких как Trivy, Snyk, или Bandit. Эти проверки тоже можно встроить в CI/CD-пайплайн, чтобы находить проблемы на раннем этапе. Если в коде хранятся секреты, то это точно то место, которое следует рефакторить как можно раньше. А где их хранить – я более подробно описал в одной из своих предыдущих статей про бэкенд разработку.
Своевременно обновляем стороннее ПО и все, что используется в разрабатываемом проекте. Это касается не только библиотек, но и серверного ПО, СУБД, и операционных систем. Старое ПО может быть уязвимым или нестабильным. Но к обновлениям подходите с осторожностью, после них обязательно тестируйте свое приложение в изолированной среде.
Ах да, чуть не забыл: не совмещаем рефакторинг с выполнением бизнес-задач! Принцип “чтоб два раза не вставать” выглядит очень соблазнительно, но старайтесь разделять эти процессы. Когда рефакторинг совмещается с бизнес-задачами, всегда есть риск, что вы потеряете фокус либо на конечной цели (бизнес-результате), либо на качестве кода. В итоге, погнавшись за двумя зайцами, можно не догнать ни первого, ни второго. А разделив эти две задачи, мы избегаем ненужной путаницы и задержек в выполнении важного для бизнеса. Кроме того, сократится и количество багов, ведь так код бизнес-задачи не будет переусложнен, и ревьювер только поблагодарит вас. Ну а, в будущем будет легче анализировать, сколько времени ушло на задачи бизнеса, а сколько вы потратили на технические улучшения.
Заключение
Надеюсь, мне удалось убедить вас, что рефакторинг – это не просто «переписывание ради переписывания», а стратегический инструмент, который помогает вашему проекту оставаться живым, производительным и поддерживаемым. Если, конечно, вы сомневались в этом ранее.
Благо сейчас существует огромное количество инструментов и методов для анализа и автоматического улучшения кода, которые помогают разработчикам делать его быстро и почти безболезненно. О некоторых из этих инструментов я попытался рассказать, но все упомянуть невозможно. Кстати, прогресс не стоит на месте, и уже активно разрабатываются и внедряются новые, более умные решения ИИ-рефакторинга на базе LLM. И, возможно, в будущем нам достаточно будет нажать лишь одну кнопку, чтобы код стал чище и понятнее.
Но пока этой кнопки нет, не забывайте делать рефакторинг самостоятельно. Без регулярного улучшения кода ваша система рискует стать наследием таким же тяжелым, как ЭЛТ-монитор: новые функции будут внедряться все медленнее, лечить баги будет все сложнее, а разработчики потеряют мотивацию еще до начала работы. Да пребудет с нами рефакторинг, и пусть он принесет нам только пользу!
Комментарии (5)
FM12
15.01.2025 22:22Спасибо за системную статью.
Вот вроде бы и какие-то вещи знаешь, умеешь, используешь.
Но когда все разложено по полочкам - знания становятся упорядоченными и легче передаются.
AidaKarmen
«Вот почему даже Ной сделал бэкап всей фауны» - отлично сказано ☺️ Интересно было узнать, что столько различных современных инструментов существует для рефакторинга, буду использовать в работе. Полезно, спасибо!
serginho
У Ноя как раз бекапов не было. При полном обновлении инфраструктуры он взял особь каждого вида/пола в единственном экземпляре. Надо было строить резервный ковчег по хорошему, и туда тоже каждой твари по паре)