
February 9, 2026
At RubyKaigi 2025, a deceptively simple Rails code quiz was presented at a booth. It looked like everyday production code — nothing exotic, no trick questions.
About 100 developers attempted it.
Only 3 answered everything correctly.
This article is based on the talk “Only ~3% got everything right: 5 common but dangerous Ruby on Rails code patterns”, presented by Yuta Nakashima (Backend Tech Lead at Hubble, Inc.) at Kaigi on Rails 2025. The original material was derived from real production issues and later expanded into a full conference session 全問正解率3____RubyKaigiで出題したやりがちな….

What makes this talk especially valuable is that it doesn’t focus on new features or trends. Instead, it exposes mistakes that experienced Rails developers still make — not because they don’t know better, but because these pitfalls hide inside “normal-looking” code.
Let’s walk through the key lessons.
1. Loading Records Instead of Asking the Database

The mistake
Checking whether a record exists by loading all related records into Ruby and iterating over them.
This often looks innocent:
- Fetch all folders for an organization
- Use any? to check if one matches an ID
Why it’s dangerous
- Every record becomes an ActiveRecord object
- Memory usage grows linearly with data size
- Database and network traffic explode
- Response time degrades as data grows
This pattern scales O(N) — which is fine until it suddenly isn’t.
The fix
Let the database do what it’s good at.
Use exists? and let SQL answer the question directly. You get:
- A single lightweight query
- Constant memory usage
- Index-backed performance
Key lesson: If you only need a boolean, never load full objects.
SPONSOR LOGISTIC INTELLIGENCE:

2. Doing Aggregation in Ruby Instead of SQL
The mistake
Fetching many records and computing totals in Ruby, often with each loops.
This usually leads to:
- N+1 queries
- Excessive database connections
- High CPU usage on the application server
Why it’s dangerous
Databases are optimized for aggregation. Ruby is not.
Running hundreds or thousands of small queries:
- Burns connection pools
- Repeats SQL parsing costs
- Multiplies network latency
The fix
Use SQL aggregation functions like SUM, or store precomputed values when appropriate.
Key lesson: If the database can calculate it, let the database calculate it.
3. Calling External APIs Inside Transactions
The mistake
Executing an external API call inside a database transaction.
Why it’s dangerous
- Transactions hold locks while waiting
- External APIs are slow and unpredictable
- Other requests get blocked
- Debugging becomes extremely difficult
A slow API response can silently freeze unrelated database operations.
The fix
Keep transactions short and deterministic.
Perform external calls after the transaction commits.
Key lesson: Transactions should protect data consistency — not wait on the network.
4. Triggering Background Jobs Before Commit

The mistake
Enqueuing background jobs while still inside a transaction.
Why it’s dangerous
- Jobs may run before data is committed
- Race conditions appear randomly
- Rollbacks leave jobs running with invalid assumptions
- Failures are hard to reproduce
This is one of the most subtle and dangerous Rails bugs.
The fix
Trigger background jobs after the transaction completes successfully.
Key lesson: Jobs should only see committed, stable data.
5. Rescuing StandardError Everywhere
The mistake
Catching all exceptions and always returning a generic 500 error.
Why it’s dangerous
- Internal details may leak
- Clients can’t distinguish error types
- Validation errors look like server failures
- Testing becomes vague and incomplete
This harms both security and developer experience.
The fix
Handle errors explicitly:
- Validation errors → 400
- External service failures → 502 / 504
- Unexpected errors → 500 with proper logging
Key lesson: Error handling is part of your API contract.
Why Only 3% Got Everything Right
The most important insight from this session is not what the mistakes are — many developers already “know” them.
The real problem is that:
When writing real production code, these issues are easy to miss — and even easier to approve during code review.
Rails’ elegance and expressiveness can hide performance, correctness, and concurrency problems in plain sight.
Final Thoughts
This talk is a reminder that Rails doesn’t remove responsibility — it redistributes it.
Understanding:
- transaction boundaries
- database behavior
- background job timing
- SQL vs Ruby responsibilities
is what separates code that works from code that scales safely.
Credit goes to Yuta Nakashima and the Kaigi on Rails 2025 / RubyKaigi team for turning real production pain into practical, actionable knowledge.
If you work on a Rails codebase that matters, these are mistakes worth revisiting — even if you’re confident you’ve already learned them.
