Sub standard housing

Of Coding Standards and Good Code Pt 1

In today’s lesson, we will talk about state management and how not to use booleans for states.

Does it work? Ship It!

We will not sit here and pretend (except for Dave and his standing desk, Dave is a INSERT INSULT HERE) none of us have done the “If it works ship it” approach before. You get caught in the crunch, your managers are breathing down your neck about deadlines and you ship code like this:

boolean isLoading = false;
boolean isDoneLoading = false;
boolean loadingError = false;

UserData userData;

void loadUserData(){
	isLoading = true;
    try {
	  userData = someAsyncNetworkCall();
	  
	  isDoneLoading = true;
	  isLoading  = false;

  } catch (Exception e) {
	loadingError = true;
	isLoading  = false;
	}
}

You have readable, beautiful variable names, you handle the three possible states you can have and all is well. Not exactly. There is a bug hiding in there and you will find it when you try to check for isLoading and isDoneLoading which can both be true in some instances. The moment you start juggling booleans, you are writing very bad code.

The Eight States

Our code from above actually creates eight states, not three! The number of possible states is 2^n where n is number of booleans. We are going to end up handling nonsensical states like if(!isLoading && !loadingError) which we should not need to check for. This phenomenon is called boolean explosion. We have defined implicit states without knowing it. You can solve this bug by writing defensive code to prevent states that should not exist but you may not want to go down that rabbit hole.

Show Us The Way, Oh Great Sensei

You need to enumerate your states.

enum LoadingState {
  IDLE,
  LOADING,
  LOADED,
  ERROR
}

LoadingState loadingState = LoadingState.IDLE;

void loadUserData(){
	loadingState = LoadingState.LOADING;
    try {
	  userData = someAsyncNetworkCall();
	  
	  loadingState = LoadingState.LOADED;

  } catch (Exception e) {
	loadingState = LoadingState.ERROR;
	}
}

You can then check and set your current state using the loadingState variable. While enums are not available in every language (Vanilla JS is one) you may have to get creative by adding a finally block and setting the booleans there. But if you arrive at such a scenario, you need to take a step back and consider a redesign.