loading...
Cover image for How do you handle the disposable IF statements ?

How do you handle the disposable IF statements ?

mazentouati profile image Mazen Touati ・1 min read

Hi there,

Sometimes you need to write a conditional block that will only be executed once or for a brief period of time then the condition won't be satisfied again, like for ever.

An example for these IFs is


// User level IFs
if (!user.activated) {
 alert('please activate your account');
 return false;
}

// Application level IFs
if ( ! isAppProperlyInstalled() ) {
  die('Please, check the installation guide...');
}

The idea of that residing code that won't be executed again has been bothering me for years. I'm kind of picky and sometimes go crazy about micro-optimization, but sometimes I just ignore it. However, I feel that there's a great design pattern that I'm not aware of that may make me feel better about it.

What do you think ? Do you share the same feeling ? and how are you dealing with it ?


Photo by Carolina Pimenta, Unsplash

Posted on by:

Discussion

markdown guide
 

This completely removes the code, using dead code elimination, at compiletime:

when conditionThatsRarelyTrue:
  discard

For runtime alternative, compiler uses branch optimization:

if unlikely(conditionThatsRarelyTrue):
  discard
elif likely(conditionThatsUsuallyTrue):
  discard
else:
  discard

The likely() will be faster, the unlikely() will be slower.
This is Nim lang.

 

If I understand the frustration here correctly, I would like to draw a parallel with repeatedly revalidating "known-good" data.
There, the solution is separating the "input" data from the "validated" data at the type level, so you work with the primitive types once per acquisition, and then your application logic can avoid checks completely. It kind of lives in an environment guarded of stupidity.

A similar pattern here is carrying out the checks only when the state could have changed. For example, it might be beneficial to check isAppProperlyInstalled in an init step, before starting the main loop of a program. Not only do you want to fail as quickly as possible, but it will make all other modules simpler, since they can assume that by the time they are loaded, all of the sanity checks and conditions are in order.

In other cases, you may have to have some functionality working before an asynchronous condition has been verified. For example, user.isLoggedIn, which might go to the network if a token is expired, etc. In this case, you can do a kind of RAII, and return a promise of the logged in user, instead of having a user whose state may change. This can be introduced in parallel with old checks:

const getUser = async () => {
  if (user) return user
  if (legacyUser.isLoggedIn) return user = wrap(legacyUser)
  return new Promise((resolve, reject) => {
    legacyUser.logIn((err, newUser) => {
      if (!err) resolve(user = wrap(legacyUser = newUser))
      else reject(err)
    }
  }
})
 

Sounds interesting. There's some great hints here, but a similar solution has to be found for each language as the asynchronous model not available for every one of them. Though, I found out that every disposable IF should be treated separately. As there's no general rule to handle them all in the same way. For example and per your suggestion, the app installation check IMO should happen before starting the server using hooks or something similar. Thanks Mihail !

 

You could still write this with callbacks, in fact, getUser could be a synchronous callback in some languages, blocking the (green)thread.

The general idea is that you are "scheduling" work for after the asynchronous condition is determined, and keeping a "handle" to the work to know if/when it succeeds. This could be a part of the language runtime, or directly in your application code, where there is more opportunity to retry/batch/time out your pending "tasks".

But yeah, the initialization phase, including critical assertions, is a totally separate story.

In the second paragraph, are you talking about " job queue " ?

Yeah.

For example the way you schedule network actions with an
offline-first/optimistic update system like Apollo, the Firebase client, etc.

Chaining on an actual promise of a network request or filling a stack of "tasks" which will begin sending to the network after a "cameOnline" poll or callback fundamentally represents the same idea: Preparing now actions that may run in the future if conditions are right.

I see, thanks for sharing your thoughts Mihail 😊

 

I wouldn't worry too much about it. If this isn't run in any sort of loop the performance hit will be almost negligible.
We're talking on the order of a few hundred to a thousand CPU cycles in the worst case.
Which if it only run when the app loads or when the page opens will be, even on the slowest machine, a nanosecond blip.
Like if your wasting your time finding 0.00001 of a second to optimize you're looking in the wrong places.
The only case where you should care is if this were in a tight loop that was running millions of times.

 

Yeah, that's logical. Though, I'm not wasting such time on the nano optimization. At most, I use early exits and keep the rare scenario to the end. I think my discomfort is mental as I'm always anxious about such rarely used code without really doing anything about it.

 
 

It will be executed again won’t it? For every new user? I tend to think of my code on a broader scope, how it runs for all users across all time. It helps me sleep at night πŸ™‚

 

Indeed, however it still not executing for the same user, I don't know but I feel there's something wrong here πŸ˜‚. Some IFs may be scoped to the entire application or to check for a specific configuration. You may find them in distributed scripts for example :

if ( ! isAppProperlyInstalled() ) {
  die('Please, check the installation guide...');
}
 

Great, the Promise-like pattern that you presented in the first example is really handy and considerable. As I mentioned in other comment, my issue is mental and this absolutely can hide the imperfections. Thanks Neil 😊

 

How about to use javascript decorators?

github.com/tc39/proposal-decorator...

In the readme they even show @logged decorator as an example

Haha that's on point πŸ‘Œ