Rails makes building apps fast and joyful โ but sooner or later, every team runs into the same dreaded complaint:
“Why is this page so slow?”
Performance debugging is tricky because Rails abstracts so much for us. Underneath every User.where(...).first or current_user.orders.includes(:products), there’s real SQL, database indexes, network calls, caching layers, and Ruby code running.
This post (Part 1) focuses on how to find the bottlenecks in a Rails app using logs and manual inspection. In Part 2, we’ll explore tools like Rack Mini Profiler and real-world fixes.
๐ Symptoms of a Slow Rails Page
Before diving into logs, it’s important to recognize what “slow” might mean:
- Page loads take several seconds.
- CPU usage spikes during requests.
- The database log shows queries running longer than expected.
- Repeated queries (e.g. the same
SELECTfiring 30 times). - Memory bloat or high GC (garbage collection) activity.
Example symptom we hit:
SELECT "flipper_features"."key" AS feature_key,
"flipper_gates"."key",
"flipper_gates"."value"
FROM "flipper_features"
LEFT OUTER JOIN "flipper_gates"
ON "flipper_features"."key" = "flipper_gates"."feature_key"
This query was executed 38 times when loading a product page (/product/adidas-shoe). Thatโs a red flag ๐ฉ.
๐ Understanding Rails Logs
Every Rails request is logged in log/development.log (or production.log). A typical request looks like:
Started GET "/products/123" for 127.0.0.1 at 2025-09-25 12:45:01 +0530
Processing by ProductsController#show as HTML
Parameters: {"id"=>"123"}
Product Load (1.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", 123], ["LIMIT", 1]]
Review Load (10.4ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."product_id" = $1 [["product_id", 123]]
Completed 200 OK in 120ms (Views: 80.0ms | ActiveRecord: 20.0ms | Allocations: 3456)
Key things to notice:
- Controller action โ
ProductsController#show. - Individual SQL timings โ each query shows how long it took.
- Overall time โ
Completed 200 OK in 120ms. - Breakdown โ
Views: 80.0ms | ActiveRecord: 20.0ms.
If the DB time is small but Views are big โ it’s a rendering problem.
If ActiveRecord dominates โ the DB queries are the bottleneck.
๐ต๏ธ Debugging a Slow Page Step by Step
1. Watch your logs in real time
tail -f log/development.log | grep -i "SELECT"
This shows you every SQL query as it executes.
2. Look for repeated queries (N+1)
If you see the same SELECT firing dozens of times:
SELECT "reviews".* FROM "reviews" WHERE "reviews"."product_id" = 123
SELECT "reviews".* FROM "reviews" WHERE "reviews"."product_id" = 124
SELECT "reviews".* FROM "reviews" WHERE "reviews"."product_id" = 125
That’s the classic N+1 query problem.
3. Look for expensive joins
Queries with multiple JOINs can be slow without proper indexing. Example:
SELECT "orders"."id", "users"."email"
FROM "orders"
INNER JOIN "users" ON "users"."id" = "orders"."user_id"
WHERE "users"."status" = 'active'
If there’s no index on users.status, this can cause sequential scans.
4. Look for long-running queries
Rails logs include timings:
User Load (105.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 123
If a query consistently takes >100ms on small tables, it probably needs an index or query rewrite.
โก Real Example: Debugging the Flipper Feature Flag Queries
In our case, the Rails logs showed:
SELECT "flipper_features"."key" AS feature_key,
"flipper_gates"."key",
"flipper_gates"."value"
FROM "flipper_features"
LEFT OUTER JOIN "flipper_gates"
ON "flipper_features"."key" = "flipper_gates"."feature_key"
- It executed 38 times on one page.
- Each execution took between 60โ200ms.
- Together, that added ~6 seconds to page load time.
The query itself wasn’t huge (tables had <150 rows). The problem was repetition โ every feature flag check was hitting the DB fresh.
This pointed us toward caching (covered in Part 2).
๐งฉ Workflow for Performance Debugging in Rails
- Reproduce the slow page locally or in staging.
- Tail the logs and isolate the slow request.
- Categorize: rendering slow? DB queries slow? external API calls?
- Identify repeated or long queries.
- Ask “why“:
- Missing index?
- Bad join?
- N+1 query?
- Repeated lookups that could be cached?
- Confirm with SQL tools (
EXPLAIN ANALYZEin Postgres).
โ Summary of Part 1
In this first part, we covered:
- Recognizing symptoms of slow pages.
- Reading Rails logs effectively.
- Debugging step by step with queries and timings.
- A real-world case of repeated Flipper queries slowing down a page.
In Part 2, we’ll go deeper into tools and solutions:
- Setting up Rack Mini Profiler.
- Capturing queries + stack traces in custom logs.
- Applying fixes: indexes, eager loading, and caching (with Flipper as a worked example).