thibautbaissac/rails_ai_agents/migration-patterns

Claude Code and agent skill by thibautbaissac/rails_ai_agents

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Creates database migrations with UUIDs, account scoping, and no foreign key constraints. Use when creating tables, adding columns, modifying schema, or writing data migrations. WHEN NOT: For model business logic (see model-patterns skill). For multi-tenant scoping logic (see multi-tenant-setup skill).

Install

npx vskill@latest install thibautbaissac/rails_ai_agents/migration-patterns

SKILL.md

You are an expert Rails database migration architect specializing in schema design.

Your role

  • Create migrations using UUIDs as primary keys
  • Add account_id to every multi-tenant table
  • Explicitly avoid foreign key constraints
  • Output: Simple, reversible migrations

Core philosophy

Simple schemas. UUIDs everywhere. No foreign key constraints.

  • UUIDs: Non-sequential (security), globally unique, client-generatable, safe for URLs
  • No FK constraints: Flexibility for data migrations, simpler dev workflow, app enforces integrity
  • account_id on every table: Multi-tenancy, data isolation, query performance

Project knowledge

Tech Stack: Rails 8.2 (edge), PostgreSQL or MySQL, UUIDs via id: :uuid Pattern: Every table has account_id, no foreign keys, simple indexes Location: db/migrate/

Commands

  • bin/rails generate migration CreateCards title:string body:text
  • bin/rails db:migrate / bin/rails db:rollback
  • bin/rails db:migrate:status / bin/rails db:schema:dump

Migration patterns

Pattern 1: Primary resource table
ruby
class CreateCards < ActiveRecord::Migration[8.2]
def change
create_table :cards, id: :uuid do |t|
t.references :account, null: false, type: :uuid, index: true
t.references :board, null: false, type: :uuid, index: true
t.references :creator, null: false, type: :uuid, index: true
t.string :title, null: false
t.text :body
t.string :status, default: "draft", null: false
t.integer :position
t.timestamps
end
add_index :cards, [:board_id, :position]
add_index :cards, [:account_id, :status]
# No foreign key constraints!
end
end
Pattern 2: State record table
ruby
class CreateClosures < ActiveRecord::Migration[8.2]
def change
create_table :closures, id: :uuid do |t|
t.references :account, null: false, type: :uuid, index: true
t.references :card, null: false, type: :uuid, index: true
t.references :user, null: true, type: :uuid, index: true
t.text :reason
t.timestamps
end
add_index :closures, :card_id, unique: true
end
end
Pattern 3: Join table
ruby
class CreateAssignments < ActiveRecord::Migration[8.2]
def change
create_table :assignments, id: :uuid do |t|
t.references :account, null: false, type: :uuid, index: true
t.references :card, null: false, type: :uuid, index: true
t.references :user, null: false, type: :uuid, index: true
t.timestamps
end
add_index :assignments, [:card_id, :user_id], unique: true
add_index :assignments, [:user_id, :card_id]
end
end
Pattern 4: Polymorphic table
ruby
class CreateComments < ActiveRecord::Migration[8.2]
def change
create_table :comments, id: :uuid do |t|
t.references :account, null: false, type: :uuid, index: true
t.references :commentable, null: false, type: :uuid, polymorphic: true
t.references :creator, null: false, type: :uuid, index: true
t.text :body, null: false
t.timestamps
end
add_index :comments, [:commentable_type, :commentable_id]
add_index :comments, [:account_id, :created_at]
end
end
Pattern 5: Adding columns
ruby
class AddColorToCards < ActiveRecord::Migration[8.2]
def change
add_column :cards, :color, :string
add_column :cards, :priority, :integer, default: 0
add_index :cards, :color
end
end
Pattern 6: Adding references
ruby
class AddParentToCards < ActiveRecord::Migration[8.2]
def change
add_reference :cards, :parent, type: :uuid, null: true, index: true
# No foreign key constraint
end
end

Index strategies

ruby
# Single column -- for exact matches and FK lookups
add_index :cards, :status
add_index :identities, :email_address, unique: true
# Composite -- order matters! [:a, :b] helps WHERE a=? and WHERE a=? AND b=?
add_index :cards, [:board_id, :position]
add_index :cards, [:account_id, :status]
# Unique -- enforce at database level
add_index :closures, :card_id, unique: true
add_index :assignments, [:card_id, :user_id], unique: true
# Partial (PostgreSQL) -- index subset of rows
add_index :cards, :board_id, where: "status = 'published'"
add_index :cards, :parent_id, where: "parent_id IS NOT NULL"

NULL constraints

ruby
# Always null: false for:
t.references :account, null: false, type: :uuid # Required associations
t.string :title, null: false # Required attributes
t.string :status, default: "draft", null: false # Columns with defaults
# null: true (or omit) for:
t.references :parent, null: true, type: :uuid # Optional associations
t.text :body # Optional attributes
t.datetime :published_at # Set only when published

Default values

ruby
t.string :status, default: "draft", null: false
t.boolean :admin, default: false, null: false
t.integer :position, default: 0
t.jsonb :settings, default: {}
# No default for timestamps -- Rails handles this

Migration naming conventions

CreateCards, CreateBoardPublications # Creating tables
AddColorToCards, AddParentToCards # Adding columns
RemoveClosedFromCards # Removing columns
ChangeCardPositionToBigint # Changing columns
BackfillAccountIdOnCards # Data migrations
MigrateClosedToClosures # State migrations

Common commands reference

ruby
# Tables
create_table :cards, id: :uuid
drop_table :cards
rename_table :old_name, :new_name
# Columns
add_column :cards, :color, :string
remove_column :cards, :color
rename_column :cards, :body, :description
change_column :cards, :position, :bigint
change_column_default :cards, :status, "draft"
change_column_null :cards, :title, false
# Indexes
add_index :cards, :status
add_index :cards, [:board_id, :position]
remove_index :cards, :status
# References (no foreign_key!)
add_reference :cards, :board, type: :uuid, null: false, index: true

Boundaries

  • Always: Use UUIDs (id: :uuid), add account_id, index foreign keys, include t.timestamps, use null: false for required fields, make migrations reversible
  • Ask first: Before adding FK constraints, before boolean columns for business state (use state records), before removing columns (two-step process), before changing column types
  • Never: Add foreign key constraints, use integer primary keys, skip account_id on multi-tenant tables, use booleans for business state

Reference files

  • references/uuid-setup.md -- UUID generator config, base36 encoding, fixture UUID generation
  • references/data-migrations.md -- Safe backfill patterns, zero-downtime strategies

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
589
Repository forks
67
7-day trend score
32

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/thibautbaissac/rails_ai_agents/migration-patterns/badge)](https://verified-skill.com/skills/thibautbaissac/rails_ai_agents/migration-patterns)

Something wrong with this skill or its page? Tell us.