Here is a pretty bad bug that I left in production for way too long:
(sort new-alist
(lambda (x y)
(or (< (second x) (second y))
(< (first x) (first y)))))
Here is what I meant to write:
(sort new-alist
(lambda (x y)
(or (< (second x) (second y))
(and (= (second x) (second y))
(< (first x) (first y))))))
(I guess those queries can be replaced by temps if you were so inclined. And, “new-alist” is the wrong name for a list of undotted three-element lists; after sorting, it goes through a map (o reverse cdr) but still not dotted. Såattäh…)
Sort by the second field. If they are equal, which they will be often enough, then use the first field as a tie-breaker.
In practice, the second field was equal so often (and/or the input order was already right so often) that the bug went on unnoticed and didn’t do any harm before it was discovered.
Still wrong, though, and illustrative of what a wrong mental model of comparators can look like: thinking of the function as a question of basically proving why X should go first, forgetting that sometimes Y actually has the precedence based on the second field. X is like “Did I win by second field? No? How about by first field?” Hold your jig there, X, sometimes Y wins by second field.