Ruby & Rails

Ruby pattern matching, in code you'd actually ship

It is not a switch statement. It destructures and validates shape in one expression, which is exactly what parsing external data needs.

18 February 2026 2 min read Mohammad Aaquib Jawed

Pattern matching arrived in Ruby 2.7 and is still treated as a curiosity. The place it genuinely earns its keep is handling data whose shape you do not control.

The problem it solves

Handling an API response usually looks like a chain of key lookups, nil checks and type tests. It is verbose, and the validation is spread across the whole block rather than stated in one place.

A pattern states the shape you require and binds the parts you want, in one expression. If the shape does not match, the branch does not run — there is no separate validation step to forget.

Where I use it

Parsing webhooks, where the payload varies by event type and each variant has a different shape.

Handling result objects — matching on success with a value versus failure with an error reads better than nested conditionals, and makes the exhaustive set of outcomes visible.

Normalising configuration that accepts several forms: a string, a hash, an array of hashes.

In each case the alternative is a conditional chain that grows quietly and stops being readable around the fourth branch.

A pattern is a specification of the input you accept, sitting where the code that handles it is.

The features worth knowing

Binding within a pattern captures values as it matches, so you do not re-index afterwards.

Guards attach a condition to a branch, so shape and value constraints live together.

The find pattern locates a matching element inside an array without a manual loop.

Deconstruction hooks let your own objects participate — define deconstruct_keys and an instance matches a hash pattern, which is a clean way to expose a stable shape without exposing internals.

Where it does not belong

Simple equality. Matching on one value against three literals is a case statement, and writing it as a pattern is showing off rather than communicating.

Deeply nested patterns become as unreadable as the conditionals they replaced. If the pattern needs more than a couple of levels, the data probably wants normalising first.

And case/in raises when nothing matches, unlike case/when. That is frequently what you want for external data — an unexpected shape should be loud — but it is a real behavioural difference, and an else branch is not optional if you meant it to be tolerant.

All writing Reply by email