Refactoring conditionals
Published: 2021-05-31 11:59:44


## Refactoring conditionals

Conditionals are supposed to add intelligence to code, but they can be a nightmare to understand.

![You need to decide which one](https://cdn-images-1.medium.com/max/8064/1*LGVAs3dCGgdUhbVdmlf_5w.png)

## Why refactor conditionals

Our targets when refactoring conditionals are:

**Making it easy to understand the conditions that we are checking**: conditions can be hard to understand if we check for specific values that are meaningfully related to the concept they try to evaluate. See this example:
{% gist https://gist.github.com/franiglesias/6209124d52a6ae69b82ee3050c36d791.js %}
The condition is fairly easy to describe in human terms: select documents that have too long names. But its code expression is really hard to understand. Let’s simplify this a bit, extracting the conditional expression to a method with a meaningful name.
{% gist https://gist.github.com/franiglesias/f79b6a8f0fb381204ce7016ccb35bce5.js %}
**Reduce the cognitive complexity when following the execution flow through their branches**. When you read a conditional structure you need to keep track of the main flow. If you introduce conditionals inside the branch of another conditional you are creating new breaking points that need to be tracked. At a certain level, you will overflow the ability of your working memory to handle all that tracking.

You can easily spot this by looking at the indentation levels of the code. The more indented it is, the more difficult it to understand and the easier it for bugs to appear.

You can manage this with several refactors. The most simple and easy to apply is to extract code in branches to methods. That opens new refactoring opportunities inside the private methods.

## Conditions with meaning

### You don’t always need conditions

We use conditionals to control the execution flow of a program: go this way if a condition is met. If not, go this other. But, sometimes, we can avoid that. Let’s look at this example:
{% gist https://gist.github.com/franiglesias/bb10f82733635db3ae1d4ec59e0c4a6c.js %}
We don’t need the flow control part here, we are served with the response from the object. Also, it’s pretty confusing that you return false in the true leg of the conditional.

To be precise, we can simply return the negation of that response:
{% gist https://gist.github.com/franiglesias/70a710f2403e3f3aa7ca4fe8722e8edb.js %}
This kind of refactor helps to avoid some potential points of failure. With the original code, you have to ensure no less than three things:

* The method invoked provides the correct value

* The conditional expression evaluates to the correct value

* Each leg of the conditional returns the correct value

Now, you only have to check one thing:

* The method invoked provides the correct value

## Ternary operator instead of if/then

The ternary operator is pretty convenient under certain conditions. You should use it carefully because it can bring more problems than solutions. But it is perfect for this situation:
{% gist https://gist.github.com/franiglesias/b819604866b4e07421cf7e8d16976ef5.js %}
When we have two possible ways of calculating a value depending on a simple condition, the ternary operator is perfectly fine:
{% gist https://gist.github.com/franiglesias/41d33a642ab5f868e95f24e79da84aec.js %}
The conditions to use the ternary operator are:

* We need to choose between two ways of calculating something

* We don’t nest ternary operators.

The benefit here is that we state clearly that we are calculating something (the **$event** that we want to send in some next step).

### If it’s complicated, extract a method

The ternary operator works pretty fine in the simple situations exposed before. But if the calculation is more complex or can’t be reduced to clean construction, you’d better extract a method and hide the conditional inside of it:
{% gist https://gist.github.com/franiglesias/e79ff6c05146561cb4f3e21f7a789446.js %}
### Replace type checking with type safety

Take a look at this code:
{% gist https://gist.github.com/franiglesias/3020079c381ad2393d180221d0a1ed08.js %}
This code reveals a big *bad smell*. If you need to check for type to perform a task, then you have a design problem. Probably, you are trying to ensure that the **Visit** has a related **Patient**, so you can do something with it.

*Does it make sense that a visit has no patient-related?* If not, you probably should have to fail with an exception. In fact, you should ensure that when instantiating the **Visit** object. Anyway, the **Visit::getVisitPatient** method should be typed to state that the expected outcome is a **VisitPatient**. By doing that, you can enclose the code block in a **try/catch** to manage the situation.
{% gist https://gist.github.com/franiglesias/2cd530d2af157f3325c9acde920b163d.js %}
**Visit::getVisitPatient()** guarantees that it only will return a **VisitPatient**. Otherwise, something is wrong.

If not having a **VisitPatient** is fine for **Visit**, then you should use **nullable** return type. This way, instead of checking for type, you will check for object existence, making code cleaner:
{% gist https://gist.github.com/franiglesias/ed6730bddc03cb0d305e0104732c48c8.js %}
You can do this because **getVisitPatient** guarantees that it will return a **VisitPatient** or a **null**, keeping consumers safe about the type, but having to manage the situation of empty value. So you simply need to check that **$visitPatient** has a value.

Also, remember that this example could reveal a violation of the **Tell, don’t ask** principle or **Demeter’s law**. It is possible that code related to **VisitPatient** can be happily encapsulated in **Visit**. You can take a look at [this previous article on the topic](https://medium.com/docplanner-tech/refactor-for-better-knowledge-allocation-ef83be9d44fe).

### Give combined conditions a meaning

Sometimes combined conditions represent a concept that is not explicitly put in code. Let’s see a pretty simple example:
{% gist https://gist.github.com/franiglesias/8becc1c72bc0f39296808bc7f07ec122.js %}
This line of code makes you think about what it is actually checking for. This line is in the context of an SMS notification service, so it makes sense to think about the idea of *a patient can be notified via SMS or not*. This can be achieved if both two conditions are met:

* The patient has a phone number (that can receive SMS)

* The patient chose to allow being notified with SMS

So, instead of asking about these two things to the same object, we can encapsulate both questions into only one, inside **VisitPatient,** because both conditions belong to the object:
{% gist https://gist.github.com/franiglesias/89949003c31a645e6004f53ba35dd68d.js %}
If we don’t have a better place to move the combined conditions, we can simply create a private method. This is the proper way when we are using values from different origins.

## Managing the flow

Conditionals are control flow structures and we use them to take different actions when certain requirements are met. We’ve been talking about the conditions, but now we will focus on their *branches*.

We can have different problems with the branches of conditional expressions:

* **Too large branches**: too much code in the body of a branch makes it easy to lose track of the context.

* **Unbalanced branches**: one of the branches is comparatively larger than the other, making the latter *invisible*.

* **Nested conditionals**: conditions inside conditions are especially difficult to understand and are a perfect place for intricated bugs to hide.

### Too large branches

The best approach is to extract branches to their own method with a descriptive name for the abstraction level. This will make it easier to understand the overall flow, and you always be able to dig into details if needed.

Look at this code. The branch is remarkably long:
{% gist https://gist.github.com/franiglesias/5b3a877c43e5b22ee439365544d02d71.js %}
We move all the code in the branch to a new method, taking care of managing how we return the data:
{% gist https://gist.github.com/franiglesias/fbcfcce306e4e3caa06afbe307649abf.js %}
Now, the main flow is far easier to read. We can dive into details by jumping into the extracted method. We even have a chance to extract this code to a collaborator in the future if we need to reuse this particular piece.

### Unbalanced branches

When one of the legs of a conditional structure is a oneliner and the other is huge, it is good practice to invert the conditional, so the shortest branch appears in the first place. This avoids missing it:
{% gist https://gist.github.com/franiglesias/469c12880609bd3092dda1615483d9eb.js %}
It’s much better to extract both branches to methods with a significant name. This way you stress the fact that two flows are depending on the **province** variable having value or not.
{% gist https://gist.github.com/franiglesias/0a1dc21aeb05dbee63dfc14bf420b80d.js %}
You should consider eliminating the **else** branch. Sometimes you won’t need it. For example, because you can perform an *early return*.
{% gist https://gist.github.com/franiglesias/db3a61a130ba938979abeea579281a0e.js %}
### Nested conditionals

Nested conditionals are a source of frustration and pain. They are difficult to follow and unsafe to modify when needed. How can we avoid them?

First of all, try to ensure that nesting is needed. In the following example, we can see that the inner conditional should be moved to another place. We can guess that by putting next, things that are related to each other. For example, the calculation of **validSince**.

The value of **validSince** can be calculated before, instead of waiting until being in the conditional. And we can introduce the **null coalesce operator** to simplify the assignment using the first value among all possible that it’s not null.
{% gist https://gist.github.com/franiglesias/6f51d9bf319f3eec8b58e52f197bc0d7.js %}
Our first approach will be to extract the main branches into private methods. Then, you repeat this extraction iteratively so all methods have a maximum of one indentation level in each one. This opens opportunities for further refactors that are pretty difficult, or impossible, in the nested structure. In fact, part of nested structure complexities relies on the fact that you have to carefully manage the execution flow to make sure you don’t overwrite variables or introduce some other bugs.

Let’s take a look at this code extracted from the famous [GildedRose kata](https://github.com/emilybache/GildedRose-Refactoring-Kata):
{% gist https://gist.github.com/franiglesias/0f3ad7b89abe39339b8d7393173a7aac.js %}
This is only half of the code and has a lot of nesting.

For this example, we extract the legs to their methods. Note that this kata deserves an object-oriented approach, but I want to illustrate a different point this time:
{% gist https://gist.github.com/franiglesias/4a1ead5d6df7a35cb1bd522b682b4cce.js %}
We can see that in the extracted methods the body is inside a conditional. We can apply a *return early* refactor that will reduce the nesting. We need to invert the conditional expressions first. As you can see, the readability has increased a lot.
{% gist https://gist.github.com/franiglesias/b901df3757ab0203a9da006e5dbc662a.js %}
We can apply *return early* again in the **increaseQuality** method:
{% gist https://gist.github.com/franiglesias/a1e76ec98590a0d2c5d3aefa4c06ab97.js %}
At this point, we can spot some remaining code duplication that we could refactor. It can be argued that the nesting is there yet. Nevertheless, the takeaway here is that with pretty simple steps we have structured the code so it is much more readable and easier to understand.

## Final words

Conditionals can be tricky to manage, but we can improve their readability a lot by using simple refactoring techniques and thinking twice about why we need them.

Of course, we could go further by applying a good object-oriented design that can even avoid the need for conditional structures. But that should be the topic for another post. In the meantime, you can watch [this talk by Sandi Metz about how and why to achieve this using the Gilded Rose example](https://www.youtube.com/watch?v=8bZh5LMaSmE).

Keep an eye on our blog for more tips about refactoring and testing to keep your code healthy.

Do you enjoy the article? Follow our profile and visit our other channels: [Docplanner.tech website](https://docplanner.tech/), [Facebook](https://www.facebook.com/docplannerTech/?__tn__=%2Cd%2CP-R&eid=ARCMkTULxdIBTOyout0irGzXnbLcnwj_n_n2_RY5mQGdpLjCbKM_VpAqBhN0B_FQD3ygzsJnYWK_Uf_1), [Twitter](https://twitter.com/DocplannerTech), and[ LinkedIn](https://www.linkedin.com/showcase/26236889/admin/).