What do COALESCE and NULLIF actually do, and why should you be careful about how you use them?
COALESCE takes two or more expressions and returns the first one that is not NULL. It is the standard way to substitute a default for a missing value. NULLIF does the opposite: it takes two expressions and returns NULL if they are equal, otherwise it returns the first one. It is the standard way to turn a sentinel value into a real missing value.
They are often used together. NULLIF(COALESCE(raw_value, 0), 0) reads as "treat a missing value and an explicit zero as the same absence of a value", which is what you want when the upstream system uses 0 as its placeholder for unknown.
The caution is about what COALESCE hides. Replacing a missing value with 0 in a query makes the report look complete while quietly changing the meaning of the number. If a customer has no orders and you coalesce the order count to 0, the report says "this customer ordered nothing", which is different from "we do not know how many orders this customer made". If the difference matters, keep the NULL and handle it in the presentation layer, or emit both the raw and the coalesced value.
NULLIF has its own sharp edge. It is not a general comparison helper: NULLIF(a, b) returns NULL whenever a and b are equal, so it is the wrong tool for comparing two columns. If you want null-safe equality, use a IS b in SQLite and PostgreSQL or the explicit (a = b) OR (a IS NULL AND b IS NULL) form.
There is also a performance angle worth raising. A function wrapped around a column is not sargable, so WHERE COALESCE(status, 'new') = 'new' cannot use an index on status even though WHERE status IS NULL OR status = 'new' can. The same expression in a WHERE clause and in a projection are not equally harmless.