A booking match gives the right answer only because two overlapping cases sit in a particular order - how do you remove that dependency?
answer
- overlap plus order is precedence
- disjoint cases make order irrelevant
- complement the guard, partition the values
- the negation can drift into a gap
- pin each branch with a test
basics
~20 sMake the cases disjoint, so each value is admitted by exactly one of them: push the distinguishing condition into both cases, typically by giving the broader case the negation of the first one's guard. Then reordering the pair changes nothing.
solid answer
~50 sOverlap plus order is the default mechanism, not a bug, but it makes position load-bearing. To remove the dependency you make the two cases admit disjoint sets of values: give the broader case a guard that is the exact complement of the narrower one's, so `where end == start` and `where end != start` partition every ranged booking between them. Now the pair can be written in either order with the same result. The cost is duplication - the second condition restates the first negated, and a later edit to one can leave a value admitted by neither case, which fails at run time. So the honest answer is a choice: make them disjoint where the conditions are simple and symmetric, or keep the ordered ladder and pin each branch with a test that fails if the cases are reordered.
code
pseudocode · 5 linesmatch request
case Booking(g, Range(s, e)) where e == s
return sameDayQuote(g, s)
case Booking(g, Range(s, e))
return nightlyQuote(g, s, e)go deeper
The takeaway is that case order can carry meaning. If two cases could both accept the same value, the one written first gets it, so moving cases around is not a cosmetic edit.
Be able to perform the fix: give the broader case the exact negation of the narrower one's guard so each value is admitted by exactly one, and say why that makes order irrelevant.
Argue both ways. Name the cost of the partition - a restated negation that can drift into a gap with no fallback - and back whichever structure you choose with tests that fail if the cases are reordered.
This is a codebase convention, not a one-off. Decide when a match must be a partition and when an ordered ladder is sanctioned, and make the ordering assumption testable so it survives people who never read the review.
## What "order is load-bearing" actually means Two cases **overlap** when some value is admitted by both. Because matching is first-match, the one written higher takes those shared values. That is a legitimate and extremely common design - it is how a ladder of special cases above a default is written. It becomes a review concern when the overlap is **invisible**: nothing in either case says "I am the special case" or "I am the fallback", and the correct behaviour is carried entirely by which line happens to be first. The symptom is easy to recognise. Move one case up, and the results change; and nobody reading either case in isolation could have predicted which values it would actually see. ## The direct fix: make the cases disjoint Two cases are disjoint when no value is admitted by both. Then order is irrelevant by construction. There are two ways to get there. 1. **Complementary guards.** The narrower case keeps its condition; the broader case gets the exact negation. Same-day stays go to one case, non-same-day stays to the other, and every ranged booking is admitted by exactly one. 2. **Distinguishing shapes.** Where the distinction is already carried by the value's shape rather than by a computed condition, the two cases can match different shapes and no guard is needed at all. This is the cleanest outcome when it is available. Both turn a precedence relationship into a partition. After either, the pair reads as a complete case analysis rather than a sequence. ## What the fix costs Complementary guards are not free, and a senior answer says so. - **Restated logic.** The second guard is the first one negated. Two expressions now have to stay opposite, and nothing enforces it. - **Drift into a gap.** Edit one guard and forget the other, and some value is admitted by neither case. Where an ordered ladder would have fallen through to the default, the partition now has a hole and the match fails at run time. - **Scale.** With three or more overlapping conditions, each case's guard has to negate every earlier one. The conditions get long, and the reader has to assemble the meaning of the last case from all the ones above it - which is exactly the readability that first-match ordering was giving away for free. ## The alternative: keep the order and make it explicit Often the ladder is the better design, and the fix is to stop the order from being *implicit* rather than to remove it: - **Name the conditions.** A guard calling a named predicate - `where isSameDay(start, end)` - tells the reader what the case is for without them decoding the expression. - **Put the default last and make it obviously the default.** A general case at the bottom is a stated fallback; the same case in the middle is an accident waiting to happen. - **Pin the branches with tests.** For each case, a value that only that case should handle, asserted against the exact result, plus one for the shared value that the ordering decides. A reorder then fails a test instead of shipping. - **Say so in the code.** One line noting that the general case deliberately sits below the special case is cheaper than a negated guard and does not drift into a gap. ## Choosing between them | Situation | Prefer | |---|---| | Two cases, one simple symmetric condition | Complementary guards - the negation is obvious and stable | | The distinction is already visible in the value's shape | Distinct patterns, no guard at all | | Several special cases above one default | Keep the ordered ladder; name the predicates, test each branch | | The conditions are expensive or effectful | Keep the ladder, so only the guards above the winner are evaluated | | A reorder would be a silent behaviour change | Tests first, whichever structure you land on | ## What an interviewer is listening for - That you do **not** call overlap a defect on sight. A candidate who says every match must be a partition has not maintained a long ladder. - The mechanism of the fix stated precisely: disjoint admitted sets, usually via a complementary guard. - The cost named without prompting: a negated restatement that can drift, and a resulting gap that has no fallback. - A verification story. Whatever you choose, the ordering assumption is pinned by tests rather than by a reviewer's memory.
- When is a deliberately ordered ladder the better design?When several special cases sit above one default. Making them disjoint forces each case to negate every condition above it, so the guards grow and the last case can only be understood by reading all the others. Keep the order, name each predicate, put the default last, and test each branch.
- What test catches an accidental order dependency?For each case, a value only that case should handle, asserted against the exact result, plus one test for the shared value the ordering decides. If someone reorders or widens a case, at least one assertion changes. Reviewer memory is not a substitute for that.
- What new failure does a complementary guard introduce that the ordered version did not have?A gap. In the ordered version the broader case is the catch-all, so anything unusual still lands somewhere. Once both cases carry conditions, an edit that breaks the complement leaves values admitted by neither, and the match fails at run time instead of taking the default.
saying these in an interview costs you the question
- Claims overlapping cases are always a defect to eliminate
- Removes the overlap by deleting the narrower case entirely
- Adds a negated guard but leaves some value admitted by no case
- Relies on a comment rather than a test to pin the ordering
- Assumes the build will flag an accidental order dependency
- Negates every earlier condition in a long ladder without weighing readability