Object-Relational Mapping (ORM) is the backbone of most Ruby web applications — it translates between your Ruby objects and relational database tables, handling everything from query generation to schema migrations. While Rails’ ActiveRecord dominates the Ruby ORM landscape by virtue of being Rails’ default, two powerful alternatives — Sequel and ROM-rb — offer fundamentally different philosophies that may better suit certain projects.

In this article, we compare ActiveRecord, Sequel, and ROM-rb across API design, performance, query flexibility, and architectural philosophy.

Comparison Table

FeatureActiveRecordSequelROM-rb
Stars58,655 (Rails)5,0912,108
ParadigmActive Record PatternSQL Toolkit + ORMData Mapping + Repository
Schema ManagementBuilt-in MigrationsBuilt-in MigrationsExternal (Sequel/Standalone)
Query DSLRich, ChainableSQL-native, Extremely FlexibleRelation-based, Composable
Database SupportMySQL, PostgreSQL, SQLite, +10+ (including Oracle, MSSQL)PostgreSQL, MySQL, SQLite
Implicit BehaviorHigh (magic methods)Low (explicit)Low (explicit)
Learning CurveEasyMediumHigh

ActiveRecord: The Rails Default

ActiveRecord is the ORM that ships with Ruby on Rails. It implements the Active Record design pattern, where each database table maps to a Ruby class and each row maps to an instance of that class. It is famously “convention over configuration” — if you name your table users and your model User, everything just works.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :comments, through: :posts

  validates :email, presence: true, uniqueness: true
  validates :username, length: { minimum: 3, maximum: 30 }

  scope :active, -> { where(active: true) }
  scope :recent, -> { where('created_at > ?', 30.days.ago) }

  def display_name
    "#{username} (#{email})"
  end
end

# Usage
user = User.includes(:posts).find_by(email: 'alice@example.com')
recent_users = User.active.recent.limit(10)

ActiveRecord’s strength is its seamless integration with Rails — form builders, validations, callbacks, counter caches, and view helpers all assume ActiveRecord conventions. The trade-off is that ActiveRecord encourages “fat models” that accumulate business logic, callbacks, and query logic, making them difficult to test and maintain at scale.

Sequel: The SQL Programmer’s ORM

Sequel takes a fundamentally different approach: it is first and foremost a SQL toolkit that also provides ORM functionality. When you use Sequel, you are writing SQL — just with Ruby syntax. This gives you fine-grained control over every query while still benefiting from connection pooling, dataset chaining, and thread safety.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require 'sequel'

DB = Sequel.connect('postgres://localhost/mydb')

# Create a dataset (lazy — no query executed yet)
users = DB[:users].where(active: true)

# Chain operations
admins = users.where(role: 'admin')
            .order(Sequel.desc(:created_at))
            .limit(20)

# Execute when needed
admins.each { |user| puts user[:email] }

# Complex joins with raw SQL
result = DB[:users]
  .left_join(:posts, user_id: :id)
  .select_group(:users__id)
  .select_append { count(:posts__id).as(:post_count) }
  .having { count(:posts__id) > 5 }
  .all

# Sequel also supports the model pattern
class User < Sequel::Model
  one_to_many :posts
  plugin :validation_helpers

  def validate
    super
    validates_presence :email
    validates_unique :email
  end
end

Sequel’s standout features include: true threading support (unlike ActiveRecord’s connection pool limitations), excellent PostgreSQL-specific features (CTEs, window functions, lateral joins), master/slave database splitting, and database sharding — all built into the core library without additional gems. It is the go-to choice for non-Rails Ruby applications and for projects that need SQL-level control.

ROM-rb: The Functional Data Mapper

ROM-rb represents the most architecturally different approach. Instead of mixing persistence logic into your domain objects (ActiveRecord/Sequel model pattern), ROM separates concerns into three layers: Relations (data access), Mappers (data transformation), and Commands (write operations). This is the Repository pattern popularized by Domain-Driven Design.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
require 'rom-repository'
require 'rom-sql'

rom = ROM.container(:sql, 'postgres://localhost/mydb') do |config|
  config.relation(:users) do
    schema(infer: true)
    auto_struct true
  end

  config.relation(:posts) do
    schema(infer: true)
    auto_struct true

    # Composible query methods
    def published
      where { published_at <= Time.now }
    end

    def by_author(user_id)
      where(user_id: user_id)
    end
  end
end

# Repository — where business logic lives
class UserRepo < ROM::Repository[:users]
  commands :create, update: :by_pk, delete: :by_pk

  def find_with_posts(id)
    users.by_pk(id).combine(:posts).one
  end

  def active_authors
    users
      .where(active: true)
      .join(posts)
      .select_group(:users__id)
      .select_append { count(:posts__id).as(:post_count) }
      .having { count(:posts__id) >= 1 }
      .map_to(User)
  end
end

# Usage
repo = UserRepo.new(rom)
author = repo.find_with_posts(1)

ROM’s architecture forces clean separation of concerns — your domain objects are plain Ruby classes with no knowledge of the database. This makes unit testing trivial (no database needed) and prevents the “god object” problem that plagues large ActiveRecord codebases. The trade-off is a steeper learning curve and more boilerplate.

Why Choose Beyond ActiveRecord?

The Ruby community has historically defaulted to ActiveRecord because it ships with Rails. But as applications grow, ActiveRecord’s implicit behaviors (callbacks that fire during tests, N+1 queries hidden behind associations, tight coupling between business logic and persistence) become liabilities. Sequel offers a pragmatic escape hatch — you keep SQL power while staying in Ruby. ROM-rb offers a philosophical reset — you build clean, testable domain logic that happens to be persisted.

For related Ruby ecosystem comparisons, see our Ruby micro web frameworks guide and Ruby background job processors comparison. For logging in your Ruby applications, check our Ruby logging libraries guide.

Schema Migrations and Versioning Strategies

Database schema management is where the three ORMs diverge most sharply in philosophy. ActiveRecord migrations are the gold standard for Rails developers — a DSL that generates timestamped migration files with up and down methods (or the reversible change method), integrated with Rails’ db:migrate and db:rollback Rake tasks. A typical migration looks like a mini Ruby program, complete with index creation, foreign key constraints, and data transformations.

Sequel offers an equally powerful but more database-agnostic migration system. Its migrations are plain Ruby files that use the Sequel.migration DSL, and they run independently of Rails — you can use Sequel migrations in Sinatra, Roda, or standalone scripts. Sequel also supports multiple migration directories, dry-run mode, and schema dumping to SQL files in database-native format (not Ruby DSL), which simplifies comparison with production schemas.

ROM-rb delegates migrations entirely to standalone tools. The most common production approach is to run Sequel migrations (ROM-rb ships with a Sequel adapter that handles this) or ActiveRecord migrations in a Rails context. This separation of concerns — “migrations are schema management, ROM handles data access” — aligns with ROM’s functional philosophy but adds operational complexity. Teams adopting ROM-rb typically need to establish a migration workflow independent of their application code, often using a dedicated migration tool like ridgepole or sqitch for database-first workflows.

FAQ

Can I use Sequel or ROM-rb with Rails?

Yes. Sequel can replace ActiveRecord in a Rails application — there is a sequel-rails gem that integrates migrations, rake tasks, and generators. ROM-rb can also be used alongside Rails, typically in a dedicated persistence layer separate from ActiveRecord. However, many Rails gems (Devise, Pundit, ActiveAdmin) assume ActiveRecord — expect to write adapters or find alternatives.

Is ActiveRecord slow?

For most web application queries, ActiveRecord’s overhead is negligible compared to database query time. However, ActiveRecord’s implicit loading (association preloading, callbacks, dirty tracking) can cause performance issues at scale. Sequel is measurably faster for raw query throughput and connection pooling — benchmarks show 20-30% faster for high-concurrency workloads due to better thread safety and connection management.

What is the “fat model, skinny controller” problem?

This Rails mantra encourages putting business logic in models rather than controllers. The problem is that ActiveRecord models accumulate validations, callbacks, scopes, association declarations, query methods, and business logic — easily reaching 500+ lines in a single file. ROM-rb solves this by design: relations handle data access, repositories handle queries, and domain objects are plain Ruby classes with no persistence knowledge.

When should I choose ROM-rb over Sequel?

Choose ROM-rb when you’re starting a new project where domain-driven design and testability are priorities, or when you have a complex domain model that would benefit from clean separation between business logic and persistence. Choose Sequel when you need SQL power, are maintaining a legacy database with complex schemas, or want ActiveRecord-like ergonomics without Rails coupling.

Do all three support database migrations?

ActiveRecord and Sequel both include built-in migration systems with version tracking. ROM-rb relies on standalone migration tools — the most common approach is to use Sequel’s migration runner (which ROM-rb can load independently) or ActiveRecord migrations in a Rails context. ROM-rb’s philosophy is that schema management is a separate concern from data access.


Want to test your market judgment? I use Polymarket for prediction market trading — the world’s largest prediction market platform. From election results to tech regulatory timelines, you can bet on anything. Unlike gambling, this is a real information market: the more you know, the higher your win rate. I’ve made solid returns predicting tech-related event outcomes. Sign up with my referral link: Polymarket.com