Summary
In my previous post I brought up the topic of code formatting. This time I'd like to bump it up a level of abstraction to the structure of the code itself. To what extent do you think teams should establish policies for code structure, and what do you think is the best way to structure flow in the presence of potential errors?

Advertisement

In addition to fulfilling its primary responsibility, a method often has to handle potential error conditions. You can organize the control flow of such a method in many ways. I once had a manager who told me he preferred to see the path with no errors first, followed by error handling code. This was back when I was programming in C, but the style I adopted then still echoes in my code today. For example, I might structure a method that handles an HTTP POST from a form like this:

if (userIsSignedIn) {
// The user is indeed signed in, so keep going...
if (finished) {
// They pressed finished, so keep going...
if (!formErrors) {
// Actually do what the user was trying to
// accomplish with this form.
}
else {
// handle form errors
}
}
else {
// they pressed cancel, so abort
}
}
else {
// handle user not signed in problem
}

My C code back in those days, therefore, often had the shape of a big right angle bracket, with several cascading ifs going in, then the "success" code, followed by elses handling all the potential errors the method might encounter.

When I was discussing this issue with a fellow programmer (not a manager) back in those days, he said that he prefered a method's code to resemble more of a pipe character (|) than an angle bracket (>). He wanted the code to be flat against the left hand side. Thus he'd write the above code like this:

if (!userSignedIn) {
// handle user not signed in problem
return;
}
if (!finished) {
// they pressed cancel, so abort
return;
}
if (formErrors) {
// handle form errors
return;
}
// Actually do what the user was trying to
// accomplish with this form.

The benefit with this approach is that my colleague avoided the cascading indentation, but at the cost of multiple returns. I had heard some programmers complain that having multiple returns inside a method makes it harder to understand. I myself didn't find such methods harder to understand, but now that I've started using Scala for a few things, I've begun to see methods more as expressions. This mindset implies that a method contains just one expression that resolves to a value, which is returned.

But even if you take the single return approach in a method, you still may prefer to handle the errors first, because they are often shorter than the "success" code. That might look like this:

if (!userIsSignedIn) {
// handle user not signed in problem
}
else if (!finished) {
// they pressed cancel, so abort
}
else if (formErrors) {
// handle form errors
}
else {
// Actually do what the user was trying to
// accomplish with this form.
}

The benefit with this approach is that you can avoid both multiple returns from the method and the cascading indentation, but the errors are on top. Some may actually find this easier to read, but my old manager at least wouldn't like having to scan all the way to the bottom to find the code that we hope will be executing most of the time.

Lastly, one other possibility is to use exceptions to structure the flow of the method. For example:

The previous example is very contrived, but should demonstrate the potential structure. When I first read about exceptions in Java, I noticed that it allowed me to put error handling code after the success code. It flattened the success code and made it easier to read. But I didn't adopt exceptions for this kind of situation, in which I'm really trying to implement one method. (Notice that the methods throwing exceptions are all private, created solely to help me implement the HTTP POST handler method.)

My question today has two parts. First, which of these approaches (or perhaps you have a different approach to suggest) would you recommend for structuring the flow of methods that must handle potential errors? Second, in my experience, each individual programmer on a project would use his or her own preferred style of structuring such flow—in other words, how flow was structured was decided by personal coding style. Most people who participated in the discussion of my previous blog post seemed to feel that establishing a code style for a project was a good idea. But how far should that style policy go? Should it only deal with formatting issues such as curly brace placement? Or should it actually address coding structure issues, such as the one presented in this blog post? Please post your opinions in the discussion forum.

I would use exceptions in cases where I may or may not want to handle the error locally. It would be common to have a global method for redirecting users to a login page. There could also be generic "redisplay the form with errors" logic in the application to respond to the form error, so that could be an exception but is a little more iffy.

Look how much complexity the exception-based method adds, especially comparing to the pipeline (2). Exceptions work well in simple situations like this, but in real life the error processing code has actually to be mixed with the main flow for the sake of robustness. This is achievable with exceptions chained as in (2), but again, with significant clarity loss. Resume: exceptions work well if correctness is the only thing you care for.

Decorator seems like a good answer here, but oh my, the cost. Interfaces must be created, arguments passed to the methods must be sufficiently generic to handle each part of the processing yet specific enough to actually solve the problem, the business process is split across methods, exceptions must be defined and handlers inserted higher up the call stack. I like it, but I hate it more.

I really dislike how the method arguments are request and response, not LoggedInUser, ValidFormData, etc. Could they act as a Decorator without requiring the same interface?

I also dislike how many classes have to be created just to handle a simple case. There may be advantage to the LoginHandler being reused, but that's lessened if the "Decorator"-ness uses different interfaces. Similarly form validation, with similar costs.

I do like that the work of performing the business task is separated from the error-checking code, but I feel the asserts represent duplication instead of just communication.

I write and maintain code with these issues frequently, as do many of us. I've tried this "decorator" scheme to minimal gain, tried the big-right-angle-bracket, tried the if-not-return guard conditions. Do the cutting-edge functional languages have an answer to this, um, messiness? There has to be a way better type systems can improve this, right? Like http://blog.moertel.com/articles/2006/10/18/a-type-based-solution-to-the-strings-problem ... but how could we port this back into languages without such expressive type systems?

I'll start by commenting on the first three techniques, in reverse order...

Technique #3: Single If-Else Statement

Of the four techniques demonstrated for Bill Venner's sample problem, it's tempting to say the third is the best. That's the one written with a single if-else structure, avoiding both jump statements and exceptions. But as I see it, a team would get themselves into trouble by establishing technique #3 as a policy. It solves the sample problem well, but is inappropriate in more complex cases.

Consider, for example, the problem one encounters when loading data from a file. One must first check if the file exists, then later check whether the file's text is in the correct format. But in between these two checks, one must open the file and read its contents. Because of this "in between" code, the problem is awkward to formulate with a single if-else statement.

Technique #2: Multiple Return Statements

This technique handles the "in between" code I referred to above. Below I've copied Bill Venner's second example, but inserted two "in between" statements and turned the conditions into function calls. Let's assume that "do_something()" only succeeds if the user is signed in, and must be executed before "finished()" is evaluated. Similarly, "do_something_else()" must come before "formErrors()".

if (!userSignedIn()) { // handle user not signed in problem return;}

do_something();

if (!finished()) { // they pressed cancel, so abort return;}

do_something_else();

if (formErrors()) { // handle form errors return;}

// Actually do what the user was trying to// accomplish with this form.

We cannot easily transform this code into a single if-else structure, but the return statements give us an adequate solution. Does that mean a team should adopt technique #2 as policy instead? Again, I think not. As Nemanja Trifunovic pointed out, return statements are inappropriate for problems involving clean-up code.

Personally, I've come to dislike all jump statements (goto, early return, break, continue). Although they're very convenient, I feel they limit a programmer's options when deciding which sections of code to extract into smaller functions. I also don't like exceptions much, so here I'll consider the "nested if-else" technique demonstrated in Bill Venner's first example.

Technique #1 is both simple and general. But if it were adopted as a policy, it would lead the design of deeply-nested structures. Certain error-handling routines would inevitably be located far below the conditions that trigger them. In such cases, it's hard to figure out which "else's" correspond to which "if's".

Alternative: No Policy

Consider the following solution to the sample problem:

private void process_form() { if (!formErrors) { // Actually do what the user was trying to // accomplish with this form. } else { // handle form errors }}

Part of the problem has been extracted into a function called "process_form". This function might be useful if it can be re-used in a context in which the user's state is irrelevant. Also, a boolean named "ok" has been introduced, which might be useful in subsequent code. The above is not the best solution in general. But in a particular situation, it may be better than the code obtained through any of the three techniques.

Perhaps coding policies are helpful for simple, well-defined problems. But as a category, I think "error-handling" is too broad to select a single technique for all situations. Teams should therefore allow individual programmers to formulate their code in the way that best suits the situation at hand.