# Create table migration
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :email, null: false, index: { unique: true }
t.string :name, null: false
t.string :password_digest
t.string :role, default: 'user'
t.integer :posts_count, default: 0
t.timestamp :last_login_at
t.timestamps
end
add_index :users, :email
add_index :users, :role
end
end
# Add column migration
class AddAvatarToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :avatar_url, :string
add_column :users, :bio, :text
end
end
# Remove column
class RemoveAvatarFromUsers < ActiveRecord::Migration[7.0]
def change
remove_column :users, :avatar_url, :string
end
end
# Rename column
class RenameUserNameToFullName < ActiveRecord::Migration[7.0]
def change
rename_column :users, :name, :full_name
end
end
# Change column type
class ChangeUserBioToText < ActiveRecord::Migration[7.0]
def change
change_column :users, :bio, :text
end
end
# Add foreign key
class AddUserIdToPosts < ActiveRecord::Migration[7.0]
def change
add_reference :posts, :user, null: false, foreign_key: true, index: true
end
end
# Custom index
class AddCompoundIndexToPosts < ActiveRecord::Migration[7.0]
def change
add_index :posts, [:user_id, :status]
add_index :posts, [:status, :created_at], where: "status = 'published'"
# Full-text search index (PostgreSQL)
add_index :posts, :content, using: :gin,
opclass: :gin_trgm_ops # Requires pg_trgm extension
end
end
# Data migration
class MigrateUserRoles < ActiveRecord::Migration[7.0]
def up
User.where(admin: true).update_all(role: 'admin')
User.where(admin: false).update_all(role: 'user')
remove_column :users, :admin
end
def down
add_column :users, :admin, :boolean, default: false
User.where(role: 'admin').update_all(admin: true)
end
end
# Reversible block for complex operations
class AddStatusToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :status, :string
reversible do |dir|
dir.up do
User.update_all(status: 'active')
end
dir.down do
# Cleanup logic
end
end
change_column_null :users, :status, false
end
end
# Add constraint
class AddConstraintToUsers < ActiveRecord::Migration[7.0]
def change
add_check_constraint :users, "age >= 18", name: "users_age_check"
# Uniqueness constraint
add_index :users, :email, unique: true
end
end
# Create join table
class CreateJoinTableUsersGroups < ActiveRecord::Migration[7.0]
def change
create_join_table :users, :groups do |t|
t.index :user_id
t.index :group_id
t.timestamps
end
end
end
# Enable PostgreSQL extension
class EnablePgExtensions < ActiveRecord::Migration[7.0]
def change
enable_extension 'uuid-ossp'
enable_extension 'pg_trgm' # Trigram matching for LIKE queries
enable_extension 'hstore' # Key-value storage
end
end
# Create enum type (PostgreSQL)
class CreatePostStatusEnum < ActiveRecord::Migration[7.0]
def up
execute <<-SQL
CREATE TYPE post_status AS ENUM ('draft', 'published', 'archived');
SQL
add_column :posts, :status, :post_status, default: 'draft'
end
def down
remove_column :posts, :status
execute <<-SQL
DROP TYPE post_status;
SQL
end
end
Rails migrations evolve database schema over time. I use change method for reversible migrations. Migrations create tables, add/remove columns, add indices. up and down methods provide explicit control. Irreversible migrations like data transformations need manual down. Database constraints—foreign keys, uniqueness, not null—ensure integrity. Indices speed queries—I add them to foreign keys and frequently queried columns. migrate:rollback undoes migrations. db:migrate:status shows applied migrations. Migrations are version-controlled, enabling team coordination. I keep migrations small and focused. Understanding SQL helps write efficient schema changes. Proper indexing dramatically improves production query performance.
Related snips
class CommentsController < ApplicationController
before_action :set_post
def create
@comment = @post.comments.build(comment_params)
System test: asserting Turbo Stream responses
class Money
include Comparable
attr_reader :amount, :currency
def initialize(amount, currency = 'USD')
Value objects for domain modeling
class Post < ApplicationRecord
belongs_to :author, class_name: 'User'
has_many :comments, dependent: :destroy
scope :published, -> { where.not(published_at: nil).where('published_at <= ?', Time.current) }
scope :draft, -> { where(published_at: nil) }
ActiveRecord scopes for reusable query logic
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
class PostsController < ApplicationController
def index
@posts = Post.includes(:author)
.order(created_at: :desc)
.page(params[:page])
.per(10)
Turbo Frames: infinite scroll with lazy-loading frame
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
Share this code
Here's the card — post it anywhere.