Rails' ORM is the textbook implementation of the Active Record pattern Martin Fowler named — the row, the class, and the data-access methods are one object. Naming conventions do most of the configuration; you write models, not mappings. It set the bar that nearly every modern web ORM still measures itself against.
← Back to Database Sideclass Order < ApplicationRecord auto-binds to the orders table.has_many :line_items, belongs_to :customer. Reads like English, generates SQL underneath.scope :active, -> { where(status: "active") }.add_column, create_table, reversible by default.The ORM layer of Rails. Usable standalone, but ~99% of usage is inside a Rails app.
The relational algebra layer underneath AR's query DSL. You drop here when chained methods can't express what you need.
Schema is canonical in db/schema.rb (or structure.sql) — generated from migrations, checked in.
Model-level validation, lifecycle hooks (before_save, after_commit). Powerful and dangerous in equal measure.
The Ruby Data Mapper. Cleaner separation, more SQL-shaped. Loved by people who outgrew AR's magic.
The N+1 detector. Practically required on any Rails app of size — surfaces missing includes in dev and CI.
Tables plural, classes singular, foreign keys named {model}_id, timestamps in created_at/updated_at. Follow the conventions and there's almost no mapping code. Break them and you can override with a single line. The economy of the framework comes from this discipline.
Order.active.includes(:line_items).where("total > ?", 100).order(created_at: :desc).limit(20). Lazy until enumerated, chainable, easy to read. Twenty years on, nothing in the JS or Python world has fully matched the ergonomics.
Most migrations are reversible automatically — add_column implies remove_column on rollback. The exceptions (data backfills, drops) require an explicit up/down. The DSL nudges you toward safe operations.
Twenty years of Rails apps have produced a deep bench of gems: bullet, scenic (database views), strong_migrations, annotate, rails-erd. The ecosystem solves problems before you find them.
after_save :send_welcome_email is fine — until the test fixture creation triggers SendGrid, until the rake task creates a million records and queues a million emails, until the callback chain forms a cycle. Mature Rails teams move side effects out of callbacks into explicit service objects.
Forget includes and the loop fires a query per row. Bullet catches it; reviewers should too. The same problem every ORM has — Rails just makes it especially easy to commit.
"Skinny controllers, fat models" was the original advice. Then models became 2000-line god classes. The current consensus is concerns, service objects, and form objects — break responsibilities out before AR turns into a junk drawer.
Two devs branch, both write migrations against the same parent, both merge — schema.rb conflicts and timestamp ordering matters. Convention is to renumber the loser; tooling exists to automate it. Painful enough at scale that some teams switch to structure.sql.
The Active Record pattern couples persistence to the domain object. For complex domains (DDD, CQRS, event sourcing), that coupling is the problem, not the solution. Rails apps that grow past a certain complexity often introduce a Repository / Data Mapper layer on top — at which point Sequel starts looking attractive.
where clauses.OrderCheckout.new(...).call instead of before_save chains.add_column with default on a big table, add_index non-concurrently).Comment.where(post_id: 1).count hot paths — denormalize the count onto the post.find_by_sql is fine when AR's DSL is in the way.