Rule
Detect contradictory or impossible logic
Code that checks conditions after they've
already been violated, or assumes states
that are impossible given the control flow.
Supported languages: 45+Introduction
Contradictory logic appears when code checks conditions that are already known to be true or false based on earlier control flow. This happens after refactoring when validation gets reordered, or when developers add defensive checks without understanding what guarantees already exist. A function that checks if (user !== null) after calling user.email has contradictory logic, the null check comes too late. These logical impossibilities indicate deeper problems with code organization or missing understanding of what each code path guarantees.
Why it matters
Security implications: False validation creates a dangerous illusion of safety. When security checks appear after the data has already been used, attackers can exploit the window before validation occurs. Code that validates user permissions after executing privileged operations provides no actual protection, just misleading comments about security.
Code maintainability: Contradictory logic suggests the code doesn't match the developer's mental model. Someone thought a condition needed checking but placed it wrong, or the code was refactored without updating related checks. Future maintainers can't trust that validation exists where needed, forcing them to trace through entire functions to understand actual guarantees.
Bug indicators: Impossible conditions rarely exist in isolation. They signal deeper issues like missing error handling, incorrect assumptions about function contracts, or failed refactoring. A check that can never execute often means a different check is missing somewhere else that should have prevented this state.
Code examples
❌ Non-compliant:
function processOrder(order) {
if (!order) {
return { error: 'Order required' };
}
const total = order.items.reduce(
(sum, item) => sum + item.price,
0
);
if (order.items && order.items.length > 0) {
applyDiscount(order);
}
if (total < 0) {
throw new Error('Invalid total');
}
return { total, status: 'processed' };
}
Why it's wrong: The code calls order.items.reduce() which crashes if items is null or undefined, then checks if items exists afterward. The total < 0 check is also contradictory because the reduce always returns non-negative values when summing prices.
✅ Compliant:
function processOrder(order) {
if (!order || !order.items || order.items.length === 0) {
return { error: 'Valid order with items required' };
}
const hasInvalidPrice = order.items.some(
item => typeof item.price !== 'number' || item.price < 0
);
if (hasInvalidPrice) {
throw new Error('Invalid item prices');
}
const total = order.items.reduce(
(sum, item) => sum + item.price,
0
);
if (order.items.length >= 5) {
applyBulkDiscount(order);
}
return { total, status: 'processed' };
}
Why this matters: All validation occurs before using the data, checks happen in logical order, and conditions reflect actual requirements. The function validates inputs upfront, then processes valid data without redundant or contradictory checks.
Conclusion
Place validation before using data, not after. Review conditions that seem defensive but appear after the data has already been accessed or modified. When refactoring, update or remove related validation to maintain logical consistency throughout the function.
.avif)
