Look before you leap vs easier to ask for forgiveness rather than permission

A long-standing debate of clean code is look before you leap (LBYL) vs easier to ask for forgiveness rather than permission (EAFP). With LBYL, you use a conditional check before trying something which may fail. With EAFP, you put an action that may fail in a try block, without checking if it would work.

So, what should you use? Conditionals, or try / catch?

We’ll examine some pros and cons of each method.

Please note that the debate doesn’t apply to:

  • time-of-check-to-time-of-use (TOCTTOU) situations or race conditions. For example, you might try to check whether a file exists before you access it. But, in the time between checking and accessing, the file might have been deleted. In cases like these, you’ll have to use error handling constructs.
  • operations that don’t fail (don’t throw exceptions and don’t return error values). You’ll have to use conditionals for those.
  • error values. These can’t be used in all of the cases where exceptions can. For example, you can’t use error values to recover from the code null.doSomething() (unless you do something unusual like combine try and error values).

LBYL

Here’s some code where user.name can be null. To make it work with conditionals, you would code it like this:

const user = {
  name: 'Bob',
  age: null,
};

function handleUserEvent() {
  if (user.age === null) {
    // handle the error. E.g. tell user to submit their age
  } else {
    user.age *= 2;
  }
}

This style is referred to as "look before you leap" (LBYL).

EAFP

Here is the equivalent code using try / catch:

const user = {
  name: 'Bob',
  age: null,
};

function handleUserEvent() {
  try {
    user.age *= 2;
  } catch (error) {
    // handle the error. E.g. tell user to submit their age
  }
}

This style is sometimes referred to as "easier to ask for forgiveness rather than permission" (EAFP).

Please note that this style only seems to be used when the code in the try block is the common case. The code in catch should be "exceptional", it shouldn’t run often.

Deciding which to choose

When deciding which one to use, consider the convention in your programming language. Alternatively, you can make your own decision based on the characteristics of each method.

Convention in your programming language

As explained in programming principles, conventions are important.

"Look before you leap" (LBYL) is the convention used in languages such as C# and Java. For example, the .NET best practices for exceptions recommend using conditions instead of exceptions if possible.

"Easier to ask for forgiveness rather than permission" (EAFP) is the convention used in languages such as Python. For example, the Python glossary mentions that EAFP is more common in Python.

Characteristics

You can also make your own decision on which to use based on their characteristics. Here are some characteristics of each:

Condition checking placement

LBYL places condition checking at the top. EAFP places the common path at the top and the error handling at the bottom.

This is a stylistic preference.

Some people prefer the common path at the top. Personally, I prefer checking for errors (or conditions to avoid errors) first, as explained in best practices for error catching and handling.

Performance

EAFP can be more performant than LBYL. With conditionals, the condition check (the check for the error case) always executes. However, the error case tends to be rare.

With EAFP, there is no condition check. The code in the try block runs immediately.

If, the majority of the time, the code doesn’t error, then using EAFP can be more performant.

The performance increase is even higher if the conditional check is expensive (in terms of how long it takes to complete).

However, in many cases, the performance difference will be insignificant.

EAFP comes out slightly on top on this point.

Semantic meaning of exceptions

This one depends on your interpretation. In the Python community, "exception" = "exceptional" = "uncommon".

In other communities (or maybe just my personal interpretation), "exception" = "exceptional" = "never supposed to happen" = "bugs (and unavoidable things like TOCTTOU)".

Adding to the responsibility of try / catch

Try / catch (or other error handling constructs) are used for:

  • errors such as bugs. For example, you might have a try / catch block in a high-level function for defensive programming purposes.
  • time-of-check-to-time-of-use situations or race conditions

With EAFP, exceptions also effectively become conditional statements. This adds another use to exceptions.

It means that, when you see a try / catch block, you don’t immediately know if it’s there for defensive programming, for TOCTTOU checks, or as an alternative to a conditional statement.

It’s a minor point, but it’s something that people may not like.

Summary

Which one you use doesn’t really matter. The differences between them are very small. Pick the one which has the points you prefer.

Final notes

So that’s it for this article. I hope that you found it useful.

As always, if any points were missed, or if you disagree with anything, or have any comments or feedback then please leave a comment below.

For the next steps, I recommend looking at the other articles on clean code.

Alright, thanks and see you next time.

Image credits

  • Squirrel behind tree: Photo by Hasse Lossius on Unsplash
  • Squirrel in snow: Photo by Misty Ladd on Unsplash
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments