Безопасное исправление сломанных миграций базы данных в AI‑сгенерированных приложениях
Безопасно восстановите сломанные миграции: просмотрите историю миграций, найдите дрейф окружений, создайте чистый baseline и защитите данные при раскатке.

Почему в AI-сгенерированных приложениях ломаются миграции
База данных меняется управляемым скриптом — миграцией. Представьте её как «шаг рецепта» для таблиц: добавить колонку, переименовать поле, добавить индекс. Миграции должны выполняться в порядке, чтобы везде был один и тот же набор таблиц и колонок.
AI-сгенерированные приложения часто рвут эту цепочку, потому что код создаётся быстро и без привычек, которые люди используют для аккуратной истории. Инструменты могут автоматически генерировать миграции, пересоздавать их после запроса или копировать паттерны из другого проекта. В результате появляются файлы, которые выглядят валидными, но конфликтуют друг с другом.
Распространённые причины:
- Две миграции пытаются изменить одну и ту же таблицу по-разному
- Файл миграции был отредактирован после того, как его уже применили где-то
- Дублирование имён миграций или меток времени, которые сортируются по-разному на машинах
- Миграции сгенерированы против локальной базы, которая не совпадает со staging или prod
«У меня работает на машине» случается, когда на ноутбуке есть дополнительные ручные правки, пропущенные шаги или другой порядок миграций. Код приложения совпадает с локальной схемой, поэтому всё кажется в порядке, пока в проде не запустят реальную цепочку миграций и не столкнутся с отсутствующей таблицей, дублирующейся колонкой или внешним ключом, который не создаётся.
Эта проблема обычно проявляется в самый неподходящий момент: при первом деплое, когда новый участник команды настраивает проект с нуля, или когда CI собирает чистую базу и выполняет миграции в строгом порядке.
Если вы получили прототип, сгенерированный инструментами вроде Lovable, Bolt, v0, Cursor или Replit, это одна из первых вещей, которую FixMyMess проверяет при диагностике, потому что проблемы с миграциями часто прячутся до дня запуска.
Симптомы и риски — что смотреть прежде чем трогать что-то
Сломанные миграции обычно проявляются при деплое, но корни проблемы могут быть раньше. AI-сгенерированные приложения быстро создают миграции, затем модели и схема меняются так, что это не соответствует тому, что уже выполнялось в других окружениях.
Очевидные признаки трудно пропустить: деплой падает с «table not found» или «column already exists», страницы крашатся, потому что запрос ожидает поле, которого нет, или вы видите отсутствующие таблицы, которые код предполагает. Ещё один типичный случай — дублирующиеся колонки (например, user_id и userid) или индексы, созданные дважды под разными именами.
Более опасные признаки тихие. Приложение может работать, но локальная, staging и production больше не совпадают. В одном окружении может быть другой тип колонки, значение по умолчанию или ограничение (например, NOT NULL), которого нет в других. Это создаёт баги «работает у меня»: фича проходит тесты локально, но ломается в проде, или — ещё хуже — пишет неверные данные, которые проявятся позже.
Перед тем как пытаться исправлять миграции, обратите внимание на риски:
- Потеря данных при запуске деструктивных миграций на неправильной схеме
- Длительный даунтайм, если миграция блокирует большие таблицы или запускает медленные бэктиллы
- Частичные откаты, когда код откатывается, но база не может корректно вернуть изменения
- Скрытые баги в приложении, когда код предполагает схему, которая есть только в одном окружении
Хорошее правило: если вы не можете чётко ответить на вопрос «какие миграции запускались в проде и в каком порядке?», остановитесь. Не запускайте новые миграции вслепую, чтобы «проверить». Это обычно расширяет разрыв между окружениями и усложняет восстановление. Если вы унаследовали AI-сгенерированный прототип и ошибки накапливаются, команды вроде FixMyMess часто начинают с аудита в режиме «только чтение», чтобы понять, что безопасно запускать, а что требует контролируемой перестройки.
Начните с быстрой инвентаризации: что есть и где
Прежде чем исправлять миграции, получите ясную картину того, что у вас есть. Большинство катастроф с миграциями происходят потому, что люди предполагают, что локал, staging и прод одинаковы, когда это не так.
Сначала перечислите все существующие окружения: обычно это локал (ваш ноутбук), staging (тестовый сервер) и production (реальные пользователи). Если есть preview-приложения, старые staging-базы или второй регион прод, включите их тоже.
Далее снимите текущую схему в каждом окружении такой, какая она есть сейчас, а не такой, какой вы хотите её видеть. Используйте то, что поддерживает ваш стек: дамп схемы, команду интроспекции или экспорт только для чтения таблиц, колонок, индексов и ограничений. Сохраните эти выводы с отметками времени, чтобы потом сравнить.
Затем соберите исходники миграций и состояние миграций:
- Папку миграций из репозитория (включая старые файлы)
- Таблицу отслеживания миграций в базе (например, таблицу, которая фиксирует, какие миграции применились)
- Любые ручные заметки или скрипты, которыми меняли базу вне миграций
- Точную команду, которая падает (и кто её выполнял)
- Полный текст ошибки и когда она появляется (при свежей настройке или после пулла)
Наконец, опишите историю ошибки простыми словами. Пример: "Локал работает, staging падает на миграции 0042, в проде вручную добавлена дополнительная колонка." Такой короткий рассказ убирает догадки и ускоряет следующие шаги, особенно если вы привлекаете помощь вроде FixMyMess для аудита.
Аудит истории миграций на предмет конфликтов и правок
Прежде чем исправлять миграции, выясните, насколько можно доверять их цепочке. AI-сгенерированные приложения часто создают миграции быстро, потом перезаписывают файлы или генерируют новые, не учитывая то, что уже выполнилось в проде.
Начните с просмотра списка миграций от старых к новым и проверьте, чиcт ли порядок. Пропуски не всегда фатальны, но они намекают, что файлы были переименованы, удалены или пересозданы. Также следите за дублирующимися ID или метками времени, которые могут привести к тому, что две разные миграции претендуют на одно «место» в истории.
Далее ищите правки старых миграций. Если миграция была применена где-то (staging или prod), а затем её файл изменили в репо, у вас теперь две истины: база содержит одну версию, репозиторий — другую. Быстрая проверка — сравнить даты изменения файлов, историю коммитов или просто просмотреть комментарии вроде «fix», добавленные в старой миграции.
Мёрджи веток — ещё один частый источник конфликтов. Две ветки могут каждая добавить «следующую миграцию», и при мёрдже вы получите конкурирующие миграции, которые обе предполагают себя следующими. Это часто видно как два новых файла миграций, созданных в одно и то же время, но с разным содержимым.
Наконец, предположите, что в каком‑то окружении были ручные изменения. Если кто‑то добавил индекс в проде, хотфиксил тип колонки или запускал SQL-сниппет вручную, миграции этого не отразят.
Короткий чеклист для аудита:
- Убедиться, что ID миграций уникальны и строго возрастают
- Пометить старые файлы миграций, которые редактировались после создания
- Найти «параллельные» миграции, созданные в разных ветках
- Отметить изменения схемы, которые есть в БД, но нет в миграциях
- Записать, какие окружения, вероятно, отличаются (local, staging, prod)
Если всё кажется запутанным — это нормально для прототипов из Cursor или Replit. Команды вроде FixMyMess часто начинают с такого аудита перед решением ремонтировать или перестраивать цепочку.
Обнаружение дрейфа между окружениями без догадок
Дрейф миграций — это когда схемы в разных окружениях (local, staging, prod) отличаются, хотя должны совпадать. В AI-сгенерированных приложениях это часто происходит после «быстрых правок» прямо в базе или после редактирования и повторного запуска миграций.
Начните с сравнения самой схемы, а не файлов миграций. Нужен ясный письменный дифф: таблицы, колонки, типы данных, индексы и ограничения (особенно внешние ключи и уникальные ограничения). Именно эти различия объяснят, почему эндпоинт работает локально, но ломается в проде.
Практичный быстрый метод — экспортировать снимок схемы из каждого окружения (дамп только схемы или вывод интроспекции ORM) и сравнить их бок о бок. При просмотре диффа фокусируйтесь на объектах, которые меняют поведение:
- Отсутствующие или лишние колонки, к которым обращается код
- Различия в nullability или значениях по умолчанию
- Отсутствующие уникальные ограничения (разрешаются дубликаты)
- Отсутствующие внешние ключи (или разные правила каскада)
- Различия в индексах, которые в реальном трафике вызывают таймауты
Отделите ожидаемые различия от реального дрейфа. Тестовые сиды, админ‑пользователи и dev‑флаги могут отличаться и это не проблема. Различия в схеме редко бывают ожидаемыми. Если схема отличается — предполагайте, что рано или поздно что‑то сломается.
Затем выберите источник истины. Это решение, а не догадка. Обычно выигрывает production, если он обслуживает реальных пользователей, но иногда prod сам по себе грязный (например, туда добавляли хотфиксы во время паники). При исправлении миграций выберите окружение с самой корректной и полной схемой и задокументируйте, почему вы его выбрали.
Пример: прототип в Replit работает локально, но регистрация пользователей падает в проде. Дифф показывает, что в проде отсутствует уникальный индекс на users.email, поэтому были созданы дубликаты аккаунтов и аутентификация стала неконсистентной. Этот один дрейф объясняет случайные баги логина и указывает, что чинить в первую очередь.
Пошагово: безопасный план восстановления
Сломанные миграции кажутся срочной задачей, но поспешные действия превращают проблему схемы в потерю данных. Самый безопасный план — банален: приостановить изменения, сделать проверенную точку восстановления и прогнать исправление в тестовой среде прежде чем трогать прод.
Сначала заморозьте деплои. Отключите автодеплой, фоновые джобы, которые применяют миграции при старте, и любые «просто запушить» релизы. Если приложение публичное, назначьте окно обслуживания и объясните команде, что под заморозкой: схема БД, модели ORM и любой код, пишущий в затронутые таблицы.
Далее сделайте бэкап, который вы действительно тестировали. Не довольствуйтесь логом «backup succeeded». Восстановите небольшой бэкап в отдельную базу, выполните простой запрос, чтобы подтвердить наличие ключевых таблиц и недавних строк, и проверьте, что приложение подключается. Если восстановление не проходит — у вас нет надёжного бэкапа.
Теперь воспроизведите ошибку в безопасном месте, используя копию продовых данных (или обезличенный снимок). Запустите команду миграций точно так же, как в проде. Зафиксируйте первую ошибку и имя файла миграции — обычно первая ошибка даёт реальную подсказку.
Далее выберите подход для восстановления. Если проблема небольшая (отсутствующая колонка, отредактированная миграция или частичное применение), согласуйте: напишите корректирующую миграцию, которая переводит схему из текущего состояния в ожидаемое. Если история перемешана (конфликты файлов, дублирующиеся ID, дрейф по окружениям), перестраивайте: создайте чистую базовую точку от текущей схемы и перезапустите цепочку миграций.
Перед релизом проведите полный прогон: начните с пустой базы, примените все миграции, заполните минимальные сиды и выполните smoke‑тест. Это момент, когда ловят «работает на моём ноуте» дрейф.
Если вы чините миграции в AI‑сгенерированном коде (Lovable, Bolt, v0, Cursor, Replit), команды вроде FixMyMess часто начинают с этой репетиции, чтобы изменение в проде было предсказуемым, а не героическим.
Перестроить чистую цепочку миграций (baseline, squash и reset)
Чистая цепочка миграций делает деплои снова скучными. Если нужно исправить сломанные миграции, не начинайте с удаления файлов. Сначала решите, стабилизируете ли вы существующее состояние или создаёте новую, хорошо документированную базовую точку.
Baseline: когда её создавать (и когда не стоит)
Создавайте baseline‑миграцию, когда схема в production корректна, но история миграций запутана, отредактирована или нарушена. Цель — зафиксировать текущую схему как новую стартовую точку.
Не делайте baseline, если прод ненадёжен (есть неизвестные ручные правки, отсутствующие таблицы или проблемы с данными). В таком случае сначала нужно восстановить или исправить схему, затем базелайнить.
Squash и reset: как сохранить историю в безопасности
Если миграции уже применены в проде, избегайте переписывания истории на месте. Вместо этого отметьте старую цепочку как «legacy» и установите ясную точку отсечки.
Безопасный паттерн:
- Заморозьте текущее состояние: сделайте бэкап и зафиксируйте версию схемы в каждом окружении
- Сгенерируйте одну «squash»‑миграцию, точно соответствующую текущей схеме
- Обозначьте её как новый baseline (например, заметкой в репо и в документации по деплою)
- Сохраните старые миграции в отдельной папке или явно помеченном разделе, но перестаньте их применять
- Для новых окружений запускайте baseline + новые миграции
Представьте прототип, где локал имеет 42 миграции, staging — 39, а в проде есть вручную добавленные таблицы. Банальная сжатая миграция с локали не совпадёт с продом. Правильный ход — делать baseline от схемы, выбранной как источник истины (обычно prod), затем применять дальнейшие изменения как новые миграции.
Документируйте baseline простыми словами: дату, исходное окружение, точный снимок схемы и правило для новых установок. Команды вроде FixMyMess часто добавляют такую документацию в процесс ремедиации AI‑сгенерированных приложений, потому что будущие деплои от неё зависят.
Протестируйте и разверните исправление, не рискуя реальными данными
Относитесь к исправлению миграций как к релизу, а не к быстрому патчу. Самое безопасное место для проверки — staging, максимально похожий на прод: тот же движок БД и версия, похожие расширения, те же переменные окружения и копия продовой схемы (и по возможности небольшой анонимизированный срез данных).
Перед тестированием сделайте свежий бэкап, который можно восстановить. Планы отката часто терпят неудачу, потому что полагаются на down‑миграции, которые никто не катался на реальных данных. На практике реалистичный откат — восстановление снимка.
Убедитесь, что миграции повторяемы
Миграция, которая «сработала один раз», всё ещё опасна. Прогоните всю цепочку на staging, затем сбросьте staging и прогоните снова. Результат должен быть одинаковым оба раза.
Следите за тревожными признаками: использование текущих временных меток как значений по умолчанию, недетерминированные бэктиллы данных или миграции, зависящие от текущего содержания таблиц.
Валидируйте приложение, а не только схему
После миграций запустите приложение и протестируйте ключевые сценарии: регистрация/логин, создание и редактирование основных записей, поиск/фильтрация и административные экраны. Затем запустите фоновые задачи и шедулеры — они часто обращаются к колонкам, которые были переименованы или сделаны NOT NULL.
Держите rollout маленьким и контролируемым:
- Назначьте короткое окно обслуживания (даже если надеетесь на нулевой даунтайм)
- Сначала применяйте миграции, затем деплойте код, который ожидает новую схему
- Мониторьте ошибки и медленные запросы сразу после релиза
- Будьте готовы восстановить базу, если что‑то пойдёт не так
Если приложение AI‑сгенерировано, дополнительно проверьте скрытые предположения о схеме в коде. FixMyMess часто видит прототипы, где UI работает локально, но прод падает из‑за отсутствующих миграций, отредактированных файлов миграций или секретов, меняющих поведение между окружениями.
Распространённые ошибки, которые усугубляют проблемы с миграциями
Самый быстрый путь превратить миграционную путаницу в реальный инцидент — «залатать пока работает», не понимая, что уже применилось и где. Когда вы чините миграции, цель — не просто запустить приложение. Цель — вернуть всем окружениям один объяснимый и согласованный путь истории.
Одна частая ловушка — редактирование старой миграции, которая уже была применена в проде. Ваш код теперь говорит одно, а в проде уже сделали другое. Коллега запускает чистую настройку, staging запускает отличную цепочку, и вы получаете две временные линии, которые уже не совпадут.
Ещё одна ошибка — «фикс дрейфа» ручными правками в базе. Например, кто‑то добавляет отсутствующую колонку прямо в прод, чтобы прекратить ошибки, но файлы миграций всё ещё не совпадают. Приложение работает день, затем следующий деплой пытается снова добавить колонку, падает и блокирует дальнейшие миграции.
Типичные ошибки в AI‑сгенерированных приложениях:
- Рассматривать down‑миграции как страховку, хотя откат никогда не тестировали на реальных данных
- Пропускать ограничения и индексы, потому что «приложение работает» без них, а потом страдать от медленных запросов и неконсистентных данных
- Удалять строки из таблицы миграций, чтобы подавить ошибки
- Переименовывать таблицы или колонки вне миграций, а потом удивляться дрейфу моделей ORM
- Смешивать изменения схемы и бэктиллы данных в одной миграции, что усложняет восстановление
Быстрые починки скрывают симптомы, но сохраняют дрейф. Вы можете пройти через немедленную ошибку, но следующее окружение (или следующий разработчик) сломается по‑новому.
Если вы унаследовали прототип из Lovable, Bolt или Replit, предполагайте, что миграции были сгенерированы в спешке. Команды вроде FixMyMess обычно сначала подтверждают, что применилось в проде, а затем перестраивают чистый путь вперёд, не переписывая историю на месте.
Быстрый чеклист перед нажатием deploy
Если миграции сгенерировал AI, предполагайте наличие пробелов. Спокойный чеклист помогает избежать самой болезненной ошибки: обнаружить проблему в проде.
Прежде чем трогать прод
Сделайте бэкап и подтвердите, что восстановление работает. «Бэкап сделан» не равно «восстановление протестировано». Восстановите в новую базу, откройте приложение и убедитесь, что ключевые таблицы и недавние записи на месте.
Далее выполните дифф схемы по всем важным окружениям (local, staging, production). Запишите отличия заранее. Если в проде есть лишняя колонка или отсутствует индекс — зафиксируйте это.
Выберите единственный источник истины и запишите это. Решите, побеждает ли production схема, конкретная ветка миграций или известное хорошее окружение. Запишите выбор в заметках, чтобы никто не «починил» не ту сторону в ходе деплоя.
Доказать безопасность цепочки миграций
Запустите полную последовательность миграций на копии, похожей на прод. Это значит копия данных прод (или санитизированный набор с той же формой), та же версия движка БД и та же команда миграций, что вы будете использовать при релизе. Следите за долгими блокировками, падающими ограничениями и отличиями «работает на моей машине».
Перед деплоем убедитесь, что в плане есть шаги валидации и отката:
- Валидация: базовый логин, несколько ключевых чтений/записей и быстрая проверка критичных таблиц/количества строк
- Откат: чёткие шаги по возврату версии приложения и восстановлению базы
Пример: если staging «опережает» прод, потому что AI‑инструмент создал лишнюю локальную миграцию, приостановитесь и согласуйте различие прежде чем деплоить. Отправлять с таким дрейфом обычно значит провалить деплой или получить молча возникающие несоответствия данных.
Если вы унаследовали грязный AI‑сгенерированный код, FixMyMess часто начинает с бесплатного аудита, который подтверждает источник истины, картирует дрейф и проверяет план восстановления до любого вмешательства в прод.
Пример: восстановление прототипа, где локал и прод разошлись
Основатель разрабатывает AI‑сгенерированный прототип, который хорошо работает на его ноутбуке. При деплое в прод миграция 012 падает с ошибкой «column already exists». Приложение не стартует, и каждый последующий деплой повторяет ошибку.
Быстрый просмотр показывает, что дело не в одном файле. Две ветки внесли похожие изменения в одну таблицу: обе добавили поле status в users, но одна сделала его nullable, а другая — с дефолтом. Локально разработчик применил одну ветку миграций, в проде ранее применили другую. Теперь история миграций и реальная схема расходятся.
Самый безопасный путь — считать прод источником истины, сделать baseline от схемы прод и двигаться только вперёд.
Пошагово на практике:
- Заморозьте деплои и сделайте полный бэкап (проверьте восстановление)
- Посмотрите схему прод и таблицу миграций, чтобы увидеть, что реально запускалось
- Создайте baseline‑миграцию, которая соответствует прод как есть (без изменений, просто выравнивание)
- Напишите новую чистую последовательность миграций, которая применит недостающие изменения небольшими шагами
Потом валидируйте приложение реальными сценариями, а не только фактом «миграции прошли». В примере вы протестируете логин (сессии, сброс пароля), биллинг (вебхуки, инвойсы) и дашборды, зависящие от изменённой таблицы.
Это типичный паттерн при исправлении миграций в AI‑сгенерированных приложениях: не пытайтесь принудительно подтянуть прод к запутанной локальной истории. Сделайте историю соответствующей проду, затем добавляйте безопасные миграции только вперёд. Если вы не уверены, какая схема правильная, FixMyMess может сначала сделать аудит репо и следа миграций, чтобы вы не учились на ошибках в проде.
Следующие шаги: держать миграции стабильными и знать, когда просить помощи
После приведения миграций в порядок цель — скучная согласованность. Решите, нужен ли вам небольшой фикс (одна плохая миграция, файл пропал, неправильный порядок) или полный ребилд (история редактировалась, окружения расходятся и понятие «latest» ненадёжно). Если после чистого прогона на пустой базе вы всё ещё видите сюрпризы — склоняйтесь к перестройке.
Простой способ предотвратить повторение — написать короткий «контракт схемы», которого будут придерживаться все. Положите его в репо как простую заметку. Он не должен быть сложным, но должен быть понятным:
- Всегда создавать новые миграции, никогда не редактировать старые после их релиза
- Каждая миграция должна быть обратимой (или объяснять, почему нет)
- За финальный мёрдж, когда несколько веток трогают БД, отвечает один человек
- Изменения продовой схемы делаются только через миграции, а не вручную в консоли
- Чистая установка (пустая БД → latest) должна проходить в CI перед деплоем
Если приложение сгенерировано AI, ожидайте больше скрытого дрейфа, чем обычно. Код может создавать таблицы при старте, сидить данные в неожиданных местах или тайно расходиться между SQLite локально и Postgres в проде. Отнеситесь к базе как к первоклассному компоненту приложения, а не к после‑думью.
Знайте, когда привлекать второе мнение. Если у вас есть реальные пользовательские данные, несколько конфликтующих окружений или вы не уверены, не удалит ли "фикс" колонки или ограничения, остановитесь. FixMyMess может провести бесплатный аудит кода, указать проблемы миграций и предложить безопасные исправления до любых обязательств — это часто быстрее, чем гадать под давлением срока.