MikaCredoRules.StrictEquality (MikaCredoRules v0.1.0)

Copy Markdown View Source

Comparisons must use ===/!== instead of ==/!=.

== coerces across numeric types — 1 == 1.0 is true — so a refactor that changes a value from integer to float keeps every comparison silently passing. === only matches the same type and value, and fails loudly the moment types drift.

# BAD
if user.age == 18, do: ...
if status != :active, do: ...

# GOOD
if user.age === 18, do: ...
if status !== :active, do: ...

Ecto queries are exempt because the query DSL only compiles == and !=:

from(u in User, where: u.age == 18)   # allowed
where(query, [u], u.age == 18)        # allowed
dynamic([u], u.age == ^age)           # allowed

Which calls are exempt is controlled by the :ignored_functions param. Bare and imported calls match by function name; qualified calls are only exempt on Ecto.Query itself or an alias of it (alias Ecto.Query, alias Ecto.Query, as: Q, alias Ecto.{Query, ...}), so Enum.join/2 sharing a name with the :join entry never hides its arguments. Only the arguments of an exempt call are skipped — a loose comparison beside a query call on the same line is still reported.

Any comparison with Mix.env() on either side is deliberately allowed, in any file — so the standard start_permanent: Mix.env() == :prod line in mix.exs passes. Mix.env/0 returns an atom, which == cannot coerce, and calling Mix.env/0 outside mix.exs is a different rule's job to catch.