Что такое migrations в Rails и как их создавать и запускать?
Migrations — Ruby-классы в db/migrate/, описывающие изменения схемы БД. Создают через rails generate migration, применяют командой rails db:migrate, откатывают через db:rollback.
Migrations в Rails: создание и запуск
Migrations — это классы Ruby, которые описывают изменения схемы базы данных в виде кода. Они позволяют версионировать структуру БД наравне с исходным кодом и воспроизводить её на любой машине.
Создание миграции
Генератор создаёт файл в каталоге db/migrate/ с именем вида YYYYMMDDHHMMSS_название.rb:
rails generate migration CreateProducts name:string price:decimal
rails generate migration AddStockToProducts stock:integer
rails generate migration RemoveDescriptionFromProducts description:text
Rails автоматически распознаёт префиксы Add*To*, Remove*From*, Create* и генерирует тело класса. Файл выглядит так:
class CreateProducts < ActiveRecord::Migration[7.1]
def change
create_table :products do |t|
t.string :name, null: false
t.decimal :price, precision: 10, scale: 2
t.timestamps
end
add_index :products, :name, unique: true
end
end
Основные методы
create_table/drop_table— создание и удаление таблицы.add_column/remove_column— добавление и удаление столбца.change_column,rename_column— изменение типа или имени.add_index/remove_index— управление индексами.add_reference— добавляет_id-столбец и опциональный внешний ключ.execute— произвольный SQL для нестандартных операций.
Метод change vs up/down
Метод change автоматически строит обратную операцию (rollback). Если Rails не умеет это сделать автоматически, нужно явно описать up и down:
class ChangeProductsPrice < ActiveRecord::Migration[7.1]
def up
change_column :products, :price, :numeric, precision: 12, scale: 4
end
def down
change_column :products, :price, :decimal, precision: 10, scale: 2
end
end
Запуск миграций
# Применить все новые миграции
rails db:migrate
# Откатить последнюю
rails db:rollback
# Откатить несколько
rails db:rollback STEP=3
# Мигрировать до конкретной версии
rails db:migrate VERSION=20240301120000
# Статус всех миграций
rails db:migrate:status
# Пересоздать БД (dev/test)
rails db:drop db:create db:migrate db:seed
schema.rb и structure.sql
После каждого db:migrate Rails обновляет db/schema.rb (или db/structure.sql при config.active_record.schema_format = :sql). Этот файл — единственный источник истины о текущей схеме. Его нужно коммитить вместе с миграцией.
Подводные камни
- Изменение существующих миграций. Уже применённые миграции нельзя редактировать — нужно создавать новую. Иначе чексумма в
schema_migrationsне совпадёт на других машинах. - Необратимые операции без up/down.
change_columnв методеchangeвызоветActiveRecord::IrreversibleMigrationпри откате — обязательно пишитеup/down. - Блокировка таблицы в production.
add_columnсdefault:в старых версиях PostgreSQL блокирует таблицу на всё время перезаписи строк. Используйтеadd_columnбез default, затемchange_column_default. - Большие таблицы и zero-downtime.
add_indexбезalgorithm: :concurrentlyв PostgreSQL блокирует запись. Используйтеadd_index ..., algorithm: :concurrentlyи заверните вdisable_ddl_transaction!. - Забытый db:migrate в CI/CD. Если миграция есть, а
schema.rbне обновлён в репозитории, другие разработчики получают расхождение схемы. - Конфликты меток времени. При одновременной работе двух веток с миграциями одного времени Rails применит обе, но порядок может быть неожиданным.
- Перенос данных внутри миграции. Вызов моделей (
Product.update_all) внутри миграции опасен: если модель позже изменится, старая миграция сломается. Используйтеexecuteс прямым SQL или создайте отдельную data-migration задачу.
Common mistakes
- Сводить migrations к названию метода без lifecycle и failure path.
- Игнорировать модель runtime: Rails 8.1 строит приложение вокруг Rack, routing, controllers, Active Record, views и conventions over configuration.
- Не отделять validation, authorization, transaction boundary и business logic.
What the interviewer is testing
- Объясняет migrations через конкретную точку lifecycle в Ruby on Rails.
- Приводит корректный минимальный пример без вымышленных методов или callbacks.
- Называет edge cases: пустые значения, ошибки, транзакции, безопасность или concurrency.