惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
C
Cyber Attacks, Cyber Crime and Cyber Security
Recent Announcements
Recent Announcements
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Security Latest
Security Latest
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cisco Talos Blog
Cisco Talos Blog
Apple Machine Learning Research
Apple Machine Learning Research
C
Check Point Blog
T
Threat Research - Cisco Blogs
I
Intezer
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
WordPress大学
WordPress大学
Engineering at Meta
Engineering at Meta
腾讯CDC
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
T
Tenable Blog
V
Visual Studio Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
Spread Privacy
Spread Privacy
GbyAI
GbyAI
T
Tailwind CSS Blog
P
Palo Alto Networks Blog
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog
NISL@THU
NISL@THU
Blog — PlanetScale
Blog — PlanetScale
G
GRAHAM CLULEY
K
Kaspersky official blog
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
P
Proofpoint News Feed
S
SegmentFault 最新的问题
P
Proofpoint News Feed
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
博客园 - 【当耐特】
Cyberwarzone
Cyberwarzone
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
V
Vulnerabilities – Threatpost
H
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
博客园 - 叶小钗

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Работа с легаси кодом: не переписывать, а приручить
valeryan15 ( · 2026-04-24 · via Все публикации подряд на Хабре

Работа с легаси кодом: не переписывать, а приручить

Уровень сложностиСредний

Время на прочтение14 мин

Охват и читатели155

Кейс

Привет, Хабр! Я Валерий Маланин, фронтенд-разработчик в команде Modus BI. И по опыту знаю, что каждый разработчик хотя бы раз мечтал попасть на проект, где всё с нуля. Свежий стек, понятная архитектура, аккуратные модули, тесты, документация и никаких комментариев в духе «не трогать, иначе всё упадёт». В таком проекте легко писать новый код и приятно разбираться в старом.

Но в реальности всё обычно выглядит иначе. Команда приходит в продукт — а там React 16, Webpack 2, компонент на две тысячи строк, круговые зависимости и ни одного теста. И это не исключение, а обычная картина для живой системы, которая давно работает в проде.

Любой проект со временем накапливает легаси. Бизнес торопит и заставляет срезать углы. Команда меняется, и вместе с ней уходит контекст старых решений. Технологии устаревают, а код остаётся. В итоге систему становится страшно менять, потому что никто до конца не понимает, что сломается после очередной правки.

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

Что такое легаси

Майкл Физерс в книге «Working Effectively with Legacy Code» дал простое определение: легаси — это код без тестов. Вне зависимости от возраста и технологий. Если код нельзя безопасно изменить — он уже легаси, даже если написан вчера.

Но есть и более широкий взгляд. Легаси-код — это унаследованный код, который продолжает работать в продакшене, но сложен в изменении: нет тестов, нет документации, технологии устарели или логика размазана без чёткой структуры. Это код, где решения, принятые три года назад из лучших побуждений, сегодня стали ежедневной проблемой.

И самое важное: легаси — это не аномалия и не чья-то личная ошибка. Это естественный результат живого продукта. Любая система, которая долго развивается, накапливает компромиссы. Бизнес требует выпускать фичи быстрее. Команда меняется. Контекст решений теряется. Поверх старой логики наслаивается новая. В какой-то момент кодовая база начинает хранить не только текущую реализацию, но и всю историю продукта вместе с её ограничениями.

Поэтому задача инженера — нем переписывать код с нуля, а признать, что легаси — это рабочая система, которая просто стала дорогой и рискованной в изменении. А значит, работать с ней системно, без паники и с пользой для продукта.

Изучение проекта и установка правил

Прежде чем что-то менять — нужно понять, что имеешь. Звучит очевидно, но на практике эту фазу часто пропускают. Разработчик приходит на проект, видит хаос и сразу хочет «навести порядок»: обновить зависимости, разрезать большой компонент, вынести бизнес-логику из UI, включить строгий линтер.. Результат предсказуем: что-то ломается, команда нервничает, а рефакторинг откатывается.

Шаг 1. Археология кода

Археология кода, или software archaeology, звучит громко, но по сути это обычная инженерная работа: система изучается слой за слоем, а команда пытается восстановить её реальную структуру. На этом этапе не нужно ничего улучшать — сначала составляется карта. Для этого следует:

  • Изучить структуру директорий и зависимости между модулями. Открыть package.json, посмотреть, какие библиотеки тянет проект, какие уже устарели, какие дублируют друг друга. Пройтись по папкам и понять, где живёт бизнес-логика, где утилиты, где UI, где слой работы с API. В легаси-проектах всё это часто перемешано: в components/ лежат и элементы интерфейса, и хелперы, и куски логики, которые вообще не должны были там оказаться.

Пример из практики

На одном из проектов Modus BI папка components/ содержала всё вперемешку — UI, бизнес-логику. Никаких подпапок, никакой группировки. Компонент кнопки лежал рядом с компонентами целых разделов. Прежде чем что-либо рефакторить, ушла неделя просто на то, чтобы составить карту: что от чего зависит, что используется, а что давно мёртвый код.

  • Найти точки входа и критические пути. Какие сценарии в приложении самые важные для пользователя? Какие модули участвуют в авторизации, оплате, сохранении данных, построении ключевых экранов? Это даст понимание, что трогать в последнюю очередь.

  • Определить «горячие» места. Самый простой способ — посмотреть git log и найти файлы, которые чаще всего менялись за последний год. Если файл трогали 200 раз — скорее всего, это и есть болевая точка проекта. Именно там скапливаются баги, именно там код становится наиболее хрупким.

  • Зафиксировать архитектуру «как есть». Даже если она далека от идеала, её нужно сделать видимой. На доске, в Miro, в диаграмме зависимостей, в обычном документе — неважно. Главное — вынести из голов в явный вид связи между слоями, сервисами, стором, API-клиентами и компонентами. Пока этой карты нет, команда работает вслепую и спорит не о системе, а о своих представлениях о ней.

На этом этапе важно смотреть не только на связи между модулями, но и на границы ответственности. Где заканчивается интерфейс и начинается бизнес-логика. Где данные преобразуются, а где только отображаются. Какие модули можно считать внутренними, а какие уже стали опорными точками для половины системы. Без этого любая попытка «почистить код» быстро превращается в случайный набор правок.

Шаг 2. Определение узких мест

Когда карта проекта готова, нужно понять, что сильнее всего тормозит разработку. Например, где команда теряет время, где чаще всего ошибается и какие участки кода делают любое изменение дорогим и рискованным. Вот типичные проблемы, с которыми сталкиваются на легаси-фронтенде:

  • Медленные сборки. Устаревшая билд-инфраструктура — один из самых болезненных тормозов. Каждый npm run dev или npm run build превращается в ожидание. Это не просто раздражает — это убивает рабочий ритм: пока разработчик ждёт сборку, он теряет контекст, отвлекается, переключается на другие задачи. 

Пример из практики 

На одном из проектов сборка дев-версии и прод-сборка занимали около 30 минут. Чтобы просто начать работу, нужно было полчаса ждать. После обновления сборщика с Webpack 2 до Webpack 5 время сборки сократилось примерно до минуты. Это не изменило бизнес-логику и не добавило ни одной новой фичи, но моментально изменило ритм работы всей команды.

  • Хрупкие места. Это участки, которые страшно трогать. Обычно там нет тестов, много неявных зависимостей и длинный хвост побочных эффектов. Любое изменение потенциально ломает что-то в другом конце приложения.

  • Модули без владельца. Код, за который никто не отвечает и который никто толком не понимает. Его писал человек, который давно ушёл, а документации нет. Почему здесь именно такой if на 471-й строке, никто и не помнит, но удалить не решаются.

  • Протечки между слоями. UI-компоненты знают о деталях API. Бизнес-логика размазана по компонентам вперемешку с разметкой. Стейт хранится где попало: часть в Redux, часть в локальном стейте, часть в URL-параметрах.

Если какой-то участок уже выглядит опасным, полезно посмотреть не только на код, но и на его поведение в проде. Какие ошибки там всплывают? Есть ли повторяющиеся инциденты? На что чаще всего жалуются пользователи? Иногда проблемное место видно не по размеру файла, а по тому, сколько нестабильности оно создаёт вокруг себя.

Задача этого этапа не в том, чтобы сразу всё исправить. Сначала нужно увидеть, где у проекта реальные точки напряжения. 

Шаг 3. Установка правил для команды

Когда есть карта проекта и видно основные болевые точки — время установить правила. Не «навести порядок во всём проекте за спринт», а зафиксировать: как пишем новый код:

Описать архитектуру «как должно быть»

Даже если сейчас проект устроен иначе, у команды должен появиться ориентир. Нужно зафиксировать, где должна жить бизнес-логика, где проходит граница между UI и слоем работы с данными, какие зависимости между модулями допустимы, а какие нет. Например, компонент не должен напрямую знать о формате ответа API, а сервис — тянуть код представления.

Такие решения лучше фиксировать в ADR. Тогда они не остаются на уровне устных договорённостей и не зависят от того, помнит ли кто-то исходный замысел.

Ввести правила для нового кода

В этом случае базовый минимум:

  • ESLint и Prettier;

  • единые правила именования;

  • понятная структура компонентов и файлов;

  • общие договорённости по организации модулей.

Но есть нюанс. Эти правила не стоит сразу распространять на весь легаси-проект. Если прогнать строгий линтер по всей кодовой базе разом, команда получит огромный MR на сотни файлов, который никто не сможет нормально отревьюить. Формально код станет «чище», а по факту проект просто утонет в косметических правках.

Пример из практики

Мы один раз попробовали включить строгий ESLint-конфиг на весь проект сразу. Результат — около 4 200 ошибок и ни одного желающего это разгребать. Со второй попытки сделали иначе: новые правила применялись только к изменённым файлам, а старый код проверялся по минимальному конфигу. Через некоторое время значительная часть кодовой базы уже соответствовала новым стандартам — без единого героического MR.

Ограничить зависимости между слоями

Если не зафиксировать правила зависимостей, новый код очень быстро повторит старые ошибки. UI снова начнёт тянуть в себя бизнес-логику. Сервисы снова полезут в стор. Временные обходы снова превратятся в постоянную архитектуру.

Поэтому здесь важно заранее договориться:

  • какие слои могут зависеть друг от друга;

  • какие импорты считаются нормальными;

  • какие связи считаются нарушением архитектуры;

  • где проходят границы ответственности модулей.

Легаси редко возникает из-за одного плохого решения. Чаще оно накапливается через десятки мелких компромиссов, которые никто вовремя не остановил.

Подготовить онбординг-документацию

Новый разработчик должен быстро понять:

  • как проект устроен сейчас;

  • какие зоны в нём уже считаются проблемными;

  • по каким правилам пишется новый код;

  • куда лучше не заходить без дополнительного контекста.

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

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

Модернизация

Легаси нельзя переписать за выходные. Но его можно постепенно улучшать. Ключевое слово — «постепенно».

Шаг 4. Выбрать стратегию модернизации

Команда приходит в старый проект, видит хаос, устаревший стек, странные зависимости, медленные сборки, модули без тестов — в итоге возникает соблазн всё выкинуть и написать с нуля. Но на практике это почти всегда дороже и рискованнее, чем кажется.

Старый код хранит не только неудачные решения, но и реальные знания о системе. В нём зашиты редкие бизнес-сценарии, старые ограничения, обходы под внешние интеграции, реакции на сбои, накопленные за годы работы в проде. Часто это выглядит как странный if, лишняя проверка или неочевидная ветка в обработке данных. Но за каждой такой конструкцией обычно стоит не чья-то прихоть, а опыт прошлых инцидентов.

При переписывании с нуля команда почти неизбежно теряет часть этого контекста. Новая система выглядит чище, но в ней ещё нет того запаса «боевой» устойчивости, который старая кодовая база получила за годы эксплуатации. 

Вторая проблема — big rewrite редко происходит в вакууме. Пока одна часть команды строит новую систему, старая продолжает жить в проде. Её нужно поддерживать, чинить, синхронизировать по логике и данным. В какой-то момент появляются две системы сразу: одна уже сложная, но рабочая, и вторая — ещё не зрелая, но потребляющая ресурсы. Это резко повышает стоимость изменений и почти всегда растягивает миграцию дольше, чем планировалось.

С инфраструктурной точки зрения такой подход тоже опасен. Новая система требует заново выстроить сборку, деплой, конфигурацию окружений, логирование, мониторинг, алерты, rollback-сценарии, доступы и эксплуатационные процедуры. То есть команда переписывает не только код, но и весь операционный контур вокруг него. Если сделать это одним рывком, риск регрессий, сбоев на выкладке и расхождений в поведении становится слишком высоким.

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

Шаг 5. Паттерн Strangler Fig

Название этого паттерна пришло из ботаники. Фиговое дерево-душитель растёт вокруг другого дерева, постепенно его обвивает и со временем занимает его место. Ровно так же работает этот паттерн с кодом — не вырывает старую систему одним движением, а заменяет её по частям, пока новая логика не возьмёт на себя весь нужный функционал.

Обычно этот процесс выглядит так:

  1. Заморозить старый участок. Команда договаривается, что в старый модуль больше не добавляется новая функциональность. Его не развивают, а только поддерживают: исправляют баги, закрывают критические инциденты и следят, чтобы он не ломал остальную систему.

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

  3. Переключать нагрузку постепенно. Это можно делать через фича-флаги, маршрутизацию, конфигурацию окружения или частичное включение для части пользователей. Главное — не переводить все сразу, пока система не стабилизировалась.

  4. Смотреть на поведение в проде. После переключения важно проанализировать:

    • выросло ли число ошибок;

    • не просело ли время ответа;

    • нет ли деградации по ключевым сценариям;

    • как ведут себя логи, метрики и алерты;

    • не появились ли различия между старой и новой реализацией.

  5. Удалить старый код. Когда новый модуль заработал, старый можно убирать: удалять код, вычищать зависимости, убирать старые конфиги, маршруты и временные прослойки. До этого момента миграция не считается завершённой.

Этот подход отлично работает на фронтенде. Его применяли при переходе от монолита к микрофронтендам, при обновлении React-приложений по частям.

Пример из практики

Мы в Modus применяли этот подход при миграции визуализации с движка PlotlyJS на AMCharts. Сначала написали новый модуль рядом со старым. Некоторое время они жили вместе на проде, но новой версией пользовалось только ограниченное количество людей — остальные продолжали работать на старой. Когда все баги были исправлены и метрики показали стабильность, старый модуль удалили одним MR. Больше 3 000 строк — в корзину.

Шаг 6. Тесты как основа безопасного рефакторинга

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

Здесь хорошо работают characterization tests — тесты, которые фиксируют фактическое поведение системы. Разработчик берёт существующий модуль, прогоняет через него реальные входные данные, смотрит, что происходит на выходе, и закрепляет этот результат тестами. Такой подход особенно полезен там, где логика давно обросла исключениями, побочными эффектами и неочевидными ветками, а документации либо нет, либо она давно не совпадает с кодом.

В легаси это важно по двум причинам:

  • Тесты помогают понять, что именно делает код. Пока логика спрятана в большом модуле без проверок, команда видит только структуру файла. Как только появляются тесты на реальные сценарии, система становится понятнее.

  • Тесты дают минимальную страховку перед изменениями. Если после рефакторинга поведение изменилось, команда увидит это сразу, а не через баг-репорт из продакшена в конце недели.

Но здесь важно не впасть в другую крайность и не пытаться сначала построить идеальную тестовую пирамиду на весь проект. В легаси это почти всегда не работает. На старте задача проще:

  • зафиксировать текущее поведение в самых рискованных местах;

  • покрыть основные пользовательские и системные сценарии;

  • проверить участки, где уже были инциденты или частые регрессии;

  • дать команде возможность менять код без слепого страха.

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

Пример из практики

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

Без тестов рефакторинг легаси быстро превращается в русскую рулетку. Можно угадать и ничего не сломать. А можно убрать «лишнюю» проверку, которая годами защищала систему от редкого, но болезненного сбоя. Поэтому в старом проекте тесты — это минимальное условие для безопасных изменений.

Шаг 7. Инкрементальный рефакторинг

После фиксации текущего поведения модель можно менять. Но здесь важно не сорваться в большой рефакторинг сразу. В легаси почти всегда выигрывают маленькие итерации: один модуль, один участок логики, один понятный MR. Чем меньше объём изменений, тем проще их проверить, отревьюить, откатить и связать с причиной, если после выкладки что-то пошло не так.

Главное правило — не смешивать рефакторинг и новую функциональность в одном MR. Если в одном изменении команда одновременно переписала старый модуль, обновила зависимости и добавила новую фичу, потом почти невозможно быстро понять, что именно стало источником проблемы. В проде такие истории заканчиваются одинаково: система ведёт себя нестабильно, команда тратит время не на исправление, а на поиск виновника.

Поэтому инкрементальный рефакторинг обычно строится так:

  1. Выбрать один проблемный участок. Не весь проект или раздел, а конкретный модуль, компонент или кусок логики, который действительно мешает двигаться дальше.

  2. Зафиксировать его текущее поведение. Через тесты, логи, метрики, иногда через ручную проверку критических сценариев, если участок особенно чувствительный.

  3. Внести одно понятное изменение. Например, вынести бизнес-логику из UI-компонента, разорвать неудачную зависимость, раздробить один монолитный модуль на несколько частей или убрать дублирование.

  4. Проверить и выкатить отдельно. Не прятать такую правку внутри большого релиза, а дать ей пройти понятный цикл проверки и выкладки.

  5. Только потом идти к следующему шагу. Не пытаться «добить всё до конца», пока не стало ясно, что предыдущий кусок пережил изменение нормально.

Приоритизация здесь тоже важна. Начинать лучше с того, что сильнее всего мешает команде. Если медленная сборка убивает продуктивность — обновите билд-инфраструктуру первой. Если один компонент-монстр на 2000 строк генерирует 80% багов — начните разбивать его.

С инженерной точки зрения у маленьких изменений есть ещё один плюс: они лучше живут в поставке. Небольшой MR проще ревьюить, его проще прогнать через CI или откатить, если после релиза всплывёт регрессия.

Пример из практики

На одном проекте команда долго откладывала разбор крупного компонента, потому что он казался слишком сложным для нормального рефакторинга. В итоге вместо одной большой задачи работу разбили на несколько коротких шагов: сначала вынесли один кусок логики, потом разорвали лишнюю зависимость, затем покрыли отдельную часть тестами и только после этого начали дробить сам компонент. Такой подход занял больше календарного времени, но не сломал прод и не парализовал команду.

Типичные проблемы и что с ними делать

Вот конкретные ситуации, с которыми вы скорее всего столкнётесь на легаси-фронтенде:

Протечки между слоями. UI-компоненты напрямую ходят в API, знают формат ответов и сами преобразуют данные перед рендером. Бизнес-логика живёт вперемешку с разметкой, а слой данных зависит от представления. В такой системе сложно менять что-то изолированно: правка в одном месте быстро расползается по соседним слоям.

Что с этим делать:

  • зафиксировать границы между слоями;

  • вынести работу с API и преобразование данных в отдельные сервисы или адаптеры;

  • оставить компонентам только то, что относится к отображению и локальному поведению интерфейса.

Нет внятного управления состоянием. Часть данных живёт в локальном состоянии, часть — в сторе, часть — в URL, а часть вообще передаётся через props на несколько уровней вниз. Пока приложение маленькое, это ещё терпимо. Но со временем такая схема начинает ломаться: данные дублируются, зависимости становятся неочевидными, а любое изменение в потоке состояния даёт побочные эффекты.

Что с этим делать:

  • определить, где должен жить каждый тип состояния;

  • не пытаться мигрировать всё сразу;

  • сначала наводить порядок в новом коде и только потом постепенно разбирать старые участки.

Устаревший стек и старая инфраструктура сборки. Старые версии сборщика, старые версии фреймворка, залежавшиеся полифиллы, нестабильный dev-сервер, долгая сборка, сложная конфигурация окружений. Формально система работает, но сама среда разработки уже замедляет команду и повышает риск ошибок на поставке.

Что с этим делать:

  • начинать не с полной замены всего стека, а с самых дорогих точек;

  • сначала упростить и ускорить билд-инфраструктуру;

  • потом двигаться к обновлению зависимостей и фреймворка;

  • после каждого шага проверять совместимость и поведение системы в реальных сценариях.

Нет единых правил code style. В одном файле табы, в другом — пробелы. Где-то var, где-то const. Компоненты и модули организованы по разным принципам, а названия функций зависят только от привычек автора. Это не главная проблема проекта, но такой фон сильно усложняет чтение и ревью.

Что с этим делать:

  • вводить ESLint и Prettier;

  • применять правила сначала только к новому и изменённому коду;

  • не превращать наведение порядка в гигантский косметический MR.

Монолитные компоненты и модули. Один компонент рендерит интерфейс, хранит состояние, валидирует форму, ходит в API и заодно отправляет аналитику. Один модуль знает слишком много о соседних частях системы и давно перестал быть чем-то одним. Такой код трудно тестировать, переиспользовать и почти невозможно безопасно менять.

Что с этим делать:

  • сначала зафиксировать текущее поведение;

  • потом выносить по одной логической части за итерацию;

  • после каждого шага проверять, что система ведёт себя так же, как раньше;

  • не пытаться разрезать монолит одним большим MR.

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

Что с этим делать:

  • начинать не с переписывания, а с восстановления контекста;

  • смотреть историю изменений, старые MR, инциденты и связанные задачи;

  • фиксировать найденное в документации и тестах, чтобы модуль перестал быть «чёрным ящиком».

Важный момент — не нужно решать все эти проблемы одновременно. Легаси почти никогда не чинится одним архитектурным усилием. Нужно выбрать самый дорогой источник боли, зафиксировать поведение, аккуратно изменить, проверить результат и только потом переходить к следующему участку.

Заключение

Легаси — это не приговор и не стыд. Это результат живого продукта. Если ваш код стал легаси — значит, продукт достаточно долго жил, чтобы приносить пользу. Это хороший знак.

Задача инженера — не страдать и не мечтать о переписывании с нуля. А системно улучшать: понять что имеешь, зафиксировать правила, покрыть тестами, менять по частям.

Работа с легаси учит вещам, которые не получишь на проекте с чистого листа: понимать чужие решения, думать о тех, кто придёт после тебя, работать с ограничениями и находить элегантные решения в неидеальных условиях.

Хороший TechLead не избегает легаси — он делает так, чтобы текущий код не стал легаси завтра. А если уже стал — знает, как с этим работать.

P.S. Присоединяйтесь к нашему BI-сообществу в Telegram и будьте в курсе последних новостей Modus!