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.
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.