2 minute read

Imagine this scenario: A restaurant cook learned from their grandmother the secret of her tasty soup. “Always put a tablespoon of salt in your soup”. And so they follow the advice, every day, no exception – and the customer reviews are, well, not great. “Disgusting”, says a food critic in their local newspaper. The cook’s mistake: Ignoring the context in which the advice is valid. Maybe the grandmother cooked her soup always in the same pot, but in the restaurant they cook much greater amounts in far bigger pots, and so the soup tastes bland. Or the grandmother used to cook for the whole big family, but the cook makes each meal individually for their sophisticated clientele, and so the soup tastes of nothing but salt.

This is what it feels like to me when programmers blindly follow or propagate advice like “never use multiple returns, they make the code harder to understand” or “always return immediately, because reassigning variables makes the code harder to understand” – ironically, you often hear exactly opposite advice justified with the same reasoning. Context matters! Taste your own soup!

Take this pseudocode example. The “single or multiple returns” example is only used to illustrate the general idea.

// Is this really easier to understand ...
var result;
if (condition) {
    result = A;
} else {
    result = B;
}
return result;

// ... than this?
if (condition) {
    return A;
} else {
    return B;
}

Some languages provide constructs for using expressions instead of procedural statements, which then result in a single return – but shouldn’t the advice (in this specific example) be “always use expressions when possible”?

// if-expression
return if (condition) {
    A
} else {
    B
}

// switch-expression
return switch (expression) {
    case a => A
    case b => B
}

// ternary expression
return condition ? A : B;
// or
return if (condition) A else B

What about a more complicated/nested example with resource allocation? If we need to free up the resources before returning, we might need to make sure to do so in all cases. Most modern languages have constructs that ensure deallocation of resources (like file handles) before returning – so shouldn’t the advice (in this specific example) be “always free up resources before returning” or even “never use multiple returns when you allocate resources”?

allocateResources();

if (collection is empty) {
    // Oops, leaked resources!
    return defaultValue;
}

foreach (element in collection) {
    if (element.predicate()) {
        // Oops, leaked resources!
        return element;
    }
}

deallocateResources();
return defaultValue;

We’re just going from one soup advice to the next. The most reliable solution is to taste your own soup: Write the guard clause, see if it makes the code simpler to understand. Try the result variable and the single return at the end, and see if it makes the code harder to understand in your specific case. We should start more advice with “consider X” or “be aware of Y” instead of “always/never do Z”. Consider the alternatives in your specific situation, try both variants, be the first and most meticulous code reviewer of the code you write, and taste your soup. Modern IDEs help doing so more quickly and safely.

Quick action “Convert switch statement to expression” in Visual Studio. Rather than only implying the correct way to express the intention in the code this allows for quickly and safely trying multiple variants and deciding which one is easiest to understand.