Name Your Subexpressions

Complicated Logic

Everyone likes to joke about how they don't use any of the stuff they learned in college. This isn't the case for me. There is some I use and some I don't. One thing I learned pretty well is how to simplify expressions and reason about boolean algebra. I use this most days in programming to verify that my code is correct and to simplify expressions. One common trick I use is to distribute a negation operator to an expression using De Morgan's theorems in order to cancel part of it out. Very handy!

What math won't tell you is why is code the way it is; what is the intent the programmer had while writing it. While maintaining and modifying code I sometimes run across some very complicated logic. Maybe this is a large "if" statement with lots of terms or nested "if" / "else" blocks. There is only so much boolean algebra can do to simplify this. Sometimes code is complicated because the problem being solved is complicated.

Example

if (features[var1].status == 'e' && ((uid == '0' && config.env == 'l') || 
user.flags.indexOf('call2') >= 0) && !(settings.flag2 || status == 3)) {
}

This code's boolean logic is actually pretty simple. This is example code I made up so there is only so far I was willing to go. But to understand code like this you have to understand the context. What do these variables mean, where is it used?

Once you figure this out, here are my tips on how to make the code easier to understand for both yourself and other programmers. Certainly you should consider boolean algebra to simplify any complex expressions. Also consider naming the magic numbers here too. But you should also name the subexpressions.

Subexpressions

const isFeatureEnabled = features[var1].status == 'e';
const isRoot = uid == '0';
const isLocalEnvironment = config.env == '1';
const isUserEntitledToCall2 = user.flags.indexOf('call2') >= 0;
const isSpecialCustomer = settings.flag2;
const isServiceDisabled = status == 3;

Try not to get hung up on the naming convention here. Some people like more terse names, some like it more spelled out. The point is to give names to these expressions by assigning them to a variable. Another possibility is to extract some of them to a function. This is naming it too.

function isLocalEnvironment(config) {

Now we can put all these named subexpressions back into the conditional.

const hasPermissions = (isRoot && isLocalEnvironment(config)) || isUserEntitledToCall2;
if (isFeatureEnabled && hasPermissions && !(isSpecialCustomer  || isServiceDisabled )) {

Notice I added another variable for part of the remaining expression. Here hasPermissions is introduced to simplify the permission check.

Is this code right or wrong? Well it is an example so who knows! But if this was real code it gives you a chance to analyze it. First we see if all the variables are checking the values they should. The definition for "isServiceDisabled" seems suspect. Maybe there are other statuses that are possible that should be checked. Or maybe it is ok. The point is we can check small parts of it without mental overload.

Then we can also check the main "if" statement. Are the "and"s and "or"s and "not"s right? That too can be checked piecemeal.

Comments

I'm never going to object to adding more comments to code. It is rare to encounter code that has been well documented. So it wouldn't be a bad thing to add comments to the "if" statement as well. But it is better to simplify complicated things than to document it. Add comments for knowledge that the code doesn't immediately provide.