Что находится в файле миграции
Файл миграции описывает переход от прежнего состояния базы к следующему. Для нашего телефона содержательная часть может быть такой:
ALTER TABLE requests ADD COLUMN phone TEXT;
SQL — Structured Query Language, язык структурированных запросов. Через него программа обращается к такой базе. ALTER TABLE означает «измени таблицу», ADD COLUMN — «добавь столбец». В результате у requests появляется текстовое поле phone.
Это пример для учебной таблицы, не команда для вставки в рабочую базу. Название таблицы и правила телефона в вашем сервисе могут отличаться. В следующем опыте эта же строка запускается внутри отдельной временной базы.
Файл может называться 002_add_phone.sql: сначала номер или время, потом понятное описание. Расширение .sql показывает, что внутри SQL. Другие инструменты хранят миграции как программы на Python или TypeScript. Название и формат зависят от проекта.
Часто рядом есть ORM — Object-Relational Mapping, сопоставление объектов программы и таблиц базы. Это библиотека, с которой разработчик обращается к записям через привычные конструкции языка. Она может помочь подготовить миграцию, сравнив описание моделей. Но изменение описания модели ещё не подтверждает изменение самой базы.
Поэтому после просьбы «добавь телефон» полезно различать три результата: помощник изменил описание поля, подготовил файл перехода и действительно применил переход. Документация Django отдельно описывает создание миграций и их применение. Файлы сохраняют в истории проекта и передают вместе с кодом.
Почему «добавь обязательный телефон» — отдельная задача
У Анны и Бориса в старых заявках телефона нет. Из требования сделать поле обязательным номер не появится.
Правило NOT NULL запрещает отсутствие значения. NULL в базе означает, что значения нет; это не ноль и не пустая строка. В нашем опыте попытка добавить телефон с запретом NULL и без значения по умолчанию завершилась ошибкой. Старые заявки остались целыми.
Добавление поля без этого запрета прошло: в двух старых строках phone стал NULL. Так в базе видно, что телефоны пока неизвестны. Условия добавления столбцов в SQLite описывают ограничения такого изменения.
Дальше нужно решение о поведении сервиса. Например, для новых заявок телефон обязателен, а старые продолжают открываться с подписью «Телефон не указан». Можно предложить человеку дополнить сведения при следующем обращении. А можно вообще оставить телефон необязательным, если он не нужен для работы.
Просьба «подставь всем 000000» лишь прячет отсутствие сведений. Интерфейс получает похожее на номер значение, а сотрудник потом пытается по нему связаться. Это уже ошибка в данных.
Значение по умолчанию, по-английски default, — значение, которое база подставляет при оговорённых условиях, если его не передали. Оно уместно, когда соответствует смыслу. Например, новой заявке можно присвоить начальный статус. Но какой статус назначить давним заявкам, нужно решить отдельно: часть из них могла быть давно выполнена.
Так же проверяют и другие правила. Уникальность запрещает повтор значений: перед её включением нужно разобраться с существующими дублями. Внешний ключ связывает записи, например заказ с покупателем: перед добавлением такой связи нужно понять, что делать с заказами, у которых покупатель не найден. Генератор кода не знает нужного решения только по названиям полей.
Миграция структуры, данных и переезд на другой сервер
Под одним словом встречаются разные работы. Уточняйте, что именно меняется.
Миграция структуры меняет устройство базы: добавляет поле, таблицу или правило. Пример — телефон в заявке.
Миграция данных преобразует уже сохранённые значения. Например, сервис раньше хранил статус done, а новая программа ожидает completed; оба слова здесь означают «выполнено». Нужно проверить, где используются эти значения, и согласованно обновить их. Добавление места для нового значения и заполнение этого места могут быть разными шагами.
Переезд базы переносит её на другой сервер или в другую систему хранения. Тогда важны ещё перенос новых записей, переключение подключения и проверка полноты. Файл «добавить телефон» не выполняет такой переезд.
Бывает и миграция конфигурации: изменение формата настроек сервиса. Поэтому на фразу ИИ «нужна миграция» нормальный следующий вопрос: «Что конкретно меняется и где? Покажи старое и новое состояние».
Как база узнаёт, что уже было применено
Обычно инструмент миграций ведёт в базе служебный список применённых изменений. При запуске он сопоставляет этот список с файлами проекта и определяет, какие шаги ещё нужны. Точный порядок может учитывать номера, время и зависимости — указания, какой шаг должен завершиться раньше.
Здесь полезна история проекта в Git — системе, которая хранит версии файлов. Файл миграции попадает в неё вместе с изменением программы. История Git не заменяет служебный список в базе. Файл уже может лежать в репозитории, то есть хранилище кода, но ещё не быть применён к рабочей базе.
Повторный запуск инструмента обычно не должен заново добавлять уже добавленный столбец. Но это заслуга учёта выполненных шагов. Если вручную повторить ту же SQL-команду, база может сообщить, что столбец уже существует. Не каждая команда сама по себе подходит для повторения.
В нашем учебном запускателе есть ещё контрольная сумма — отпечаток содержимого файла. Повтор прежней миграции он пропустил, а изменённое содержимое под прежним именем отверг. Так устроен наш запускатель; у других инструментов проверка истории может отличаться.
Практическое правило: применённую общую миграцию обычно не переписывают. Новое исправление оформляют следующим шагом. Иначе у одного участника команды старый файл уже выполнен, у другого под тем же именем лежит новая версия, и одинаковые названия перестают означать одинаковое состояние.
Если кто-то меняет таблицу напрямую через панель базы, её состояние может разойтись с записанной историей. Такое расхождение называют drift, «дрейф схемы». Его разбирают сравнением реального устройства и истории. Отметка «выполнено» без проверки не исправляет отсутствующее поле.
Небольшой опыт, который можно повторить
Этот код создаёт собственную временную базу, добавляет телефон и читает результат. Он не берёт адреса подключения из настроек проекта и не обращается в сеть. После завершения временная папка удаляется.
Нужен Python 3 со стандартным модулем sqlite3. SQLite — база, которая может хранить данные в обычном файле без отдельного сервера базы. Сохраните пример в отдельный файл lesson_migration.py и запустите его командой python3 lesson_migration.py.
import sqlite3
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as folder:
path = Path(folder) / "lesson.sqlite"
with sqlite3.connect(path) as db:
db.execute("CREATE TABLE requests (id INTEGER PRIMARY KEY, full_name TEXT NOT NULL, amount INTEGER NOT NULL)")
db.executemany(
"INSERT INTO requests VALUES (?, ?, ?)",
[(1, "Учебная Анна", 1500), (2, "Учебный Борис", 1500)],
)
db.commit()
before = db.execute(
"SELECT id, full_name, amount FROM requests ORDER BY id"
).fetchall()
db.execute("ALTER TABLE requests ADD COLUMN phone TEXT")
after = db.execute(
"SELECT id, full_name, amount, phone FROM requests ORDER BY id"
).fetchall()
assert [row[:3] for row in after] == before
assert all(row[3] is None for row in after)
db.execute(
"INSERT INTO requests VALUES (?, ?, ?, ?)",
(3, "Учебная Вера", 2100, "demo-contact"),
)
db.commit()
with sqlite3.connect(path) as db:
saved = db.execute(
"SELECT full_name, phone FROM requests WHERE id = 3"
).fetchone()
assert saved == ("Учебная Вера", "demo-contact")
print("Старые заявки:", len(after), "Сумма:", sum(row[2] for row in after))
print("Новая заявка:", saved)
Что происходит внутри: CREATE TABLE создаёт таблицу, INSERT добавляет записи, SELECT читает их. PRIMARY KEY задаёт основной уникальный номер записи. commit() фиксирует изменения. Строки assert останавливают программу с ошибкой, если наблюдение не совпало с ожиданием. None — обозначение отсутствующего значения в Python; здесь так читается NULL из базы.
Проверки сравнивают не только число строк: прежние номера, имена и суммы должны остаться точно такими же. Затем программа добавляет третью заявку и читает её через новое подключение. demo-contact — вымышленная метка, а не настоящий телефон; проверку формата номера этот опыт не изучает.
У нас выполнение закончилось таким выводом:
Старые заявки: 2 Сумма: 3000
Новая заявка: ('Учебная Вера', 'demo-contact')
Это маленькая проверка перехода, не готовый инструмент миграций. Здесь нет посетителей, одновременных записей и огромной таблицы. Устройство модуля и фиксация изменений описаны в документации Python по sqlite3.
Как обновлять сервис, пока им пользуются
При обновлении старая и новая программы иногда работают одновременно. Например, одну серверную программу уже заменили, а другая ещё обрабатывает запрос. Или отдельная задача продолжает отправлять уведомления по старым правилам. Фоновая задача — работа сервиса, которая выполняется отдельно от текущего нажатия посетителя.
Поэтому порядок «сначала всегда код» или «сначала всегда база» не подходит ко всем изменениям. Важен вопрос: сможет ли каждая ещё работающая версия обращаться к базе после очередного шага?
Для нашего телефона возможен такой план:
- Добавить необязательное поле. Убедиться, что прежняя программа продолжает читать и записывать заявки.
- Выпустить код, который умеет сохранять телефон и открывать старые заявки без него.
- Если нужны старые значения, заполнить их по понятному источнику. Не выдумывать номера.
- Если поле действительно должно стать обязательным для всей таблицы, сначала проверить, что больше нет записей без значения и все действующие программы умеют его передавать. Затем отдельно включить правило базы.
В нашей проверке старое чтение с явным перечнем полей продолжило работать после добавления столбца. Это не доказательство совместимости любого старого кода. Программа, которая ждёт строго определённое число значений в ответе, может потребовать изменения. Нужно запустить ваши чтение и запись, а не только сравнить схему.
При замене старого поля новым используют подход expand and contract — «сначала расширить, потом убрать лишнее». Сначала добавляют новое место хранения, учат программу работать в переходном состоянии, переносят и проверяют данные. Старое поле удаляют позже, когда от него больше никто не зависит. Такой разбор есть в руководстве Prisma.
При этом новые записи не должны потеряться между переносом и переключением. На время перехода нужен продуманный способ сохранять изменения согласованно; конкретное решение зависит от сервиса. Простое копирование старых строк один раз эту задачу не закрывает.
Небольшому сервису иногда разумнее выделить короткое обслуживание и временно остановить запись. Это тоже план обновления. В нём заранее объясняют, что увидит посетитель, как закончатся уже начатые операции и по какому признаку запись можно снова открыть.
Почему миграция может надолго занять базу
Две учебные заявки мало нагружают базу. Преобразование большой рабочей таблицы может оказаться гораздо тяжелее. Изменение может потребовать прочитать или переписать множество записей.
База также использует блокировки: временно ограничивает другие операции, чтобы они не мешали изменению. В это время запросы посетителей могут ждать. Условия зависят от операции и системы базы; документация PostgreSQL об ALTER TABLE отдельно описывает уровни блокировок.
Ещё один частый случай — индекс, вспомогательная структура для поиска записей. Его создание тоже может занять время и ресурсы. «Всего одна строка SQL» ничего не говорит о длительности работы.
Для большого переноса значений часто нужны порции, ограничение нагрузки и учёт уже обработанного. Заполнение старых записей новым значением называют backfill, «дозаполнение». После прерывания нужно понимать, какие строки ещё остались, и можно ли безопасно повторить шаг.
Транзакция объединяет изменения так, чтобы их можно было зафиксировать вместе или отменить при ошибке. В отдельной SQLite-проверке мы внутри одной явно начатой транзакции добавили поле, изменили суммы и намеренно вызвали ошибку. Отмена вернула прежнюю структуру и суммы. Но из этого нельзя заключить, что любой инструмент оборачивает всю миграцию в такую транзакцию или что любая операция в любой базе допускает это.
Перед рабочим запуском полезно получить три ответа: что может задержать посетителей, как долго допустимо ждать и что произойдёт при остановке на середине. Наш маленький опыт время работы на вашей базе не предсказывает.
Что на самом деле означает откат
Откат кода возвращает прежнюю программу. Обратная миграция, её иногда называют down, пытается вернуть прежнее устройство базы. Восстановление из резервной копии возвращает сохранённое ранее состояние данных. Это три разных действия.
Мы проверили разницу на отдельной копии учебной базы. У третьей заявки было значение demo-contact. Удалили поле телефона, а потом создали поле с тем же именем заново. Заявок осталось три, но у третьей телефон стал NULL. Прежняя структура вернулась, значение — нет.
Именно поэтому недостаточно спросить: «Есть ли обратный файл?» Нужно узнать, какие сведения он сможет вернуть, какие потеряет и будет ли прежняя программа работать с текущими записями. Иногда правильнее исправить проблему следующим изменением, сохранив уже появившиеся данные.
Резервную копию в опыте мы сделали до добавления телефона. Для SQLite использовали штатный механизм копирования базы; его документация объясняет получение согласованной копии. Простое копирование одного файла занятой базы не стоит автоматически считать таким же результатом.
Копию восстановили в другое место и прочитали. Там были Анна и Борис с прежними суммами. Веры не было: её заявка появилась после момента копии. Восстановление оказалось исправным — и всё равно не вернуло последнюю запись, потому что её там никогда не было.
Для живого сервиса это вопрос плана восстановления: что делать с заказами между моментом копии и остановкой записи? У некоторых систем есть восстановление к нужному моменту по журналу изменений, но его наличие и работу нужно подтвердить. Само слово «бэкап» этого не обещает. Подробный порядок проверки есть в статье о восстановлении резервных копий.
Что делать, если миграция не прошла
Сначала сохраните текст ошибки и выясните, к какой базе обращалась команда. Пока это неизвестно, повтор с более сильными флагами только увеличивает неопределённость.
Строка подключения — настройка, которая указывает программе нужную базу и способ входа. Она часто содержит секреты. В отчёте достаточно безопасного обозначения среды и подтверждения назначения; пароль и полный адрес с доступами в чат не нужны.
Слова reset («сбросить»), drop («удалить»), truncate («очистить таблицу») и флаги с force («принудительно») требуют понимания конкретного действия. Иногда они полезны в пустом учебном проекте. Но ответ «надо сбросить базу, чтобы миграция прошла» не объясняет судьбу уже сохранённых заказов.
Если ИИ предлагает такой шаг, попросите перечислить, что будет удалено, почему без этого нельзя и какое есть исправление с сохранением данных. Не превращайте успешное прохождение команды в единственную цель. Общий способ разбирать наблюдение и причину показан в уроке о поиске ошибки с ИИ.
Что попросить у ИИ в своём проекте
Этот запрос предназначен помощнику, который уже может читать файлы вашего проекта: например, в Claude Code, Codex или Antigravity. Сначала получите объяснение и план отдельной проверки. Рабочую базу для этого менять не требуется.
Помоги разобраться с миграциями этого сервиса. Объясняй как человеку, который собирает проект с ИИ и учится программированию. Сейчас только изучение файлов: рабочую базу, её данные и настройки не меняй.
Изменение, которое хочу сделать: [например, добавить телефон к заявке].
1. Найди систему базы, установленную версию инструмента миграций, папку с их файлами и способ запуска. Покажи точные файлы. Секреты из настроек не печатай. Если версии или назначение подключения нельзя установить, так и скажи.
2. Объясни разницу между описанием модели, файлом миграции и её применением. Что из этого уже сделано по доступным свидетельствам? Не считай файл доказательством применения к базе.
3. Покажи ожидаемую структуру до и после. Что будет с прежними записями, отсутствующими значениями, дублями и связями? Где нужно моё решение о смысле данных?
4. Найди, какой код читает и записывает затронутые поля. Предложи порядок изменения базы и выпуска программы с учётом старой версии и фоновых задач. Не обещай совместимость без проверки.
5. Подготовь план отдельного локального опыта только с вымышленными записями. Он не должен брать рабочую строку подключения из окружения. Проверь старые значения, новую запись и её отдельное чтение, повтор запуска и сбой посередине. Добавь проверку как обновления существующей базы, так и установки с нуля.
6. Раздели откат программы, обратное изменение структуры и восстановление данных. Какие новые записи потеряются при восстановлении старой копии? Что нужно проверить до рабочего запуска?
7. Выведи короткий план, неизвестные условия и один следующий безопасный шаг. Команды выбирай только для найденного инструмента и его версии. Сброс базы, удаление данных и применение к рабочей среде в эту задачу не входят.
От хорошего ответа здесь нужны точные найденные файлы и проверяемый порядок действий. Если помощник сразу предлагает универсальную команду, хотя ещё не установил инструмент и среду, верните его к первому пункту. В разных версиях одной библиотеки команды могут различаться.
Если хотите сначала разобрать объяснение без доступа к коду, создайте текстового помощника в Ное. Для него подходит следующая инструкция: она работает с тем, что вы прислали, и не требует подключения рабочей базы.
Как разобрать план обычным разговором
Присылайте обезличенное описание: какое поле меняется, есть ли старые записи, что уже проверено и что именно предлагает ИИ. Не отправляйте реальные заявки, пароли и строки подключения. Помощник сможет объяснить последствия предложения, но выполнение команд остаётся в проекте.
Ты объясняешь начинающему создателю сервиса план миграции базы. Используй только присланные сведения. Не придумывай устройство проекта, команды, версии, результаты или доступ к базе.
Мой случай: [изменение, прежние данные, предложение помощника и наблюдения].
Объясни, что меняется в структуре и что в данных. Новые термины расшифруй рядом. Отдели факт от предположения. Файл миграции не доказывает её применение.
Если добавляют обязательное поле, а для прежних строк нет значения или источника, сначала объясни возможный отказ миграции. Не утверждай, что поле уже появится пустым: это зависит от базы и точной команды. Изменение кода формы тоже не доказывает её работу.
Различай откат кода, обратную схему и восстановление копии. Созданное заново поле не возвращает удалённые значения. В ранней копии нет поздних записей.
Не советуй сброс, удаление или рабочий запуск. При нехватке условий задай до двух вопросов. Предложи одну проверку в отдельной учебной базе с вымышленными данными. Не выдавай предложение проверки за её результат.
Ответ: «Что происходит», «Чего пока не знаем», «Что проверить». По одному короткому абзацу, до 1200 знаков. Не заявляй, что сам читал проект или выполнил команды.
Создайте в Ное помощника для разбора миграций и поставьте ему эту инструкцию. К нему можно возвращаться с обезличенным планом следующего изменения и разбирать новые вопросы на знакомом примере. Настоящую проверку проводят в приложении и базе; понятное объяснение помогает её спланировать.
Мы дали помощнику два учебных случая: обязательный телефон без старых значений и восстановление ранней копии после удаления поля. Во втором ответе он написал:
Восстановление копии базы на 10:00 вернёт структуру и данные именно того момента: только 2 заявки и вообще без телефона для 3‑й, потому что она тогда ещё не существовала.
В первом варианте инструкции помощник ошибочно предположил, что обязательное поле появится пустым. После уточнения стал сначала обсуждать возможность отказа миграции. Но ответы всё равно оказались длиннее заданных 1200 знаков, а во втором не было отдельного объяснения отката кода. Он также употребил «СУБД» без расшифровки: это система управления базами данных, программа для работы с базой. Сверяйте объяснение с условиями своего случая и задавайте пропущенный вопрос; сам опыт в базе остаётся решающей проверкой.
Что мы проверили в учебной базе
Редакционный опыт выполнен на SQLite 3.45.1 в отдельных временных файлах. Рабочие базы и клиентские данные не использовались. Кроме короткого примера выше, мы проверили следующие ситуации:
Ещё запускатель проверил повтор применения и изменение уже применённой инструкции. Эти результаты описывают конкретный маленький опыт. Они не подтверждают скорость, блокировки и возможность отката в другом проекте. Зато показывают, какие вопросы задавать к его настоящей проверке.
Изменение базы становится понятнее, когда видны остальные части приложения. В уроке про архитектуру проекта собираем карту сервиса и выясняем, какие правила, процессы и внешние вызовы затронет следующая правка.
С чего начать прямо сейчас
Найдите одно недавнее изменение, в котором ИИ упоминал миграцию. Попросите показать её файл и обычными словами объяснить судьбу одной старой записи: что в ней появится, что останется и что может исчезнуть.
Затем разберите, где изменение уже применено и чем это подтверждено. Если сейчас известен только файл, следующий шаг — выяснить состояние нужной среды. Так вы постепенно начнёте видеть за словом «миграция» конкретную работу с данными своего сервиса.
Частые вопросы
Для каждой правки нужна миграция?
Нет. Цвет кнопки или текст на странице обычно не меняют базу. Миграция нужна, когда меняется её устройство или требуется согласованное преобразование сохранённых данных. Смотрите на последствия правки, а не только на её размер.
Почему на пустой базе всё работает, а на старой нет?
В пустой базе нет накопленных значений, которые могут нарушать новое правило. Например, дублей перед включением уникальности. Проверяйте и установку с нуля, и переход с прежней версии на характерных учебных данных: они отвечают на разные вопросы.
Нужно ли вручную запускать файл после каждого выпуска?
Это зависит от проекта. Применение может быть отдельной командой или частью процесса выпуска. Выясните, кто и где его запускает, как учитываются выполненные шаги и где видно ошибку. Самостоятельный запуск каждой копией приложения без согласованного механизма может создать гонку — несколько программ попытаются выполнить изменение одновременно.
Что такое начальная миграция и заполнение тестовыми данными?
Начальная миграция создаёт исходное устройство базы. Если база уже существует, нужно аккуратно согласовать её состояние с историей, а не повторно создавать таблицы. Заполнение примерами часто называют seed: это отдельное действие. По наличию учебных записей нельзя судить, правильно ли настроено обновление.
Можно ли полностью доверить миграции ИИ?
Помощник может найти файлы, подготовить изменение и проверить его в разрешённой среде. Но вы должны понимать назначение подключения, судьбу прежних данных и наблюдаемый результат. Ответ «всё применено» без указания среды и доказательства не позволяет отличить успешное обновление от изменённого файла.
Вывод
Возьмите одну старую запись и проследите её через изменение базы. Когда понятно, что с ней произойдёт и как это проверить, миграция перестаёт быть непонятной командой из ответа ИИ.