Задача: Перенести сайт на новый движок

Перенести сайт на новый движок

23.06.2026pivkarta.ru

Сайт очень старый, изначально был на MODX, потом переписывался на кустарных js-технологиях и еще на первой призме. В итоге он требует слишком много ресурсов (2+ гига).

docker stats --no-stream |grep pivkarta
fab6503884d8   docker-pivkartaru-mysql-1          0.47%     531.3MiB / 6.954GiB   7.46%     572MB / 12.1GB    26.7GB / 1.58GB   41
6657aa9c2c22   docker-pivkartaru-front-1          23.59%    405.4MiB / 6.954GiB   5.69%     45.7GB / 91.9GB   40.1GB / 31.6MB   23
202c9196d804   docker-pivkartaru-api-1            3.88%     226.4MiB / 6.954GiB   3.18%     815MB / 547MB     3.71GB / 1.27MB   23
782d45865722   docker-pivkartaru-prisma-1         2.70%     908.3MiB / 6.954GiB   12.75%    500MB / 646MB     8.43GB / 62.9MB   78

Для сравнения haih-agent даже с n8n есть не более гига (обычно метров 500).

Ворклоги

Структура legacy-БД: гипотеза «просто подключиться» пересматривается

После локального запуска посмотрели фактическую структуру существующей MySQL. Масштаб legacy оказался существенно больше, чем видно только по Docker-стеку: 250 таблиц, около 198 тыс. строк и ~82 MiB данных.

Из 250 таблиц примерно 150 имеют старый MODX-префикс fSdf12_. Это большой системный слой самого MODX и установленных за годы Extras: ACL/policies, contexts, manager/dashboard, media sources, package transport, lexicon, sessions, users/groups, billing/shop-модули, society, SDK/importer, search, monitoring, GeoIP и другие подсистемы. Значительная часть этих таблиц пустая или содержит буквально единицы записей, но при анализе legacy они всё равно выглядят как потенциально значимая часть системы.

Отдельно присутствует ещё один исторический слой модели: десятки Prisma-style relation-таблиц _...; многие из них сейчас пустые. Видны и следы последовательных миграций/дублирования сущностей: например старые и новые варианты Place, Beer, User, Photo, комментариев и связующих таблиц, backup-таблицы пользователей, views и разные storage engines (MyISAM + InnoDB). Это уже не одна предметная схема, а несколько поколений архитектуры, живущих в одной базе.

При этом реальное предметное ядро pivkarta.ru намного меньше. По данным видно несколько тысяч заведений (Place ~3.8k), около 1.6k сортов/позиций Beer, ~13k связей PlaceBeer, фотографии, пользователи и связи заведений с пивом/метро/пользователями; плюс контентные сущности вроде комментариев, тем, новостей/блогов и т.п. Какие именно из них нужны современной версии продукта, ещё предстоит определить по фактическим требованиям — количество записей само по себе не доказывает, что feature надо переносить.

Это наглядно подтверждает тезис из статьи «MODX платит огромную постоянную архитектурную цену за функциональность, которой большинство проектов почти не пользуется»: даже пустая таблица не означает нулевую архитектурную стоимость. Если subsystem существует, при работе с legacy приходится выяснять его модель, связи, runtime semantics и понимать, можно ли его безопасно отбросить. В этой базе эффект хорошо виден буквально физически: большая часть schema описывает возможности движка и исторические эксперименты, а не сегодняшнюю предметную модель pivkarta.ru.

Изменение направления эксперимента

В предыдущем worklog первым вариантом был минимально инвазивный путь: оставить существующую MySQL как persistence boundary и подключить новый API через Knex. После просмотра схемы этот вариант уже не выглядит очевидно оптимальным.

Теперь более перспективная гипотеза — создать новую чистую БД с минимальной предметной моделью и мигрировать в неё только доказанно необходимые данные. Старую БД использовать как источник данных и археологии требований, но не делать её постоянным фундаментом новой архитектуры.

Это добавляет работы сейчас: агенту придётся разобраться, какие из 250 таблиц действительно участвуют в продукте, какие являются техническими таблицами MODX/Prisma/Extras, какие представляют старые версии тех же данных, а какие features вообще больше не нужны. Но именно это и является хорошим brownfield-тестом HAIH: не переносить накопленную сложность только потому, что она уже существует; восстановить реальные требования и собрать под них минимальную новую модель.

Следующий логичный шаг — построить карту legacy tables → реальные capabilities → нужны/не нужны → новая модель → migration/verification, а затем выбрать первый вертикальный сценарий и перенести его вместе с данными end-to-end.

Связанный эксперимент: haih.site и задача /tasks/cmujk0ehv0m20qw0rs14y30bw. Текущая задача: /tasks/cmqqxckd3003zp50up0dmzbld.

Итог миграции pivkarta.ru: production и SEO/performance evidence

Задача завершена: новый pivkarta.ru выведен в production. Дальнейшие доработки будут оформляться отдельными задачами.

Масштаб brownfield-миграции

Новый сайт построен поверх развивавшегося HAIH runtime, но для реального legacy-проекта движок был существенно доработан до полноценной production-схемы: единый Express runtime, React Router SSR, GraphQL/Apollo/Pothos, Knex к существующей MySQL, Vite middleware в development, production static serving, Varnish, обработка legacy thumbnails через Sharp, ETag/GET/HEAD и корректное завершение серверных ресурсов.

Восстановлена основная публичная структура старого продукта: пиво, заведения, публикации, участники, комментарии, города, карта и контакты, включая исторические URL и связи между сущностями. Инвентаризация legacy-данных зафиксировала 1 636 записей пива, 3 797 заведений, 600 статей, 64 города, 3 296 комментариев и 26 548 публичных профилей.

Для совместимости был построен реестр из 73 760 адресов. Полный HTTP-обход зафиксирован как passed: 73760 без ошибок на проверенном состоянии. Дополнительно проверялись 11 579 ссылок контента и 19 660 URL ресурсов. Последующие изменения ветки покрывались целевыми тестами, поэтому старый полный crawl не следует считать проверкой каждого последнего коммита.

Измеримый production-результат

Основная работа текущей итерации с 28 сентября заняла по таймерам 18:01:02.

Lighthouse до/после:

МетрикаLegacyNew
Performance4993
Accessibility75100
Best Practices7496
SEO83100

Application runtime по docker stats:

  • legacy front: 588.4 MiB;
  • legacy api: 231.3 MiB;
  • legacy prisma: 953.2 MiB;
  • legacy application layer суммарно: ~1773 MiB (~1.73 GiB);
  • новый app: 178.2 MiB.

Таким образом, application RAM уменьшилась примерно в 10 раз (~90%), при одновременном росте Lighthouse Performance с 49 до 93 и достижении 100 по Accessibility и SEO. Особенно показательно, что один только Prisma 1 старого стека потреблял ~953 MiB — более чем в пять раз больше всего нового application runtime.

Главный инженерный вывод

Миграция подтвердила brownfield-гипотезу HAIH: для замены legacy-приложения необязательно воспроизводить его историческую архитектуру. Практический путь оказался таким:

legacy system → восстановление реальных requirements/capabilities → проверка данных и URL-контрактов → отбрасывание ненужного наследия → минимальная новая архитектура → verification → production evidence.

Первоначальная гипотеза о сохранении legacy-модели как основной архитектурной границы была скорректирована после исследования реальной БД и 250 таблиц. В результате сохранены ценные публичные данные, связи и web-контракт, но не воспроизведена накопленная инфраструктурная сложность старой системы.

Это отличает результат от обычного редизайна: получен production replacement реального многолетнего сайта с измеримой совместимостью, существенно меньшей стоимостью runtime и заметно лучшими web-метриками.

Граница этой задачи

В рамках этой задачи считаем миграцию выполненной. Отложенные write-сценарии (авторизация, создание/редактирование сущностей), дальнейший индивидуальный редизайн внутренних страниц, дополнительные production-проверки и прочие улучшения должны выполняться отдельными задачами, а не расширять текущую.

Новый эксперимент HAIH: brownfield на pivkarta.ru

Задача была поставлена давно как модернизация старого haih-agent, но с тех пор появился новый подход HAIH, который мы сейчас проверяем на практике в haih.site. Текущая работа над концепцией ведётся в задаче «Разработать и запустить сайт», а pivkarta.ru выглядит хорошим следующим подопытным для нового движка/подхода.

Если haih.site — greenfield-эксперимент «как быстро собрать достаточную архитектуру из реальных требований», то pivkarta.ru может стать brownfield-экспериментом: как быстро AI-агент способен разобрать существующий продукт, сохранить его реальные требования и постепенно заменить устаревшую архитектуру, не перетаскивая её исторический багаж в новую систему.

Важно не начинать с тотального rewrite и заранее выбранного «правильного стека». Существующую MySQL можно оставить как рабочий persistence boundary и для первого vertical slice подключиться к ней через Knex, поверх сделать новый typed API на Pothos/GraphQL, а на клиенте — codegen/Apollo + React. То есть переносить прежде всего требования и поведение, а не старую архитектуру.

Первый практический эксперимент: выбрать один реальный пользовательский сценарий и провести его end-to-end через новую систему (React → Apollo → GraphQL/Pothos → Knex → existing MySQL), зафиксировав время реализации, проблемы, способы проверки и ресурсы. Уже имеющийся baseline старой системы (~2 ГБ RAM по текущим замерам) позволит сравнивать результат не только по функциональности, но и по стоимости runtime.

Это должно стать вторым типом evidence для HAIH после haih.site: не только «как быстро AI собирает новое», но и как быстро он способен переосмыслить и вытеснить legacy по работающим вертикалям.

Legacy baseline запущен локально

Удалось локально поднять старый pivkarta.ru — Docker сильно помог сохранить воспроизводимость даже спустя годы. Одновременно стало хорошо видно, насколько большой исторический слой накопился в текущей архитектуре.

Некоторые характерные компоненты baseline:

  • build image: mhart/alpine-node:10 — Node.js 10;
  • Docker Compose 3.7;
  • prismagraphql/prisma:1.15.3 (Prisma 1) отдельным сервисом между приложением и БД;
  • MySQL 5.7;
  • отдельные контейнеры front и api из общего старого prisma-cms;
  • отдельный frontend pivkarta.ru-2;
  • phpMyAdmin;
  • собственный Caddy proxy;
  • MailHog;
  • отдельный rtcmultyconnection;
  • Coturn со STUN/TURN-инфраструктурой.

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

Для эксперимента HAIH это особенно полезно. Не будем механически переносить этот zoo на современные версии. Старый проект становится источником требований и baseline поведения, а не спецификацией новой архитектуры. Для каждого слоя нужно выяснять: какую реальную capability он обеспечивает сейчас, нужна ли она pivkarta.ru сегодня, и какое минимальное проверяемое решение удовлетворяет этому requirement.

Первый намеченный новый vertical slice остаётся простым: сохранить существующую MySQL как legacy persistence boundary и пройти React → Apollo/codegen → GraphQL/Pothos → Knex → existing MySQL. Это позволит начать вытеснять старую систему работающими вертикалями, не начиная с рискованной миграции данных и не перенося Prisma 1 только потому, что он присутствует в legacy.

Связанный HAIH-эксперимент: haih.site и задача «Разработать и запустить сайт». Текущая задача pivkarta.ru: /tasks/cmqqxckd3003zp50up0dmzbld.