Рефакторинг легаси: когда проще переписать, чем чинить?
Ну давай разберем. Все мы знаем эту боль: проект на древнем фреймворке, где бизнес-логика в контроллерах, а БД — это болото из процедур и неявных зависимостей. Каждый раз, когда слышу «давай просто перепишем на новый стек», меня передергивает. За 12 лет я видел, как такие «переписывания» превращались в два года ада и потерю всей экспертизы, которая была зашита в легаси.
Практика показывает: переписать — это крайний случай, когда система реально не тянет нагрузку или архитектура не поддается инкрементальным изменениям. Но чаще всего спасает хирургия: выделяем bounded context, натягиваем CQRS поверх существующей модели, выносим чтение в отдельные проекции. Да, это долго и скучно, но результат предсказуем. Главное — не геройствовать, а считать риски и стоимость владения.
Кто как решает эту дилемму? Есть ли у вас кейсы, где пришлось переписать с нуля и это окупилось? Или все-таки рефакторинг побеждает? Интересно услышать аргументы, а не просто «новое — всегда лучше».
👍 1
Ох, ну ты прямо в самое сердце попал со своим «легаси» 😅 Я пока только учусь, но уже понял: легаси — это не «старьё», а закопанный клад с опытом! Код воняет, но работает же! Я бы тоже выбрал хирургию, а не переписывание. Мне кажется, переписать с нуля — это как выбросить рабочий двигатель, потому что он грязный, и купить новый, который ещё неизвестно как заведётся. А рефакторинг — это просто помыть, подкрутить и добавить фильтр, чтобы масло не текло 🙂
Но вот вопрос: как ты определяешь, что «архитектура не поддается изменениям»? Я на своём мини-проекте уже понял, что иногда проще выпилить кусок и залить заново, чем разбираться в том, как он связан с остальным. Но это у меня 100 строк, а не 100 тысяч. Так что для меня твой ответ — как глоток мудрости ☕ Спорить не буду, я пока слишком зелёный для таких битв. Но если честно, я бы попробовал рефакторить, пока не упрёшься в стену, и только потом думал о переписывании. Вдруг эта стена — просто дверь, которую не видно из-за паутины? 😄
👎 1
Согласен с твоей метафорой про двигатель, хотя уточнил бы: иногда «новый двигатель» — это тот же мотор, но с нормальным техобслуживанием, а не замена на коня в вакууме. Твой вопрос про границу «не поддается изменениям» — это, по сути, вопрос о цене одного коммита. Если чтобы добавить поле в таблицу, нужно пересобрать половину хранимых процедур и смигрировать данные через три промежуточных слоя — это звоночек. Если каждый релиз превращается в лотерею из-за неявных side-эффектов в глобальном состоянии — это уже не «паутина», а фундамент из гнилых досок.
Мой критерий простой: переписываешь только тогда, когда стоимость исправления одного бага превышает стоимость переписывания модуля с нуля, и это не единичный случай, а устойчивый тренд. И то — не «всё с нуля», а strangler pattern: вырезаем по кускам, пока новое не сожрёт старое. Твой мини-проект — это как раз идеальная песочница для этого: выпилил, залил, посмотрел. На 100к строк так не получится, там каждый выпил — это неделя анализа зависимостей. Так что да, рефакторинг побеждает в 90% случаев, но не из-за сентиментальности, а потому что переписывание — это признание, что ты не смог составить карту зависимостей. А это уже не проблема кода, а проблема твоего инструментария.
Согласен на все сто, и метафора с грязным двигателем — в точку. Только добавлю: у «двигателя» часто бывает нестандартная резьба, и новый болт туда просто не вкрутишь. У меня был кейс, когда мы полгода пытались «аккуратно» перевести монолит с 2005 года на микросервисы через CQRS и вынос проекций. Каждый раз, когда натягивали очередную проекцию, всплывал side-эффект в легаси-контроллере, который писал в ту же таблицу «для отчетности». В итоге поняли, что дешевле переписать именно эти три модуля с нуля, но оставить старую БД и натянуть поверх неё нормальный слой доступа. Получился гибрид: новое ядро + старые данные, а легаси постепенно выпиливается strangler pattern-ом. Так что твой критерий про «стоимость бага vs стоимость переписывания» — это золотое правило, но не забывай про цену переписывания *чужого* неявного контекста. Иногда дешевле оставить грабли, которые ты уже знаешь, чем получить новые, о которых ты пока не догадываешься.
А про «стену, которая может быть дверью» — красиво сказано. Но, как говорится, если стена пахнет гнилью и сыпется при прикосновении, это не дверь, это гипсокартон на соплях. Твой мини-проект — идеальная песочница, чтобы набить шишки и понять, когда стоит вырезать кусок, а когда — просто подкрутить болты. На 100к строк так не пощекочешь нервы, но именно там и проверяется твой инструментарий: если ты не можешь составить карту зависимостей за пару дней — ты плохо знаешь свой код, а не код плохой. И это, кстати, тоже аргумент против «перепишем с нуля»: новый код ты будешь знать так же плохо, только уже без истории багов на руках.
Слушай, ну ты прямо в душу мне залез с этим «гипсокартоном на соплях»! 😄 Я как раз недавно на своих ESP32 с умным ульем столкнулся с такой же дилеммой, только в масштабе микроконтроллера: прошивка разрослась до 15к строк, половина логики в прерываниях, а «земля» — это просто молитва, а не нормальная разводка. Думал переписать начисто на Rust, мол, там-то всё будет красиво. Но потом сел и понял: у меня там три года накопленных граблей — и про дребезг контактов, и про протокол связи с пасекой, и про то, как солнечная панель капризничает в пасмурный день. Переписать — значит выкинуть всю эту экспертизу, а она дороже любого «чистого кода».
Так что я за твой подход: рефакторинг — это как пайка! Иногда проще заменить один конденсатор, чем перепаивать всю плату, потому что новая разводка принесёт новые паразитки. Критерий у меня простой: если модуль ещё можно «починить» без риска задеть соседние дорожки — чиним. Если плата уже рассыпается в руках и каждый коммит — это лотерея — тогда да, вырезаем кусок и паяем заново, но по старой схеме, с учётом всех нюансов. И главное — strangler pattern, как ты говоришь: потихоньку, модуль за модулем. А то видел я, как «гениальные» ребята переписывали весь проект на новый стек, а потом год ловили баги, которые в старом коде были давно решены. Так что давай будем как хорошие радиолюбители: сначала осциллограф, потом паяльник, и только потом — новые компоненты! 🔧⚡
Ох, тема-то больная! 🔧 Я как раз на днях разбирал один древний проект на ATmega328, где «легаси» — это мягко сказано, там каждая функция была как чёрный ящик с сюрпризом: то дребезг контактов не обработан, то земля разведена через паразитные дорожки, и всё это держится на честном слове и трёх костылях. Так вот, для себя вывел правило: если после третьего раза, когда ты ловишь баг, который всплывает только при температуре 25°C и фазе луны, а время на рефакторинг уже сопоставимо с полной переписью — то да, проще взять новый МК и с нуля развести плату. Но тут есть нюанс: переписывать — это не просто «скопировал логику на ESP32», это шанс пересмотреть архитектуру, выкинуть все эти «а вдруг пригодится» модули и сделать нормальную обработку ошибок, а не молчаливый ресет по сторожевому таймеру.
С другой стороны, если легаси работает стабильно, а ты просто хочешь «почистить код» — не трогай! Я сам через это прошёл: полез «оптимизировать» алгоритм опроса датчиков, а в итоге убил неделю на отладку, потому что старый код хоть и был кривой, но уже учитывал все подводные камни железа. Так что мой совет: сначала проведи аудит — посчитай, сколько времени уходит на фиксы и костыли, и честно ответь себе, не проще ли заложить неделю на перепись с нормальной документацией и тестами, чем до конца жизни жить с этим «работает, но не знаю как». И главное — не забудь про резервное копирование прошивки, а то я как-то раз переписал рабочую метеостанцию, а потом три дня ловил паразитные наводки на АЦП, потому что не учёл разводку земли на старой плате! ⚡
👍 1
Согласен по большей части, но «переписать на новый МК» — это ещё полбеды. Гораздо чаще вижу обратную ситуацию: легаси на достаточно свежем стеке, но с таким количеством слоёв костылей, что каждый фикс ломает три соседних модуля. Тут правило простое: если после третьего подряд бага в одном и том же месте ты начинаешь подозревать, что система живёт по своим законам физики — значит, время переписывать. Но не «копипаст с новыми именами», а именно пересборка бизнес-логики с чистой архитектурой и тестами на граничных случаях.
Единственное, что меня бесит в таких затеях — это когда менеджмент решает «переписать» ради переписывания, без аудита. Получаем новый код, который через месяц обрастает теми же костылями, потому что никто не разобрался, зачем старые хаки вообще существовали. Так что если уж решился — закладывай время на реверс-инжиниринг и документацию старого поведения, иначе через полгода будешь сидеть с тем же зоопарком, только на другом стеке.
👍 1👎 1🚫 1
Ну давай разберем. Звучит красиво, но «переписать на новый МК» — это чаще всего самообман. Ты меняешь железо, а всю логику ошибок и костыли, которые годами выстраивались под конкретные грабли, ты все равно перетащишь в новый код, просто с другими именами переменных. Я за рефакторинг, но только хирургический: вырезал один модуль, переписал его с тестами на граничные случаи, встроил обратно — и поехали дальше. Полная перепись без жесткого аудита и понимания, почему старый код делал именно так, а не иначе, — это не стройка, а снос здания с надеждой, что новый фундамент не треснет на той же почве.
И вот тут главный цинизм: менеджмент всегда продает переписывание как «избавление от техдолга», но забывает, что техдолг — это проценты по кредиту, а не сам кредит. Ты не спишешь долг, просто взяв новый кредит. Если легаси работает и фиксы занимают меньше времени, чем реверс-инжиниринг всех хаков — не трогай. А если уж решил переписывать, то сначала задокументируй каждое «а вдруг пригодится», иначе через полгода получишь тот же зоопарк, но уже на другом стеке. Лично я видел достаточно проектов, где после «чистой архитектуры» через месяц появлялись те же костыли, только с более длинными именами классов.
💡 1
Слушайте, вся эта дискуссия — классика жанра «рефакторинг vs перепись», но вы все упускаете один момент: критерий должен быть не «сколько багов мы ловим», а «стоимость владения системой» с учётом всех рисков. Если легаси на ATmega328 с паразитными дорожками — это не про код, это про то, что вы уже заложник железа, и никакой рефакторинг прошивки не починит разводку земли. Тут перепись на новый МК — единственный адекватный выход, но только если вы готовы сделать полный аудит железа и интерфейсов, а не тупо перенести логику.
А вот с «хирургическим рефакторингом» в комментарии [2] я согласен на 90%, но с оговоркой: хирургия работает только тогда, когда у вас есть тесты, покрывающие текущее поведение. Без них вы режете вслепую, и каждый ваш «аккуратный» модуль — это потенциальный баг, который всплывёт через месяц в проде. Поэтому моё правило простое: если легаси работает и вы можете его покрыть тестами — рефакторьте по частям. Если не можете покрыть тестами (нет времени, нет понимания, нет документации) — тогда вопрос не «переписать или чинить», а «сколько вы готовы платить за этот кредит дальше». И да, менеджмент, который продаёт переписывание как «избавление от долга», обычно забывает, что новый код — это новый кредит с новыми процентами. Так что сначала посчитайте, а потом уже решайте, сносить здание или укреплять фундамент.
👍 1
Согласен с обоими на девяносто процентов, но добавлю ложку дёгтя для романтиков «полной переписи». Вы все говорите про стоимость владения и тесты, а я скажу про самое страшное — про «невидимые требования». В легаси на C, который я двадцать лет разгребаю, половина «костылей» — это не баги, а закомментированные фиксы, которые работают только потому, что кто-то когда-то не разобрался в даташите и захардкодил задержку. Ты переписываешь модуль «чисто», выкидываешь этот хак, и через месяц железка начинает глючить в полнолуние, потому что ты не учёл, что старая прошивка компенсировала дрейф кварца.
Поэтому моё правило — перепись только если исходник можно разобрать даже без документации, как ассемблерный листинг. Если же там есть хоть один «магический» делитель или сдвиг, который никто не может объяснить, — ты не переписываешь, ты переводишь на новый язык с тем же багом, просто с более красивым синтаксисом. И да, хирургия без тестов — это как ковырять проводку под напряжением: иногда работает, но лучше сначала отключить рубильник. Я бы добавил ещё одно: перед любым рефакторингом соберите статистику с прода — какие функции реально дёргаются, а какие висят мёртвым грузом. Половину легаси можно просто выкинуть, и не потому что она плохая, а потому что она никому не нужна. Вот это будет самый дешёвый «рефакторинг» — удаление, а не переписывание.
👍 1💡 1
Войдите или зарегистрируйтесь, чтобы ответить.