The Rails Gems We Actually Ship: Redwerk’s Production Gemfile

Need authentication, background processing, PDF export, or Stripe billing? Someone has almost certainly published a gem for it, and that is exactly why developers reach for Rails gems so often. It is also why a Gemfile can turn into a maintenance bill nobody signed up for.

The scale of the ecosystem explains the affection. RubyGems.org, the official registry for Ruby on Rails gems and every other Ruby package, currently hosts 193,910 gems, 244,700 registered users, and nearly 255 billion downloads.

Of the ten most popular Ruby gems of all time, the leaders are bundler and a cluster of AWS SDK gems, but three core Rails libraries make the list too, with i18n, activesupport, and rake each past 1.3 billion downloads.

Add one line to your Gemfile, run bundle install, and you inherit thousands of hours of work from the people who maintain those Ruby libraries. This article is the honest version of how we treat that inheritance at Redwerk: the gems we run in production when we build and maintain Ruby on Rails applications for clients, the ones we cut, and the rubric that decides which is which.

Every Rails Gem Is Also a Liability

A Rails gem is a packaged library of Ruby code you drop into a project to add a feature without building it yourself. Every gem in your Gemfile is code you depend on but did not write, maintained by people you have never met, and capable of breaking, going stale, or turning hostile. That is not paranoia. It is the documented reality of modern software, where reuse is the default and the attack surface has grown right along with it.

The data backs that up. In August 2025, researchers uncovered a campaign of 60 malicious gems posing as social media automation tools, active since at least March 2023 and downloaded more than 275,000 times before they were pulled. Keeping the registry itself alive is not free either, since Ruby Central estimates that running RubyGems.org, including servers, infrastructure, and paid maintainers, costs around $500,000 a month. None of this means gems are bad. It means each one is a decision, and decisions deserve a process.

How We Decide Whether a Gem Earns Its Place

Most write-ups gesture at instinct here, but instinct does not transfer across a team or explain itself to a client. Before a gem goes into a Gemfile, we run it through a short checklist. The goal is not to minimize gem count for its own sake. It is to make sure every dependency is paying rent.

  • Maintenance signals: release cadence, the ratio of open to closed issues, and bus-factor risk. A clever gem with one maintainer and a year of silence is still a risk.
  • Weight and transitive footprint: a gem pulls in its own dependencies, and you inherit all of them, so we check what arrives downstream before committing.
  • The does-Rails-already-do-this test: we bias toward boring, vanilla Rails, and if the framework covers it, we use the framework.
  • Reversibility: if the gem is abandoned tomorrow, how hard is it to rip out? We price lock-in up front.
  • Licensing and budget: free versus paid tiers matter when a client is watching spend.
  • Security posture: signed releases, a responsive maintainer, and a clean history.
  • Fit with the team: the developers who inherit the app have to live with our choices.

Our working rule is simple: if no one on the team can explain why a gem is in the Gemfile, it should not be there.

There Is No Single Correct Gemfile

The right gem list depends on the project in front of you, which is something generic listicles never admit. An agency runs into this constantly, because we move between fresh builds, decade-old codebases, and everything between. There is no universal Ruby on Rails gems list that fits every situation.

  • On greenfield builds we lean on modern Rails defaults and minimal extra infrastructure.
  • On inherited or legacy apps we audit before we add, minimize churn, and respect the patterns already there.
  • For budget-conscious clients we avoid paid gem tiers and extra services when a database-backed option does the job.
  • For compliance-heavy clients we tighten security tooling and keep upgrade policies conservative.

Same framework, different Gemfile, depending on the project.

The Foundation: Server, Database, and Background Jobs

Some choices are not worth debating. For the web server we run Puma, and for the database we run PostgreSQL, and on a typical client build neither decision generates much discussion. The genuinely interesting call lives in background jobs.

With Rails 8, background processing became a real fork in the road. Active Job is the framework’s queuing abstraction, and the practical question is which adapter sits behind it. The activejob vs sidekiq vs solid queue decision usually comes down to infrastructure and scale rather than syntax.

The solid queue vs sidekiq tradeoff is the heart of it. Solid Queue ships as the Rails 8 default and stores jobs in your existing database using a Postgres and MySQL locking feature called FOR UPDATE SKIP LOCKED, so it needs no Redis and no extra service. That buys you transactional integrity, where a job and the data it depends on commit together, plus simpler operations. Sidekiq is Redis-backed, battle-tested, and still the throughput champion once you push past a few thousand jobs a minute. Run the comparison the other way and the sidekiq vs solid queue logic is identical: you weigh the extra infrastructure cost and the transactional risk of a separate store against raw speed.

For most client apps processing under a thousand jobs a minute, Solid Queue is the right default and one less thing to operate. When a product truly runs at scale, Redis earns its keep. When we rebuilt the PlusPlus Slack bot, the original Python version buckled under load, so we re-engineered it on Ruby on Rails with Redis, PostgreSQL, and AWS, and it now handles over a million user actions per minute. That is the scale where the heavier infrastructure clearly pays for itself.

The Data Layer: Active Record Extensions

The gems in Ruby on Rails that touch your data layer deserve extra scrutiny, because they sit close to everything. Most apps need the same handful of features: pagination, soft deletion, full-text search, time-based grouping, and JSONB modeling. For years that meant reaching for a gem by reflex.

We pair each need with a candidate. Pagy is light and fast for pagination. Pg_search leans on PostgreSQL rather than bolting on an Elasticsearch cluster you do not need yet. Groupdate saves real time when you group records by day, week, or month.

You probably do not need a gem for this, though. Soft deletion is the classic example, since a deleted_at timestamp and a default scope handle most cases without a dependency, and skipping the gem avoids the subtle bugs that soft-delete libraries are known for. Modern Active Record also models JSONB columns natively, so reach for an extension only when your querying genuinely outgrows the built-ins.

Authentication and Authorization

Readers conflate these two constantly, so it helps to separate them cleanly. Authentication is proving who a user is. Authorization is deciding what that user is allowed to do once they are in. They call for different tools, and the landscape for the first one shifted recently.

Rails 8 shipped a built-in authentication generator that scaffolds sessions, password handling, and reset flows without a gem. For a straightforward client build, that native generator is now our starting point, and it removes a heavyweight dependency we used to add on autopilot. Devise still earns its place when a project needs its mature ecosystem of extensions, such as confirmable accounts, lockable logins, or multiple OmniAuth providers, where rebuilding by hand would cost more than the dependency.

For authorization we standardize on Pundit, which keeps permission logic in plain Ruby policy objects a new developer can read in minutes. Clear, testable policies matter far more on an app we hand back to a client team than any clever query language.

Frontend and Views

The frontend is the real fork in the road, and we choose based on the client’s team and product, not fashion. Two genuinely good paths exist, and the wrong one creates years of friction. Picking well here is mostly about who maintains the app next.

Hotwire keeps you inside Rails, ships less JavaScript, and suits content-driven and CRUD-heavy apps that a Rails team will own. Inertia with React makes sense when the product is highly interactive, or when the client already has React developers who will carry the front end.

For view structure we use ViewComponent to keep markup testable and reusable, which pays off for whoever inherits the codebase. On the asset pipeline, Rails 8 favors Propshaft over the older Sprockets setup, and for most new builds we follow that default. The guiding principle is maintainability for the team that lives with the app after we step away.

Crafting APIs

Most client apps expose an API at some point, and the choices there trade familiarity against performance. The defaults are fine until they are not, and knowing when to switch is the skill. We try not to optimize for a problem the app does not have yet.

For JSON serialization we usually start with Active Model Serializers or Blueprinter, and we move to a faster option like Alba only when profiling shows serialization is the actual bottleneck. We treat REST APIs as documentation-first, generating an OpenAPI spec so the client’s other teams can integrate without reverse-engineering our controllers. That discipline showed its value on Cleanagents, an on-demand app for self-employed cleaners across Germany and Austria, where the existing platform exposed no APIs at all. We built RESTful APIs in Rails to talk to the Android app, used the Geocoder gem to resolve order coordinates, and pushed that geocoding into a background process so the app stayed responsive.

GraphQL has a real place when a client has many consumers with different data needs, such as several frontends hitting one backend. For a single app with predictable endpoints, it usually adds schema complexity and caching headaches a plain REST API would have avoided. We recommend it when the product calls for it, and we talk clients out of it when it does not.

AI Features: The 2026 Reality

Plenty of products now want a language model somewhere in the stack, and Rails has caught up. Gems like ruby-openai and the newer RubyLLM make calling a model and getting structured output back genuinely practical. Pairing those with a typed-output approach keeps responses predictable enough to store and act on.

The caveat that most write-ups skip is the important one. Do not bolt AI dependencies onto a product that does not need them, and run every AI gem through the same rubric as any other dependency. The risk is not theoretical. In Sonatype’s testing, 27.8% of an LLM’s dependency-upgrade recommendations referenced versions that were never published, and a few pointed to genuinely compromised packages. An AI coding agent confidently recommending a hallucinated gem is precisely the moment to fall back on boring, verified choices.

Observability, Logging, and Security in Production

For apps we maintain, a few categories are not optional. Structured logging with Lograge, basic metrics, and error tracking through something like Sentry are the difference between resolving an incident in minutes and guessing in the dark. These gems earn their place precisely because you only notice them when they are missing.

Dependency auditing belongs in the pipeline, not in someone’s memory. We run bundler-audit to flag gems with known vulnerabilities and Brakeman for static security analysis, both wired into CI so a risky dependency fails the build rather than reaching production. This matters more for an agency shipping to clients than for a solo side project, and it is one of the items on our Ruby on Rails code review checklist that we treat as non-negotiable.

Developer Experience and Testing

Testing culture is really about the quality of the app after our involvement ends. We standardize on RSpec with FactoryBot for fixtures, Capybara for system and browser tests, and a profiler when we need to find where time actually goes. The aim is a suite the client’s own team can trust and extend.

We use Bullet to catch N+1 queries in development before they become production slowdowns, because nothing erodes confidence in an inherited codebase faster than a page that loads slowly for no obvious reason. Good coverage and clean query patterns are what make a handoff an actual handoff rather than a goodbye and good luck. That maintainability is the real deliverable.

The Rails Gems We Actually Ship: Redwerk’s Production Gemfile

Keeping a Gemfile Healthy Over Years

The part of the job that defines agency work is keeping a Gemfile healthy long after launch. We treat maintenance as a routine rather than a rescue mission, because small and frequent updates are far cheaper than the once-every-three-years version jump that turns into a multi-week migration. In practice, that means a few standing habits:

  • Audit on a schedule: we review dependencies on a set cadence instead of waiting for something to break, running bundle outdated and bundler-audit in CI so stale gems and known vulnerabilities surface on every build.
  • Upgrade in small steps: patch and minor versions go in on a regular rhythm, and major versions are planned deliberately with the changelog read first, so no upgrade lands as a surprise.
  • Automate the busywork: tools like Dependabot or Renovate open update pull requests for us, which keeps the backlog visible and the diffs small enough to review.
  • Watch for abandonment: we flag gems with a single maintainer or a long silence, then replace them on purpose before an unmaintained dependency blocks a Rails or Ruby upgrade.
  • Keep Ruby and Rails current: staying on supported versions of the framework and the language avoids the security and compatibility dead ends that come with end-of-life releases.
  • Prune what no one can justify: every audit is also a chance to remove gems nobody can explain, which shrinks the attack surface and the maintenance load at the same time.
  • Lean on the test suite: solid coverage is what makes all of the above safe, since it lets us upgrade with confidence instead of hope.

On inherited client apps, this discipline is where the payoff shows up. The apps that are pleasant to maintain five years on are the ones where someone kept the Gemfile honest, and that work matters far more over time than it ever shows in a feature demo.

Build It with a Team That Actually Ships Rails

A good Gemfile is a sign of engineering judgment, and judgment is what you are really hiring when you bring in an agency. At Redwerk we have spent years building and maintaining Rails apps where dependency choices carried real consequences. The case studies tell that story better than any feature list.

We rebuilt the PlusPlus Slack bot from a Python prototype that could not scale into a Rails system handling over a million actions per minute. We built the backend and RESTful APIs for Cleanagents so an Android app could serve self-employed cleaners across Germany and Austria. And on Adfectious, a mobile advertising platform, we designed the architecture and a banner-processing engine, with install code delivered across Ruby on Rails, PHP, ASP, C#, and JSP. If you are starting a new Rails build, or wondering whether your current Gemfile is helping or costing you, contact us and we will take a look.

See how we used Ruby on Rails and gems like Geocoder to ship Cleanagents, an on-demand cleaning platform later acquired by Helpling.de

Please enter your business email isn′t a business email