ai_misc/skills/hoblin-activerecord/SKILL.md
--- name: ActiveRecord description: This skill should be used when the user asks to "write a migration", "add a column", "add column to table", "create an index", "add a foreign key", "set up associations", "fix N+1 queries", "optimize queries", "add validations", "create callbacks", "use eager loading", or mentions ActiveRecord, belongs_to, has_many, has_one, :through associations, polymorphic associations, inverse_of, touch: true, counter_cache, dependent: destroy, where clauses, scopes, inclu
npx skillsauth add madbomber/experiments ai_misc/skills/hoblin-activerecordInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides comprehensive guidance for working with ActiveRecord in Rails applications. Use for writing migrations, defining associations, optimizing queries, preventing N+1 issues, implementing validations, and following database best practices.
# Create
user = User.create(name: "Alice", email: "[email protected]")
user = User.create!(...) # Raises on failure
# Read
User.find(1) # Raises RecordNotFound
User.find_by(email: "x") # Returns nil if not found
User.where(active: true) # Returns Relation
# Update
user.update(name: "Bob")
user.update!(...) # Raises on failure
# Delete (callbacks run)
user.destroy
# Delete (no callbacks)
user.delete
| Concept | Purpose |
|---------|---------|
| belongs_to | Child side of association (has foreign key) |
| has_many / has_one | Parent side of association |
| has_many :through | Many-to-many via join model |
| includes / preload | Eager loading (prevent N+1) |
| scope | Named query builder |
| validates | Model-level data validation |
| before_save / after_commit | Lifecycle callbacks |
Need to access associated data?
├── NO → Use `joins` (filtering only)
└── YES → Need to filter/sort by association?
├── NO → Use `preload` (separate queries)
└── YES → Large dataset with many associations?
├── YES → Use `includes` with `references`
└── NO → Use `eager_load` (single JOIN)
| Method | Strategy | Best For |
|--------|----------|----------|
| includes | Auto-choose | Default choice |
| preload | Separate queries | Large datasets, no filtering |
| eager_load | LEFT OUTER JOIN | Filtering by association |
| joins | INNER JOIN | Filtering only, not accessing data |
# N+1 problem
Post.all.each { |p| p.author.name } # 1 + N queries
# Solution
Post.includes(:author).each { |p| p.author.name } # 2 queries
Does the rule ALWAYS apply, regardless of business logic?
├── Yes → Database constraint
│ └── Examples: NOT NULL, foreign keys, unique emails
└── No → Model validation
└── Examples: Format rules that change, conditional requirements
Need helpful user-facing error messages?
├── Yes → Model validation (possibly WITH constraint)
└── No → Constraint alone is fine
Best Practice: Use both for critical fields:
# Migration (data integrity)
add_index :users, :email, unique: true
# Model (user feedback)
validates :email, presence: true, uniqueness: true
class Author < ApplicationRecord
has_many :books, dependent: :destroy
has_one :profile
end
class Book < ApplicationRecord
belongs_to :author # Required by default
belongs_to :publisher, optional: true # Allow NULL
end
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
# Join model can have attributes
validates :scheduled_at, presence: true
end
| Option | Purpose |
|--------|---------|
| inverse_of | Required with custom foreign_key |
| dependent: :destroy | Cascade delete with callbacks |
| counter_cache: true | Cache association count |
| touch: true | Update parent's updated_at |
# Always reversible
add_column :users, :name, :string
add_index :users, :email, unique: true
add_reference :orders, :user, foreign_key: true
# Concurrent index (no table lock)
disable_ddl_transaction!
add_index :users, :email, algorithm: :concurrently
remove_column :users, :legacy_field, :string # Include type!
change_column_default :users, :status, from: nil, to: "active"
before_validation → after_validation →
before_save → around_save → before_create →
around_create → [INSERT] → after_create →
after_save → [COMMIT] → after_commit
# WRONG - Race condition!
after_save :enqueue_processing
# CORRECT - Runs after COMMIT
after_commit :enqueue_processing, on: :create
# BAD - loads all records
User.all.each { |u| process(u) }
# GOOD - processes in batches
User.find_each { |u| process(u) }
# Bulk operations
User.where(old: true).in_batches.update_all(archived: true)
includes to prevent N+1 queriesfind_each for processing large datasetspluck(:column) instead of all.map(&:column)inverse_of when using custom foreign_keyafter_commit for background jobs and external APIshas_many :through over has_and_belongs_to_manydefault_scope (causes subtle issues)delete when you need callbacksupdate_column to bypass validations casuallyafter_save for external system interactions| Anti-Pattern | Solution |
|--------------|----------|
| N+1 queries | Use includes, preload, or eager_load |
| User.all.map(&:email) | Use User.pluck(:email) |
| Uniqueness without index | Add unique database index |
| validates :active, presence: true | Use inclusion: { in: [true, false] } for booleans |
| after_save for jobs | Use after_commit |
| Callback hell | Extract to service objects |
| default_scope | Use explicit scopes |
| has_and_belongs_to_many | Use has_many :through |
For detailed patterns and complete API references, consult:
references/basics.md - Conventions, CRUD, dirty tracking, STI, type castingreferences/migrations.md - Schema changes, indexes, constraints, safe patternsreferences/validations.md - Built-in validators, custom validators, contextsreferences/callbacks.md - Lifecycle hooks, transaction callbacks, alternativesreferences/associations.md - All association types, inverse_of, dependent optionsreferences/querying.md - Finders, eager loading, scopes, batch processingReady-to-use code patterns in examples/:
examples/basics/ - CRUD, dirty tracking, type casting, inheritanceexamples/migrations/ - Schema changes, indexes, safe patterns, reversibilityexamples/validations/ - Built-in, conditional, custom, contexts, constraintsexamples/callbacks/ - Lifecycle, transaction callbacks, conditional, alternativesexamples/associations/ - Basic, through, polymorphic, self-referential, extensionsexamples/querying/ - Finders, eager loading, scopes, batch processing, optimizationdata-ai
Postgres performance optimization and best practices from Supabase. Use this skill when writing, reviewing, or optimizing Postgres queries, schema designs, or database configurations.
development
Apply SOLID principles when writing, reviewing, or refactoring Ruby code. This skill should be used when designing classes, evaluating architecture, reviewing pull requests, or refactoring existing code. It provides actionable checklists, violation detection patterns, and Ruby-idiomatic refactoring strategies for each of the five SOLID principles.
tools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
development
Analyze Ruby and Rails code quality with RubyCritic. Identifies code smells, complexity issues, and refactoring opportunities. Provides detailed metrics, scores files A-F, compares branches, and prioritizes high-churn problem areas. Use when analyzing Ruby code quality, reviewing PRs, or identifying technical debt.