Only 3% Got It Right: 5 Dangerous Ruby on Rails Code Patterns from RubyKaigi

Only 3% Got It Right: 5 Dangerous Ruby on Rails Code Patterns from RubyKaigi
Only 3% Got It Right: 5 Dangerous Ruby on Rails Code Patterns from RubyKaigi

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で出題したやりがちな….

Article content

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

Article content

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

Article content

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.



Article content

Leave a comment