Skip to content

Generate migrations (database or data)

Each ActiveRecord model must have a database table. Database tables are created and modified by migrations.

A migration is a Ruby class that inherits from the current Rails migration version and is located in the db/migrate directory. For the current application version, use ActiveRecord::Migration[8.1].

When Rails is upgraded, use the version generated by rails g migration ... for new migrations instead of copying an older migration superclass.

Class naming: Action + ModelClassName — e.g. CreateMyModel or AddMyColumnToMyModel.

Method: Use a single change method. The older two-method format (up / down) may appear in legacy code.


Filename Timestamp Format

Migration filenames follow this convention:

YYYYMMDDHHMMSS_migration_class_name.rb

The timestamp is generated via Time.now.utc.strftime("%Y%m%d%H%M%S"), producing a 14-digit UTC timestamp.

IMPORTANT for agents: Never use placeholder timestamps like 20210101000000. Always use a realistic current UTC timestamp.

Example breakdown for 20240502101659:

Part Value Meaning
2024 YYYY Year
05 MM Month
02 DD Day
10 HH Hour (24h, UTC)
16 MM Minute
59 SS Second

Example filename:

db/migrate/20240502101659_create_my_model.rb

Creating a new migration

The strongly recommended way is to use the rails generator:

rails g migration CreateMyModel author:belongs_to name:string description:text status:integer

This creates db/migrate/20240502101659_create_my_model.rb with:

class CreateMyModel < ActiveRecord::Migration[8.1]

  def change
    create_table :my_models do |t|
      t.belongs_to :author, null: false
      t.belongs_to :collection, null: true
      t.string :name, null: false
      t.text :description, null: false
      t.integer :status, null: false, index: true

      t.timestamps
    end
  end

end

Indexing

Columns used for search should be indexed. Define indexes inline:

t.string :name, null: false, index: true

or explicitly with add_index:

add_index :my_models, :name

Using an existing table

In some specific cases an existing table may be reused. In that case, the table name is specified in the model definition.


Changing an existing table

Never alter a migration after it has been executed. Always create a new migration for any change. Example:

rails generate migration RemoveDescriptionFromMyModels description:text

Generated migration:

class RemoveDescriptionFromMyModels < ActiveRecord::Migration[8.1]

  def change
    remove_column :my_models, :description, :text
  end

end

Before release

Before the first release of a new model, all migrations created during development must be merged into a single migration that creates the table with all columns.

Development period best practice

When a model changes frequently during development, use force: true on create_table to drop and recreate the table on each migration run:

create_table :my_models, force: true do |t|
  # column definitions
end

Tip: To force re-execution of a migration in an already-migrated environment, increment the timestamp by 1 second. Example: 20240502101659_create_my_model.rb20240502101700_create_my_model.rb


Data migration

Data migrations modify existing data in the database. They are located in the db/data directory and follow the same filename convention as DB migrations.

Data migrations inherit from EasyExtensions::EasyDataMigration and must implement both up and down methods.

Use cases:

  • Prepare data before first release
  • Alter existing data when a significant change is introduced
class DataChangeInDb < EasyExtensions::EasyDataMigration

  def up
    # data change implementation
  end

  def down
    # rollback implementation
  end

end