I’ve blogged about it before. Bang is evil, and by bang I mean negative conditionals. Even when you try hard to make them easy to read, boolean expressions can be error prone.

Today’s installment comes to us thanks to liquibase preconditions. My team ran into a problem yesterday with boolean logic in liquibase preconditions that was specifically related to negation. The migration in question looked something like this:

<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM table_x WHERE column_a = 'XYZ';</sqlCheck>
</preConditions>
<sql>
UPDATE table_x SET column_a = 'ABC' WHERE column_a = 'XYZ';
</sql>

Can you spot the bug?

The bug is using ‘XYZ’ in the sqlCheck rather than ‘ABC’. We want to change ‘XYZ’ to ‘ABC’ in table_x. Therefore, our sqlCheck is the inverse of what we want. Our migration can never run.

Why did this happen? It happened because the logic is to run a check, then compare the result to zero, then invert the condition and only take an alternative action if we don’t get zero. Mentally parsing the combination of expressions while reading the migration is error prone. It’s easy to mentally negate the expression an incorrect number of times.

The bottom line is that negative conditionals impede the readability of any language. Period.