Ruby on Rails performance enhancements.
Measurement first. Never guesswork.
Expert Ruby on Rails, applied where it is actually slow. We profile before we change anything, then fix what real users feel, not what looks good on paper.
These days, customers expect applications to feel instant. It doesn’t matter whether they’re working with thousands of records or millions. They expect graphs to load quickly, chat to feel real-time, and attachments to appear without hesitation.
Ruby on Rails is fast out of the box, but as an application grows, small design decisions start to compound. A few extra queries here, an unbounded background job there, and before you know it, the app slows down.
BigBinary has been working with Ruby on Rails for 15+ years, and performance work has been a constant theme. Sometimes it’s a classic N+1 query. Sometimes Sidekiq is configured in a way that creates bottlenecks instead of throughput. Sometimes Redis caching is doing more harm than good, causing thundering herds, stale data problems, or wasted memory without improving real-world speed.
We start with measurement
Performance problems are not always where you expect them to be. Before changing code or adding more servers, we want to understand what’s actually making the application slow.
- 01
Production-like profiling
An application that performs well with a hundred records might struggle with a million. We profile applications using realistic data volumes, traffic patterns, and workloads. This helps us identify bottlenecks that might never appear in a developer’s local environment.
- 02
Real request traces
A slow page doesn’t necessarily mean a slow database query. The time might be spent rendering views, waiting for an external API, or acquiring a database connection. We examine request traces to understand where time is being spent instead of making assumptions about what needs fixing.
- 03
Find what is actually slow
Sometimes the problem is an N+1 query. Other times, it’s an overloaded Sidekiq queue, an inefficient cache, or Puma workers waiting for database connections. We investigate the entire request lifecycle to identify the underlying bottleneck rather than treating its symptoms.
- 04
Changes that users feel
Reducing a query from 10 milliseconds to 5 milliseconds might look impressive in a benchmark, but it won’t necessarily improve the user experience. We prioritize changes that make pages load faster, reduce waiting times, improve throughput, or lower infrastructure costs.
Many teams try to “buy” their way out of performance problems by throwing more compute at them.
That can mask symptoms for a while, but it usually turns into an expensive habit.
The Scaling Rails series
Over the last 15+ years, we’ve worked on Rails applications with very different performance challenges. Some handle thousands of background jobs. Others experience sudden traffic spikes. Some are limited by database connections, while others struggle with CPU utilization or inefficient concurrency.
Scaling these applications requires understanding how Ruby, Rails, Puma, Sidekiq, and the database work together.
We’ve published a six-part Scaling Rails series that goes beyond general performance advice. It explores the underlying mechanics of Rails applications and explains how to measure, configure, and scale them.
- 01
Understanding how Puma handles requests
Puma is the web server used by most Rails applications. Understanding how it handles requests is essential when deciding how many threads and workers an application needs.
This article explains how Puma processes incoming requests, how its thread pool works, and what the default Puma configuration means for a Rails application.
Read: Understanding how Puma handles requests → - 02
The GVL in Ruby and its impact on scaling Rails
Adding more threads doesn’t necessarily make a Ruby application faster. Ruby’s Global VM Lock (GVL) limits how Ruby code can execute concurrently within a process.
This article explains the difference between CPU-bound and I/O-bound workloads, concurrency and parallelism, and how the GVL affects the number of Puma processes an application needs.
Read: The GVL in Ruby and its impact on scaling Rails → - 03
Amdahl’s Law: The relationship between speedup and concurrency
How much faster can an application become when we increase concurrency? And at what point does adding more threads stop helping?
This article explores Amdahl’s Law, the theoretical limits of parallel execution, and the relationship between thread counts, request queue time, and application throughput.
Read: Amdahl’s Law and concurrency → - 04
Finding the ideal number of Puma threads using GVL instrumentation
There’s no universal answer to how many Puma threads a Rails application should use. The right number depends on the application’s workload.
This article demonstrates how to measure GVL behavior using instrumentation, determine how much time an application spends waiting on I/O, and use that information to choose an appropriate Puma thread count.
Read: Finding the ideal number of Puma threads → - 05
Understanding Active Record connection pooling
Increasing Puma workers and threads also increases the potential demand for database connections. Without proper configuration, applications can exhaust their connection pools and encounter timeouts.
This article explains how Active Record manages database connections, how to calculate connection requirements, how asynchronous queries affect connection usage, and when tools such as PgBouncer can help.
Read: Understanding Active Record connection pooling → - 06
Understanding queueing theory
Scaling isn’t simply about adding more servers. As systems approach their capacity, even small increases in traffic can cause disproportionate increases in response times.
This article introduces queueing theory, Little’s Law, the knee curve, and the relationship between concurrency, throughput, and latency.
Read: Understanding queueing theory →
Engineering decisions backed by measurement.
These articles reflect how we approach performance work at BigBinary. We don’t believe in blindly increasing thread counts, adding more servers, or introducing caching without understanding the underlying problem.
We measure how the application behaves, identify the actual bottleneck, and make changes based on evidence.
Explore the complete Scaling Rails series →The fastest query is the one you never run.
We pay attention to how data flows through the system. What gets loaded on each request? What gets calculated again and again? What could be done once and reused? A page that loads every column of every record just to show a count, or recalculates the same totals on every visit, will keep getting slower as the data grows, no matter how big the server is.
So we look at the whole path. We load only the records and columns a page needs, paginate long lists, and use counter caches and preloading where they help. We add the indexes that queries actually use, remove joins that aren’t needed, and cache what is expensive to compute and rarely changes. Reports and dashboards that summarize millions of rows can often read from precomputed tables instead of crunching the numbers on every request.
Work that doesn’t need to happen while the user waits moves to the background, such as sending emails, generating PDFs, calling slow third-party APIs, and processing imports. Jobs carry IDs rather than whole objects, work through large datasets in batches, and are safe to retry. Queues are sized and separated so a flood of low-priority jobs can’t hold up the urgent ones, and failures and queue latency are visible rather than discovered by customers.
Files and images take a path of their own. They are served from a CDN and object storage, not streamed through Rails processes that should be handling requests.
Done well, Sidekiq becomes a lever for speed and reliability rather than a mysterious box that occasionally catches fire.
Performance work shouldn’t be a one-time rescue mission. The goal is to create a system that stays fast as the product evolves, where you can spot regressions early, understand what changed, and fix issues before customers feel them.
We aim to leave you with a faster application and a clearer toolkit: dashboards, alerts, and practical guidelines so future features don’t quietly undo the gains.
The result is an app that feels snappy to users, costs less to run, and gives your team confidence as you grow.
Other ways we can help.
Ruby on Rails Application Development
We design and build Ruby on Rails applications from the ground up. From architecture and database design to development, testing, and deployment, our team works with you to turn business requirements into reliable, maintainable software built for long-term growth.
Ruby on Rails Upgrades
Keep your Rails application current without disrupting your business. We handle Ruby and Rails upgrades, dependency updates, deprecated APIs, test failures, and compatibility issues, helping you move to newer versions safely while improving the overall health of your codebase.
Ruby on Rails Code Audit
Get an experienced second set of eyes on your Ruby on Rails application. We review your architecture, code quality, security, database usage, performance, dependencies, and test coverage, then provide practical recommendations your engineering team can act on.
Ruby on Rails Maintenance
Running a Rails application requires continuous attention long after the initial launch. We provide ongoing maintenance covering bug fixes, security updates, dependency upgrades, performance improvements, monitoring, and production issues so your team can focus on building the business.
Team Augmentation
Add experienced Ruby on Rails developers to your existing engineering team without spending months recruiting and onboarding. Our developers work within your tools and processes, collaborate directly with your team, and contribute to features, bug fixes, performance improvements, and the long-term maintainability of your application.