Error Handling and If Statements
Today's post will be a short one.
It's about error handling.
Error handling is one of those things which is pretty important if you do not want your code to die of fixable causes all the time. On the other hand, it's not especially exciting to read. It also tends to overcomplicate code by a decent amount. For example, look at this example:
bool do_the_thing(Input& input, Frobnicator& pipeline)
{
if (input.valid())
{
if (pipeline.capacity() > 3)
{
Output output;
int result = pipeline.handle(input, APPLY_SUBZERO_TEMPERATURE, &output);
if (result == -1)
{
LOG(WARNING) << "Couldn't handle input";
return false;
}
else
{
collect_statistics(result, &output);
}
}
else
{
LOG(ERROR) << "Our pipeline capacity has been exceeded";
return false;
}
}
else
{
LOG(ERROR) << "Invalid input";
}
return true;
}
You might have skipped actually reading this giant chunk of code above. After all, you were promised an interesting blog post, not a wall of imaginary if statements, right?
But then what if we just remove all the error handling and focus on the interesting part?
bool do_the_thing(Input& input, Frobnicator& pipeline)
{
Output output;
int result = pipeline.handle(input, APPLY_SUBZERO_TEMPERATURE, &output);
collect_statistics(result, &output);
}
We applied infinite amounts of optimism... and also assumed that the checks don't actually do anything stateful. (They better don't.)
There's a nice middle ground though! Of course, code style is subjective, but I do happen to like writing such code in the following way:
bool do_the_thing(Input& input, Frobnicator& pipeline)
{
if (!input.valid())
{
LOG(ERROR) << "Invalid input";
return false;
}
if (pipeline.capacity() <= 3)
{
LOG(ERROR) << "Our pipeline capacity has been exceeded";
return false;
}
Output output;
int result = pipeline.handle(input, APPLY_SUBZERO_TEMPERATURE, &output);
if (result == -1)
{
LOG(WARNING) << "Couldn't handle input";
return false;
}
collect_statistics(result, &output);
return true;
}
You can flatten out the if statements, replacing error handling branches of the large tree with just returns. Instead of forever staying in "the branch that has succeeded so far for all the checks, so that's why we're 3 levels deep now", we just deal with the bad cases first, so that we can afford forgetting about them afterwards.
It's easier to read because each time you see an if statement that vaguely resembles error handling, you can just skip the entire thing and don't even look at what's in the if statement. You can also be pretty sure that if you somehow get to the end of this piece of code, nothing bad happened in the process.
(We do like code that you don't have to read a lot better than the alternative.)
That's all. Might be obvious, and it's definitely not as weird as some opinions on TCP ports, but it might make your code a little bit nicer. If you agree with all this, that is. Subjectively.