Code that depends on unwritten invariants — `const tax = base * rate;` that assumes rate > 0, with no enforcement and only a stale comment if any.
Invariants the code assumes are stated explicitly; readers don't need to deduce them.
Before the refactoring
// rate must be positiveconst tax = base * rate;
After the refactoring
if (rate <= 0) throw new Error('rate must be positive');const tax = base * rate;
Bugs that violate the invariant bubble out as mysterious downstream errors; readers must reconstruct the invariant from context; the assumption drifts silently as the code evolves.
Assertions used as control flow couple production behavior to debug-mode invariants — keep them as runtime contracts that should never fire.
Bugs that violate the invariant fail loudly at the source instead of bubbling out as mysterious downstream errors.
Using assertions as control flow — production code that depends on the assertion firing or not firing becomes coupled to debug-mode behavior.