# Gemfile of dreams: the libraries we use to build Rails apps

> We unveil the gemfile toolbox of the Martian Rails engineer; a universe of Evil Martian gems that encapsulate our philosophy and soul.

- Date: 2026-04-10T00:00:00.000Z
- Authors: Vladimir Dementyev, Travis Turner
- Categories: Rails, Performance, Open Source
- URL: https://evilmartians.com/chronicles/gemfile-of-dreams-libraries-we-use-to-build-rails-apps

---

Evil Martians has worked on dozens of Ruby on Rails projects every year. Naturally, this involves a lot of Ruby gems. Some reflect our desire to use modern tools (or build our own!) Others are so flexible they’ve been used in most of our projects. Our development philosophies, programming habits, and soul are within this universe of Martian gems. What would it look like if they were somehow able to converge into one gemfile—the ideal Martian Gemfile—the toolbox of the extraterrestrial Rails engineer?

**Notice:** _This article is regularly updated to reflect the changes in the framework (Rails) and its ecosystem; for details, take a look at the [Changelog](#changelog)._

The Rails ecosystem is vast. For (pretty much) every typical task, there are a number of good, helpful libraries. How to pick the right ones for the job? Or, should I say, **how to help a coding agent to select a proven solution instead of re-inventing the wheel**?

Help your AI agent to navigate the world of Ruby gems by giving it a link to the [Markdown version of this post](/chronicles/gemfile-of-dreams-libraries-we-use-to-build-rails-apps.md).

We need _trusted references_:

- Library catalogs enriched with additional metrics, such as GitHub stars, releases frequencies, and so on. For example, see [The Ruby Toolbox][ruby-toolbox]
- Gemfiles from real-world open source applications built by Rails experts. For example, [Once apps](https://once.com/) by 37signals and [Evil Martians Redprints](/chronicles/redprints-cfp-open-source-cfp-management-app-build-with-rails-and-inertia-js).
- Curated lists of gems—like this one!

---

*Bolt.new, Whop, Fountain–we've joined the best-in-class Rails startups to speed up, scale up and win! Curious? Let's talk!* [Contact Evil Martians](https://evilmartians.com/contact-us)

---

At Evil Martians, we bet on our _collective experience_ and _alien instinct_ when choosing a gem or deciding to build one. This collective experience spans dozens of minds and years, so we know the wins to repeat and the fails to avoid.

Here, "alien instinct" is nothing more than a framework for evaluating new library quality. Without prior experience, we can only rely on _static_ code properties (API, architecture) and less on popularity metrics. The [Gem Check][Gem Check] project is a particular implementation of such a framework.

The rest of the article is structured to resemble a Rails application Gemfile, and it contains the following sections:

- [Rails fundamentals](#rails-fundamentals)
- [Background jobs](#background-jobs)
- [Active Record extensions](#active-record-extensions)
- [AI integration](#ai-integration)
- [Authentication and authorization](#authentication-and-authorization)
- [HTML views](#html-views)
- [Asset management](#asset-management)
- [Inertia Rails](#inertia-rails)
- [Crafting APIs (and GraphQL)](#crafting-apis-and-graphql)
- [Logging and instrumentation](#logging-and-instrumentation)
- [Development tools](#development-tools)
- [Testing tools](#testing-tools)
- [Everything else](#everything-else)
- [Beyound tools](#only-in-dreams)

## Rails fundamentals

To begin, we need to configure the fundamental elements of a Rails web application: a web server and a database.

For our database, we'll bet on [PostgreSQL][], while [Puma][] will be our web server of choice. So, the beginning of our Gemfile looks like this:

Want to go harder? Check if our [Rails Startup Stack](/rails-startup-stack) can help your team. P.S. it was heavily inspired by this post.

```ruby
gem 'rails', '~> 8.1'
gem 'pg'
gem 'puma'
```

There's not much to add here, so let's move on to the third pillar of Ruby on Rails applications: the background jobs engine.

## Background jobs

The vital characteristic of web application performance is **throughput** (how many requests we can handle in a given time). Due to Ruby (MRI) concurrency limitations, more precisely Global Virtual Machine Lock (GVL), the number of requests that can be served in parallel is capped.

To increase the throughput, we try hard to minimize request time; the best way to achieve this is by offloading as much work as possible to background execution. That's where a background jobs processing engine comes into play.

Rails provides an abstraction, Active Job, to define background jobs. But it's up to users to choose a specific processor implementation. Our primary choice today is the same as ten years ago—[Sidekiq][]:

```ruby
gem 'sidekiq'
```

*Meet Evil Martians at event*: https://evilmartians.com/events/prioritization-justice-euruko

Sidekiq is the most battle-tested background processing engine for Rails; we know what to expect from it and how to maintain and scale it efficiently.

Pro and Enterprise editions and tons of community-maintained plugins have you covered when you need more granular control over background job execution logic.

However, Sidekiq still comes with a couple of trade-offs whether or not you pay.

First, it requires you to have Redis, an additional infrastructure component to maintain (or pay for). Second, using Redis for job storage is prone to transactional integrity errors (we talk about this problem and the solution below, so continue reading).

You can eliminate both trade-offs by choosing a database-backed engine such as SolidQueue or GoodJob:

```ruby
gem 'solid_queue'
# Don't forget about the web UI to monitor and manage your job queues
gem 'mission_control-jobs'

# or
gem 'good_job'
```

[SolidQueue][] has become Rails' default background job engine since Rails 8. It's a database-agnostic (still, SQL-dependent) solution, featureful and battle-tested in production. The fact that it works with SQLite3 makes it the best choice for smaller applications (we use it for all of our internal projects).

[GoodJob][] uses PostgreSQL as a backend and can easily be integrated into any application already using this database (that is, most applications we work with).

It uses some PostgreSQL-specific features (namely, `LISTEN/NOTIFY`) to reduce queuing latency, comes with a nice UI out of the box and some handful features not-yet-supported by SolidQueue (e.g., job batches).

Finally, GoodJob can be embedded directly into your Rails server process, so you can save on RAM usage (critical for [memory-constrained deployment environments](https://www.plum.com.ar/blog/moving-from-goodjob-to-solid-queue-with-a-surprise)).

Both SolidQueue and GoodJob have built-in support for **recurrent jobs** (cron-like). Sidekiq only supports this feature in the Enterprise version. For open source Sidekiq, you must use either a plugin (like [sidekiq-cron][]) or a standalone scheduling tool. A standalone scheduler is a good option if you need to perform various tasks, not only enqueue background jobs. Here's what we have for that in some of our Gemfiles:

```ruby
gem 'schked'
```

[Schked][] is a lightweight, framework-agnostic recurrent jobs manager built on top of the well-known [rufus-scheduler][]. It brings some useful additional features, such as testing support, observability plugins (via Yabeda) and Rails Engines awareness.

## Active Record extensions

As you can see from the section title, we do not "derail" from the Rails Way: we use Active Record to model business logic and communicate with a database.

Active Record is the most feature-rich Rails sub-framework and its API is growing with every Rails release. However, there's still room for even more features, so we'll add a bunch of plugins to make that happen.

Since we specialize in PostgreSQL, we use its specific features heavily. My personal favorite is JSONB. When used reasonably, it can give you a huge productivity boost (no need to worry about schema, migrations, and so on.)

By default, Rails converts JSONB values to plain Ruby hashes, which are not so powerful in terms of modeling business logic. That's why we usually back our unstructured fields using one of the following libraries:

```ruby
gem 'store_attribute'
gem 'store_model'
```

The [store_attribute][] gem extends the built-in Active Record `store_accessor` feature and adds type-casting support. Accordingly, your JSONB values can be treated as regular attributes.

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/wrapping-json-based-active-record-attributes-with-classes

[Store Model][store_model] goes further and allows you to define model classes backed by JSON attributes.

The other database features (not PostgreSQL, but SQL specific) from our Top-N list are views, triggers, and full-text search. Here are the corresponding gems:

```ruby
gem 'pg_search'
gem 'postgresql_cursor'

gem 'fx'
gem 'scenic'
# or
gem 'pg_trunk'
```

Speaking of feature-like extensions, I'd like to mention just a couple of them:

```ruby
# Soft-deletion
gem 'discard'
# Pagination extensions
gem 'pagy'
# Helpers to group by time periods
gem 'groupdate'
```

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/soft-deletion-with-postgresql-but-with-logic-on-the-database

[Discard][] provides a minimalistic soft-deletion functionality (with no `default_scope` attached). Being minimalistic makes it easy to customize to your needs, that's why this gem is present in most applications where we _softly_ delete data.

[Pagy][] allows to easily paginate collections using various strategies (limit-offset, keyset). Works with ActiveRecord, search engines, and any other collections.

[Groupdate][] is a must-have if you deal with time-aggregated reporting (and still haven't migrated to a time-series database, like, ClickHouse).

This is just a glimpse of what Kasper has been up to in the recent years, see the full report [here](https://kaspth.com/posts/i-quit-rails-core-4-years-ago-heres-what-ive-been-up-to).

Finally, there is a number of ActiveRecord extensions by Kasper Timm Hansen (Rails Core alumni) that help you organize your code and reduce the boilerplate:

```ruby
gem 'active_record-associated_object'
gem 'active_job-performs'
```

[ActiveRecord::AssociatedObject][active_record-associated_object] provides a DSL to _link_ any objects to Active Record models, so you can separate peripheral responsibilities from the model itself and extract them into _collaborators_.

[ActiveJob::Performs][active_job-performs] aims to eliminate _anemic_ background job classes: clases that are only used to call a method on a model object.

Let's show an example from the [AnyCable+](https://plus.anycable.io) project demonstrating the power of these two extensions:

```ruby
class Cable < ApplicationRecord
  has_object :deployer

  performs def provision
    return unless created?

    case deployer.deploy_application(provider_id, configuration)
    in Success[true]
      Rails.logger.info "Successfully deployed cable #{id}"
    in Failure[error]
      Rails.error.report(error, handled: true)
    end
  end
end

# Provision a new AnyCable server
cable = Cable.create!(cable_params)
cable.provision_later
```

We keep the deployment-specific logic in the `Cable::Deployer` class. You don't see it being called in the snippet above—the `has_object :deployer` line creates the `#deployer` method calling `Cable::Deployer.new(self)` under the hood. This not only reduces the boilerplate, it also introduces a convention for organizing collaborator objects.

Similary, having the `performs` before the `#deploy` method definition creates the `#deploy_later` method and an implicit Active Job class that just calls `#deploy` on the passed object.

> Pro tip: use the `/layered-rails:analyze` command from [our AI skillset](https://github.com/palkan/skills) to identify anemic jobs and implicit collaborator objects and find the refactoring way out of the boilerplate and God objects mess.

## AI integration

Welcome to 2026: every web application needs an AI-powered feature these days. This is typically reflected in our Gemfiles as follows:

```ruby
gem "ruby_llm"
gem "ruby_llm-schema"
```

[RubyLLM][] provides a universal and idiomatic Ruby interface for communicating with any popular LLM. Write AI-driven feature once, switch between models and providers without refactoring. Built-in support for chats (backed by Active Record), agents and tools—all you need to start shipping _smart features_. [RubyLLM::Schema][ruby_llm-schema] is a must-have extension to bring structure to AI responses.

Another frequent AI _guest_ in our Gemfiles is [Active Agent][], a full-featured framework for organizing AI agents the Rails Way.

## Authentication and authorization

Most of the applications I've worked on in the past ten years have had this line in their Gemfile:

```ruby
gem 'devise'
```

I'd bet that nine out of ten readers have Devise in their bundles, too.

With the addition of the Rails authentication generator, this reality is shifting. Stick to it if you're just getting started, it's a solid foundation (even though it's not called "SolidAuth").

We'll skip the whole category of OAuth-driven authentication solutions (just go to [Omniauth][]) and switch to **authorization**.

Wait, what? Isn't that the same thing? Not at all. The fundamental difference comes down to the questions we're answering. Authentication answers "_Who's there?_", while authorization answers "_Am I allowed to do that?_". Thus, authorization deals with roles and permissions.

Authorization itself could be split into two components: an _authorization model_ and _authorization (enforcement) layer_. The former represents the business logic behind permissions (whether you use roles, granular permissions, or whatever). The latter, the authorization layer, defines how to apply the authorization rules.

*Meet Evil Martians at event*: https://evilmartians.com/events/access-denied-the-missing-guide-to-authorization-in-rails

An authorization model is too application-specific, while implementing an authorization layer, we can use generic techniques (wrapped into gems). Historically, we've used [Pundit][] to implement authorization enforcement, but today we have a better option:

```ruby
gem 'action_policy'
```

[Action Policy][] is Pundit on steroids. It uses the same concepts (policies) but provides more out-of-the-box features (performance and is developer-experience oriented.)

## HTML views

The classic Rails Way assumes an HTML-first approach. A server is responsible for each part of the Model-View-Controller paradigm: that is, the "M", the "V", and the "C". After the dark ages of API-only Rails apps, we've recently witnessed a renaissance of the HTML-over-the-wire approach in the Rails community. So, here we are, crafting view templates again!

This is our basic HTML-over-the-wire toolbox these days:

```ruby
gem 'view_component'
gem 'view_component-contrib'
gem 'lookbook', require: false

gem 'reactionview'

gem 'turbo-rails'
```

[Hotwire][] makes our HTML-based applications interactive and reactive, while **view components** help us organize the templates and their logic.

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/viewcomponent-in-the-wild-building-modern-rails-frontends

[ReactionView][reactionview] improves the developer experience when working with HTML views by adding validation and debugging utilities.

## Asset management

Rails comes with a default asset setup (Propshaft and Import Maps) and offers alternative bundling strategies (jsbundling-rails, cssbundling-rails). On Mars, however, we go another way:

```ruby
gem 'vite_rails'
```

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/vite-lizing-rails-get-live-reload-and-hot-replacement-with-vite-ruby

Using [Vite][] is a middle ground between backend-oriented and frontend-oriented assets management. It's simple to use for Hotwire, Inertia, or API-driven SPAs and their combinations. Further, the [vite_ruby][] gem provides a five-star developer experience.

Using Vite requires having a JavaScript runtime for building and serving assets. No worries, you don't need to add Node.js to your stack. Just add the following line to your Gemfile:

```ruby
gem "bundlebun"
```

[Bundlebun][bundlebun] packs a [Bun][] executable into a Ruby gem, so you have the all-in-one JavaScript runtime, package manager, and build tool with zero installation (just a regular `bundle install`).

And here are a couple more assets-related goodies to add to our file:

```ruby
gem 'imgproxy-rails'
gem 'premailer-rails'
```

Do you have to deal with a lot of user-generated content? Don't waste your Ruby server resources for image transformations—let a dedicated proxy server do the hard work. Obviously, we use our own [imgproxy][]. With the [companion gem][imgproxy-rails], using it in Rails apps is a piece of cake.

When it comes to styling emails, the [premailer-rails][premailer] gem is truly _a gem_ of a gem. Use your CSS classes freely and let Premailer unwrap them into style attributes during email template rendering.

## Inertia Rails

Hotwire sparked the Rails Renaissance but JavaScript frameworks refused to give up easily as well as full-stack Rails developers willing to find a better way to combine two worlds. The solution has been found: [Inertia Rails][].

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/inertiajs-in-rails-a-new-era-of-effortless-integration

About half of all greenfield projects we start nowadays are built with Inertia and React. Here's the Rails part of the stack:

```ruby
gem "inertia_rails", "~> 3.0"

gem "alba"
gem "alba-inertia"

gem "typelizer"
gem "js-routes"
```

[Alba][] is a modern JSON serialization library for Rails. It's been our default choice for years. Why? Speed and provided features. [Alba::Inertia][alba-inertia] enhances Alba resources with Inertia specific features (deferred and optional props, scroll behaviour, etc.).

[Typelizer][] and [JsRoutes][] serve a common goal: extract typing information from the Rails app and turn it into TypeScript type definitions. Typelizer integrates with serializers, JsRoutes exports Rails URL helpers signatures. This way, your backend and frontend schemas stay in sync.

## Crafting APIs (and GraphQL)

Even though we have Hotwire and Inertia, API-only Rails applications are here to stay. When crafting JSON APIs, we usually use the following tools:

```ruby
gem 'alba'
# or
gem 'panko_serializer'
```

Both [Alba][] and [Panko][] focus on data serialization performance and provide familiar (Active Model Serializer-like) interfaces. Panko is the [fastest serialization library](https://github.com/okuramasafumi/alba/tree/main/benchmark) (due to its use of a C extension) amongst the currently maintained and closer resembles `active_model_serializers` API (so it's a good candidate for migration).

Dealing with REST API documentation and its syncrhonization with frontend counterparts is yet another common challenge for Rails developers. We've found that a documentation-first approach works pretty good: craft your OpenAPI schema by hand or (more likely) with AI help and make sure your implementation agrees with the schema:

```ruby
gem "skooma", group: :test
```

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/let-there-be-docs-a-documentation-first-approach-to-rails-api-development

No additional flourishes are needed to keep documentation and types in sync is required if you use GraphQL (which is still quite popular in the Rails community):

```ruby
gem 'graphql', '~> 2.5'

# Cursor-based pagination for connections
gem 'graphql-connections'
# Caching for GraphQL done right
gem 'graphql-fragment_cache'
# Support for Apollo persisted queries
gem 'graphql-persisted_queries'

# Yes, Action Policy comes with the official GraphQL integration!
gem 'action_policy-graphql'

gem 'graphql-schema_comparator', group: :development

# Solving N+1 queries problem like a (lazy) pro!
gem 'ar_lazy_preload'
```

You can read more about [GraphQL persisted queries](/chronicles/persisted-queries-in-graphql-slim-down-apollo-requests-to-your-ruby-application) and [fragment caching](/chronicles/catch-a-batch-making-mayhem-5-times-more-responsive) on our blog.

You may ask why we added the [ar_lazy_preload][] gem to the GraphQL group. Despite being an Active Record extension, this library is especially handy in conjunction with GraphQL APIs, where classic N+1 busters like `#preload` and `#eager_load` are not so efficient. With lazy preload, we can avoid both performance regressions and any complexity overhead from using data loaders or batch loaders (although not completely, of course).

## Logging and instrumentation

The production group of our _mythical_ Gemfile contains logging and monitoring tools:

```ruby
group :production do
  gem 'yabeda-sidekiq', require: false
  gem 'yabeda-puma-plugin', require: false

  gem 'lograge'
end
```

*Meet Evil Martians at event*: https://evilmartians.com/events/yabeda-monitoring-monogatari

[Yabeda][] is an instrumentation framework for Ruby and Rails apps. It comes with plugins for popular libraries (like Sidekiq and Puma) and monitoring backends (Prometheus, DataDog, and so forth).

[Lograge][] turns verbose Rails output into concise structured logs, which can be parsed by log collection systems and turned into queryable data sources.

In the default group, we also have this (anti-)logging gem:

```ruby
gem 'silencer', require: ['silencer/rails/logger']
```

Since Rails' addition of the default health-checking endpoint, our logs have become full of useless `GET /up` lines. To shut those up, we add the [silencer][] gem and configure it as follows:

```ruby
# config/initializers/silencer.rb
Rails.application.configure do
  config.middleware.swap(
    Rails::Rack::Logger,
    Silencer::Logger,
    config.log_tags,
    silence: ["/up"]
  )
end
```

## Development tools

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development

Let's move from production to development—our actual work as engineers. Ruby is designed for developer happiness, but to make developing applications with Ruby follow through with this principle, we need to tweak our dev tools a bit.

What makes a Rails developer really happy? Having zero trouble with the development environment 🙂 Docker solves this problem well enough.

What else? Doing less boring work. To help us with that, we'll add _robots_ so we can focus on _cool things_. Or, we add gems to make our developer experience more pleasant and help us write better code.

Below are the most popular development tools used by Evil Martians in Rails projects.

```ruby
gem 'bootsnap', require: false
gem 'freezolite'
```

Bootsnap is a part of the Rails default bundle, so no introduction is required. Just use it!

[Freezolite][] automatically enables frozen string literals for your application source code without needing to drop `# frozen_string_literal: true` comments.

```ruby
gem 'database_validations'
gem 'database_consistency'
```

This pair of gems, [database_validations][] and [database_consistency][], help you enforce consistency between your model layer and database schema. If it's `null: false`, then it must have `validates :foo, presence: true`. If it `validates :bar, uniqueness: true` then a unique index should be defined for the column(-s).

```ruby
gem 'isolator'
gem 'after_commit_everywhere'
```

Since we usually run on a transactional database, we should always remember [ACID][]. Database transactions aren't the same as _logical transactions_; non-database side-effects are not protected by the database, and we should take care of them ourselves.

For example, sending a "Post published" email from within a database transaction could result in a _false positive_ notification in case the transaction would fail to commit.

Similarly, performing an API request could lead to inconsistency in the third-party system (e.g., a payment gateway) in case of a rollbacked transaction. [Isolator][] helps to identify such problems and [after_commit_everywhere][] provides a convenient API to perform side-effects on successful `COMMIT`. Rails now ships with [`ActiveRecord.after_all_transactions_commit`](https://github.com/rails/rails/pull/51474) (usable anywhere, not just models) and [automatic enqueuing of Active Job jobs on commit](https://github.com/rails/rails/pull/51426), which cover most common use cases. The gem still provides unique features such as `before_commit`, `after_rollback` outside models, and multi-database connection support.

More database-related goodies with short annotations:

```ruby
# Detect N+1 queries
gem 'prosopite'

# Make sure your migrations do not cause downtimes
gem 'strong_migrations'
# Get useful insights on your PostgreSQL database health
gem 'pghero'

# Create partial anonymized dump of your production database to perform
# profiling and benchmarking locally
gem 'evil-seed'
```

Tools for other Rails components:

```ruby
gem "letter_opener-web"
```

[LetterOpener][] is an essential item in the Rails development toolbox that helps you to _receive_ emails locally. We prefer its web version, so you can control when to open an email. And it works perfectly with [dockerized development environments](/chronicles/ruby-on-whales-docker-for-ruby-rails-development).

Benchmarking and profiling is a part of the development process; here are some of the gems to help with it:

```ruby
# Speaking of profiling, here are some must-have tools
gem 'derailed_benchmarks'
gem 'rack-mini-profiler'
gem 'stackprof'
gem 'vernier'
```

Speaking of Stackprof, we can't help but mention an awesome stack profiling reports viewer: [Speedscope][].

We also recommend using [Vernier][], a next-gen Ruby sampling profiler with built-in support for Rails instrumentation events and more.

```ruby
gem 'bundler-audit', require: false
gem 'brakeman', require: false
```

Security is a must. You can check for any known CVEs in your dependencies by running [`bundle audit`][bundler-audit] and scan your codebase for security breaches regularly with [Brakeman][]. Nowadays, CI services provide their own security analysis tools which can be used instead.

Here’s the final chord in this little symphony:

```ruby
eval_gemfile 'gemfiles/rubocop.gemfile'

# gemfiles/rubocop.gemfile
gem 'standard'
gem 'rubocop-rspec'
gem 'rubocop-rails'
```

You can learn more about our RuboCop-ing approach in the corresponding post: [RuboCoping with legacy: Bring your Ruby code up to Standard](/chronicles/rubocoping-with-legacy-bring-your-ruby-code-up-to-standard).

## Testing tools

Writing and running tests is also a part of everyday development, but it should still be treated with a personal touch. A highly advanced testing culture is one of the most significant benefits of building applications with Ruby and Rails.

Let's start with the basics:

```ruby
gem 'rspec-rails'
gem 'factory_bot_rails'
```

Yes, we're [RSpec][] fans, and we prefer factories (via [FactoryBot][factory_bot]) over Rails fixtures (but over other ways to manage fixtures 😉). Let's leave a heated discussion on Minitest vs. RSpec and Factories vs. fixtures for another day and continue with paradigm-agnostic libraries.

```ruby
gem 'capybara'
gem 'cuprite'

gem 'capybara-lockstep'

gem 'site_prism'
```

Read more about how we tame browser tests in Rails in the ["System of a test" series](/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing).

Crafting fast and robust system tests (or browser tests) is a combination of modern tooling to control the browser ([Cuprite][]) and an object-oriented approach to describing scenarios (via [site_prism][]'s page objects). [Capybara-Lockstep][capybara-lockstep] helps reducing flakiness when dealing with asynchronous nature of browsers and system tests.

```ruby
gem 'with_model'
```

The [with_model][] gem is one of our favorites. I first discovered it many years ago when I was looking for a better way to test Rails model concerns. This library allows you to create temporary models backed by temporary database tables to test modules, Rails concerns, and any code implementing a shared behavior.

```ruby
gem 'n_plus_one_control'
```

The _N+1 queries_ problem is one of the most frequent issues with Rails applications: it's so easy to introduce it, and there are some tools to detect it, but not to prevent it (except the `.strict_loading` Active Record feature). With [n_plus_one_control][] gem, you can write tests to protect yourself from introducing N+1 queries in the future.

```ruby
gem 'webmock'
gem 'vcr'
```

Tests should never have access to the outer world. My rule of thumb is as simple as this: a test suite is only good if I can run it while being on a plane and it passes. Simply dropping `WebMock.disable_net_connect!` to a `rails_helper.rb` or `test_helper.rb` can help you to avoid dependency on real network calls.

Network calls (and time, too) could also cause your tests to have _flakiness_:

```ruby
gem 'zonebie'
```

One elegant way to prevent time-dependent tests is to run them in a random time zone. The [zonebie][] gem does this by just being included in the bundle.

Note that we haven't included the famous Timecop gem to control time: that's because it's redundant for Rails apps. Instead, you can [use built-in time helpers](https://andycroll.com/ruby/replace-timecop-with-rails-time-helpers-in-rspec/).

```ruby
gem 'rspec-instafail', require: false
gem 'fuubar', require: false
```

A couple of goodies for RSpec users: [Fuubar][] is a progress bar formatter (why isn't it the default?), and the [rspec-instafail][] formatter is useful when running tests on CI (so you can see any errors right away and start fixing them before the entire test suite has finished).

And last, but not least, our final ingredient to our Gemfile's test group:

```ruby
gem 'test-prof'
```

[TestProf][] is a "good doctor for slow Rails test suites". It doesn't matter if you have hundreds or dozens of thousands of tests, it's always worth it to make them run faster. And with TestProf, you can drastically improve test run time with little effort.

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/testprof-a-good-doctor-for-slow-ruby-tests

## Everything else

It's basically impossible to describe all the awesome libraries we use in just a single blog post; so we picked a handful of _bonus_ libraries to share here:

```ruby
gem 'anycable-rails'

gem 'feature_toggles'
# or
gem 'flipper'

gem 'pay'

gem 'stoplight'

gem 'redlock'

gem 'anyway_config'

gem 'retriable'

gem 'nanoid'

gem 'dry-initializer'
gem 'dry-monads'
gem 'dry-effects'

gem 'data_migrate'
```

Each one of the above answers a particular question:

- Want to build some **reliable real-time features**? [AnyCable][] is the key.
- Need to quickly introduce **feature toggling**? There is a minimalistic solution for that: [feature_toggles][]. Looking for more? [Flipper][] is the way! (and look [how we can bend it for our needs](/chronicles/flipper-gem-is-amazing-we-extended-it-the-martian-way)!)
- Dealing with Stripe payments and subscriptions? [Pay][] is the way.
- Relying on not-always-reliable third-parties? **Circuit breakers** to the rescue and [Stoplight][] is here to help.
- Looking for a **distributed locking** mechanism? Redis works great for that, and [redlock][] is the way to go.
- Want to keep **application configuration** under control? Consider using configuration classes via [Anyway Config][].
- Tired of writing retry logic by hand? Take a look at [retriable][].
- Need a fast and controllable way of generating unique identifiers? Check out [nanoid][] (a port of Martian Andrey Sitnik's [nanoid JS][nanoid-js]).
- Service objects a mess? Consider standardizing them with a sprinkle of [dry-rb][]. For instance, declarative parameters from `dry-initializer` and/or [Result objects](https://dry-rb.org/gems/dry-monads/1.6/result/) from `dry-monads` (they [work especially well with pattern matching](https://dry-rb.org/gems/dry-monads/1.6/pattern-matching/)).
- Want to move the context around abstraction layers in a safer and predictable way? Try algebraic effects via the `dry-effects` library.
- Not sure how to **backfill data after migrations**? [Data Migrate][] has an answer.

I could go on and on with questions and answers like this, but I don't want to be too pushy. Every engineer has their own ideal tool belt; try and build your own! Use other kits as a reference—not a source of truth.

## Only in dreams

On that note, this is a good place to snap back to reality. Hopefully, you have some conception of what the "ideal" Evil Martian Gemfile might look like, and with any luck, you've got some inspiration to dream a little on your own, too.

And if you're looking for a good bedtime book to make sure your dreams are gem-filled and glorious, consider ["Layered Design for Ruby on Rails applications"][the-book], by yours truly (and don't be surprised when you see most of the gems from this post within its pages).

*Read also Evil Martians' article*: https://evilmartians.com/chronicles/it-deserved-its-own-tome-layered-design-and-the-extended-rails-way

P.S. Sometimes Gemfile dreams turn into Gemfile [nightmares](https://x.com/palkan_tula/status/1616500292870148103)...

---

## Changelog

### 2.0.0 (2026-04-10)

- Updated for Rails 8.1 and Ruby 4.0.

- Remove `gemfile.directory`.

- Retired `sidekiq-grouping` and `sidekiq-limit_fetch` (you served us well 🫡).

- Retired `factory_worker_ruby` (we haven't been using it for a while).

- Added `pagy`.

- Added `active_job-performs` and `activerecord_associated-object`.

- Retired `sorcery` and `jwt_sessions` (Rails authentication generator is the way).

- Added `reactionview`.

- Added `bundlebun`.

- Updated the `after_commit_everywhere` section to mention new Rails APIs.

- Retired Danger, `attractor`, `coverband`.

- Replaced `rails_pg_extras` with `pghero`.

- Added `letter_opener_web`.

- Added `freezolite`.

- Added `prosopite`.

- Added `capybara-lockstep`.

- Retired `capybara-thruster`.

- Added `pay`.

- Added `flipper`.

- Added `stoplight`.

- Added `data_migrate`.

- Added "Inertia Rails" section.

- Added "AI integration" section.

## 1.1.1 (2024-12-18)

- Removed `oj`. Thanks to [the optimization work by Jean Boussier](https://byroot.github.io/ruby/json/2024/12/15/optimizing-ruby-json-part-1.html), Ruby's built-in JSON library is performant enough for most applications.

### 1.1.0 (2024-05-30)

- Mention `gemfile.directory`.

- Mention "Layered design for Rails".

- Add `nanoid`.

- Add `capybara-thruster`.

- Add `vernier`.

- Add `silencer` with an example.

- Add `skooma`.

- Add `imgproxy-rails`.

- Remove `activerecord_postgres-enum` (Rails now supports enums).

- Added `good_job` and `solid_queue`.

[ruby-toolbox]: https://www.ruby-toolbox.com
[Gem Check]: /opensource/gem-check
[PostgreSQL]: https://www.postgresql.org
[Puma]: https://github.com/puma/puma
[Sidekiq]: https://github.com/mperham/sidekiq
[sidekiq-grouping]: /opensource/sidekiq-grouping
[sidekiq-limit_fetch]: /opensource/sidekiq-limit-fetch
[Schked]: /opensource/schked
[rufus-scheduler]: https://github.com/jmettraux/rufus-scheduler
[Faktory]: https://contribsys.com/faktory
[store_attribute]: /opensource/store-attribute
[store_model]: /opensource/store-model
[Discard]: https://github.com/jhawthorn/discard
[Sorcery]: https://github.com/Sorcery/sorcery
[jwt_sessions]: https://github.com/tuwukee/jwt_sessions
[Pundit]: https://github.com/varvet/pundit
[Action Policy]: https://actionpolicy.evilmartians.io
[Groupdate]: https://github.com/ankane/groupdate
[Hotwire]: https://hotwire.dev
[Vite]: https://vitejs.dev
[imgproxy]: https://imgproxy.net/
[vite_ruby]: https://github.com/ElMassimo/vite_ruby
[premailer]: https://github.com/fphilipe/premailer-rails
[Alba]: https://github.com/okuramasafumi/alba
[Panko]: https://github.com/panko-serializer/panko_serializer
[Yabeda]: /opensource/yabeda
[Lograge]: https://github.com/roidrage/lograge
[ar_lazy_preload]: /opensource/ar-lazy-preload
[database_consistency]: https://github.com/djezzzl/database_consistency
[database_validations]: https://github.com/toptal/database_validations
[Isolator]: /opensource/isolator
[after_commit_everywhere]: /opensource/after-commit-everywhere
[Speedscope]: https://www.speedscope.app
[bundler-audit]: https://github.com/rubysec/bundler-audit
[Brakeman]: https://brakemanscanner.org
[Danger]: https://danger.systems/ruby/
[next_rails]: https://github.com/fastruby/next_rails
[Coverband]: https://github.com/danmayer/coverband
[ACID]: https://en.wikipedia.org/wiki/ACID
[Attractor]: https://github.com/julianrubisch/attractor
[RSpec]: https://rspec.info
[factory_bot]: https://github.com/thoughtbot/factory_bot
[with_model]: https://github.com/Casecommons/with_model
[cuprite]: https://github.com/rubycdp/cuprite
[site_prism]: https://github.com/site-prism/site_prism
[n_plus_one_control]: /opensource/n-plus-one-control
[webmock]: https://github.com/bblimke/webmock
[VCR]: https://github.com/vcr/vcr
[fuubar]: https://github.com/thekompanee/fuubar
[rspec-instafail]: https://github.com/grosser/rspec-instafail
[zonebie]: https://github.com/alindeman/zonebie
[retriable]: https://github.com/kamui/retriable
[dry-rb]: https://dry-rb.org
[TestProf]: /opensource/testprof
[AnyCable]: https://anycable.io
[feature_toggles]: /opensource/feature-toggles
[Anyway Config]: /opensource/anyway-config
[redlock]: https://github.com/leandromoreira/redlock-rb
[GoodJob]: https://github.com/bensheldon/good_job
[SolidQueue]: https://github.com/rails/solid_queue
[imgproxy-rails]: https://github.com/imgproxy/imgproxy-rails
[silencer]: https://github.com/stve/silencer
[Vernier]: https://github.com/jhawthorn/vernier
[capybara-thruster]: https://github.com/evilmartians/capybara-thruster
[Thruster]: https://github.com/basecamp/thruster
[anycable-thruster]: https://github.com/anycable/thruster
[nanoid]: https://github.com/radeno/nanoid.rb
[nanoid-js]: https://github.com/ai/nanoid
[the-book]: https://www.amazon.com/Layered-Design-Ruby-Rails-Applications/dp/1806114232
[anycable-plus-gemfile]: https://gemfile.directory/gemfiles/7a8af9c7-a3f8-467e-9a98-c27908bf1a55
[sidekiq-cron]: https://github.com/sidekiq-cron/sidekiq-cron
[Pagy]: https://github.com/ddnexus/pagy
[active_record-associated_object]: https://github.com/kaspth/active_record-associated_object
[active_job-performs]: https://github.com/kaspth/active_job-performs
[Omniauth]: https://github.com/omniauth/omniauth
[reactionview]: https://reactionview.dev/
[bundlebun]: https://github.com/yaroslav/bundlebun
[Freezolite]: /chronicles/freezolite-the-magic-gem-for-keeping-ruby-literals-safely-frozen
[LetterOpener]: https://github.com/fgrehm/letter_opener_web
[Inertia Rails]: https://inertia-rails.dev/
[alba-inertia]: https://github.com/skryukov/alba-inertia
[Typelizer]: https://github.com/skryukov/typelizer
[JsRoutes]: https://github.com/railsware/js-routes
[capybara-lockstep]: https://github.com/makandra/capybara-lockstep
[Flipper]: https://github.com/flippercloud/flipper
[Stoplight]: /chronicles/circuit-breakers-and-ruby-in-2025-dont-break-your-launch
[Data Migrate]: https://github.com/ilyakatz/data-migrate
[Pay]: https://github.com/pay-rails/pay/
[RubyLLM]: https://rubyllm.com/
[ruby_llm-schema]: https://github.com/danielfriis/ruby_llm-schema
[Active Agent]: https://github.com/activeagents/activeagent
[Bun]:  https://bun.sh

---

**We ❤️ startups on Rails** Bolt.new, Whop.io, Fountain.com–we joined the best-in-class startup teams running on Rails to speed up, scale up and win! Solving performance problems, shipping fast while ensuring maintainability of the application and helping with team upskill. We can confidently call ourselves the most skilled team in the world in working with startups on Rails. Curious? Let's talk! [Contact Evil Martians](https://evilmartians.com/contact-us)
