Interesting Bugs Caught by ESLint's no-constant-binary-expression (2022)

A new ESLint rule that flags constant boolean and comparison expressions in JavaScript is surfacing a surprising number of real bugs, especially where developers misunderstand operator precedence or short‑circuit logic. Commenters argue that such “useless” code almost always indicates discrepancies from programmer intent and is hard to catch with unit tests or even 100% statement coverage, making static analysis and strict linters a high‑ROI safety net. The conversation also touches on trade‑offs: linters versus compilers for enforcing these checks, tension between readability and cleverness, and similar tooling for languages like Python, Go, and TypeScript.

Role of the lint rule

  • Constant binary/logical expressions are seen as serious code smells, especially in safety‑critical domains, because they almost always hide bugs and make parts of code unreachable.
  • Many commenters are impressed by how many real bugs this rule exposes in high‑profile projects, seeing it as “mentoring at scale” inside editors.
  • Some push back on calling all “useless” code bugs, arguing there are legitimate cases for intentionally redundant or noop code to clarify intent; they see suppressing the rule in those cases as acceptable.

Testing, coverage, and constant expressions

  • Several comments stress the distinction between statement coverage and branch/decision coverage; constant sub‑expressions can give 100% statement coverage while leaving branches effectively untested.
  • People note that 100% branch coverage would, in theory, preclude these bugs, but full coverage (especially branch) is rare and expensive, so static checks are still valuable.

Code clarity, ‘useless’ code, and style

  • Many prefer verbose, “boring” code and named intermediate booleans over clever one‑liners, especially for conditionals and returns.
  • There is concern that large amounts of genuinely unused or dead code make comprehension and review harder; advice includes aggressively deleting it and relying on version control.

Operator precedence and common mistakes

  • Numerous examples show confusion over precedence (a === b ?? c, !x == null, states.includes('VALID' || 'IN_PROGRESS'), etc.).
  • Some advocate liberal use of parentheses or splitting expressions into named variables; others complain that formatters remove “unnecessary” parens.
  • A minority argue precedence itself was a design mistake and new languages should simplify or eliminate it.

Temporary constants and ergonomics

  • Developers frequently use constructs like if (0 && expr) or if (true) to temporarily force/disable branches during debugging.
  • Some find linters blocking this annoying; others argue that warnings (with inline suppression when intentional) are exactly how you avoid accidentally committing such code.

Language and tool ecosystem

  • Similar checks exist or are desired in other ecosystems (Python, Go, C/C++ compilers), though heavy macro/template use can complicate them.
  • There is debate over whether such checks belong in compilers (risking breaking existing code) versus linters (opt‑in, more flexible).
  • In TypeScript, some want stricter static analysis for these patterns, but others note TS must remain a JS superset, leaving linters to enforce “probably a bug” patterns.