Calling max or min on lists of type t that are empty gives another type

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the types category.

Last Updated: 2024-04-25

We had the following code used to calculate the availability of discounts.

order.downloads.select do |product|
  last_updated_at = product.notes_files.maximum(:released_on)

  if order.completed_at > last_updated_at
    false
  else
    true
  end
end

The problem? Well a product, when in a deleted state, can have no associated note_files records - thus product.notes_files will be []. And so the maximum(:released_on) applies to an empty set, returning nil. Because the next line compares this value with a term, it causes a type error.

The fix was a guard clause for deleted products (which, being deleted, ought not have discount offers anyway).

order.downloads.select { |product|
  next false if product.deleted_at
  # ... as before
end

Lesson

Calling max/min etc. on a list of type T will usually return an entity of another type (e.g. nil) if the list is empty. Anticipate this in your code.