Hash#merge is shallow, so merging nested hashes silently throws away the nested data you meant to keep. Here is why it happens and how deep_merge fixes it.
Asserting that a Rails JSON API returns the right body sounds simple, until the test fails on two blobs that look identical. Here is what actually trips these assertions and how to compare responses cleanly.
Your test asserts two hashes are equal, the timestamps look identical, but it still fails. Here is why Ruby time values break hash equality and how to fix it.
In Ruby, :name and "name" are different hash keys, even though they look almost the same. That one distinction is behind a whole category of nil-returning, test-failing bugs.
When a Minitest assertion fails on two hashes, the output is nearly unreadable. Here's why it happens and a faster way to find the one value that changed.
Several ways to compare two Ruby hashes and find exactly what changed, from plain Ruby techniques to a visual side-by-side diff, with the tradeoffs of each.
Your Rails test asserts two hashes are equal, they look identical, but the test still fails. Here's a checklist of the usual causes and how to find the real one.
Ruby has four different methods for asking whether two things are equal, and they don't mean the same thing. Knowing which is which explains case statements, hash keys, and a class of subtle bugs.
Ruby 3.2 added Data.define, a built-in way to create small immutable value objects. It's what Struct should have been, and it replaces a surprising amount of boilerplate.
Both reduce and each_with_object fold a collection into a single result, but they disagree about who owns the accumulator. Getting that wrong is the most common Enumerable bug there is.
The ||= memoization pattern is everywhere in Ruby code, and it quietly breaks on nil, false, and any method that takes arguments. Here is what actually happens and how to do it right.
Ruby hashes can carry their own default value or default block. Used well, they replace nil checks, initialization ceremony, and a surprising amount of glue code.
Ruby's lazy enumerators let you chain map, select, and reject on enormous datasets without loading everything into memory. Here's how they work and when to reach for them.
Most Ruby error handling is either too broad or too silent. Here's how to use rescue, retry, and custom exceptions to build code that fails clearly and recovers gracefully.
Ruby's Enumerable module gives you 50+ methods for free. Understanding it changes how you think about collections, pipelines, and data transformation.
Hash#[] silently returns nil for missing keys. Hash#fetch raises an error. That distinction prevents more bugs than you'd expect.
Ruby has three methods for building clean data pipelines: tap, then, and yield_self. Each does something slightly different, and knowing which to reach for makes your code more expressive.
Why Ruby's 'if it quacks like a duck' philosophy leads to more flexible, testable, and elegant code.
Ruby's metaprogramming is its most powerful and most dangerous feature. Here's how experienced teams decide when to use it and when to walk away.
Concerns are controversial in the Rails world. They shouldn't be. The problem isn't the pattern, it's how people use it. Here's how to use concerns well.
Ruby was designed to make programmers happy. That's not a slogan, it's a design philosophy that shaped how the language reads, and why that still matters.
When you call a method in Ruby, there's a specific chain of places Ruby looks to find it. Understanding that chain is the key to debugging 'wrong method called' bugs.
The frozen_string_literal pragma is one line of code that prevents mutation bugs, improves performance, and signals intent. Here's why you should be using it everywhere.
Ruby didn't just popularize test-driven development, it made testing feel inevitable. Here's how a community obsession became an industry standard.
Most Rake tasks are untested blobs of procedural code. Here's how to write ones that are namespaced, documented, testable, and actually maintainable.
Include Comparable, define one method, and get six comparison operators plus sorting for free. It's one of Ruby's best-kept secrets for custom objects.
N+1 queries, unnecessary column loading, and the count/size/length trap. These three patterns silently kill Rails performance, and they're easy to fix.
Ruby 3's pattern matching with case/in isn't just syntax sugar. It's a fundamentally different way to destructure data, and it's been production-ready since 3.1.
Ruby gives you two ways to create simple data objects. One is fast, explicit, and production-ready. The other is convenient, flexible, and almost always the wrong choice.
Introducing RubyHash, a tool that turns unreadable Minitest hash diffs into clean, sorted, side-by-side comparisons so you can see exactly what changed.
Subscribe to get new Ruby and Rails articles delivered straight to your inbox.