1. 11
    1. 4

      IIRC “single exit” wasn’t supposed to mean “only exit from one place” but “only exit TO one place.” Things like exceptions break that, as the function can now exit both to the main path and to the exception handler.

      1. 4

        I’d heard this too. Seems it’s another piece of Fortran-inherited folk wisdom, like goto being inherently evil.

        https://softwareengineering.stackexchange.com/a/118793

      2. 3

        Erm, okay, if that happens to work for you (for some definition of “works for you”), great. I’m happy for you. Power to you. Keep doing it. But here’s why I deliberately code with almost the polar opposite of this strategy:

        Refactoring to use guard clauses and multiple returns does not actually change the tree of execution paths. A simple example (in Ruby): Consider these two method bodies.

        if foo
          process foo
        else
          raise 'error'
        end
        

        is equivalent to

        raise 'error'  if ! foo
        process foo
        

        But the second one doesn’t resemble or represent the execution path tree like the first one does. So, in your effort to reduce whitespace or indentation, you’ve actually hid or obscured that tree, instead of communicating the tree more obviously and clearly.

        We do want to reduce conditional branching (ABC score, Cyclomatic Complexity, yadda yadda), but I think a far superior way to approach refactors like this is to outright remove conditions with strategies like the Null Object Pattern. Example:

        class Foo < Model; end
        
        foo = Foo.find(id)
        
        if ! foo.nil?
          foo.bar
        end
        

        can become

        class Foo < Model; end
        class NilFoo
          def bar; end
        end
        
        foo = Foo.find(id) || NilFoo.new
        foo.bar
        

        where there is no conditional used any more.

        Yes, I’m aware we can write foo&.bar in the first snippet, but the power of the Null Object Pattern is still applicable in non-trivial, real-world cases where you’d otherwise be littering your code with nil checks (thereby increasing the ABC score). And, yes, I understand that using || in lieu of the nil check in this example does not reduce the number of execution paths, but that’s because this example is so simple. That single || can take the place of many multiple nil checks in a real-world codebase.

        1. 1

          Yes, I’m aware we can write foo&.bar in the first snippet, but the power of the Null Object Pattern is still applicable in non-trivial, real-world cases where you’d otherwise be littering your code with nil checks (thereby increasing the ABC score)

          What’s wrong with increasing the ABC score?

          1. 3

            The theory goes that more complicated code is harder to safely change and harder to debug. My personal experience bears this out.