Returning guards — I hate you!
Yes, I really hate them. Just look at them:
def meaningful_method_name(a, b)
return if a.excluding?
return unless b.turbo_not_excluding? # real behaviour
end
Ok, let’s replace them with no-premature return notation
def meaningful_method_name(a, b)
if a.excluding?
elsif !b.turbo_not_excluding?
else
# real behaviour
end
end
Or
def meaningful_method_name(a, b)
if !a.excluding? && b.turbo_not_excluding?
# real behaviour
end
end
Now you can see that this method has actually two levels of abstraction included — the actual behaviour and when this behaviour is expected. The latter should be treated as precondition — in perfect world as part of method signature, in less then perfects outside of this method. If you don’t want to use it’s behaviour in other circumstances — just don’t. If it’s guarding public method, available as API (like in gem) — don’t fail silently, use errors instead.
Usually the“real behaviour” will have another if’s to control flow — now you have nested ifs and you’re not even aware of it. You no longer have one refactoring step towards better code, but two steps. And what’s even more important — you don’t see the possibility.