I'm trying to imagine worst case scenarios here, and at least in an environment like JavaScript, I'm struggling to see how a beginner being overeager with early returns can possibly create anywhere near the same degree of mess as a someone overusing nested if/else statements.
Agreed on early returns being questionable for "two variations of the same concept" though, these days I generally use if/else for situations like that.
That's why newer languages have try-finally.
Basically
Allocate resource
try
guard clause (early return)
logic
finally
deallocate
Basically, the try-finally clause guarantees that the finally clause is run when there is an early return.
Writing clean and maintainable code is as art and requires balancing many variables. Sometimes an early return is warranted, sometimes an if/else might make more sense. If you're writing C code and you need to release some resource before returning (mutex, file handle etc...) then an early return might be error-prone and an "else" or "goto" might result in cleaner and more readable code. I use gotos a lot in error handling in C, I think it's a superior pattern to the one proposed by TFA if you have a lot of cleanup to do. In C++ and other object-oriented languages it's less of an issue thanks to RAII and/or GC.
Beyond that if/else might be more readable if both clauses are on the same "level" semantically. For instance if you write:
It looks like `do_a` and `do_b` are not on the same "level", semantically speaking. If instead they're two variations of the same concept then an else might make more sense.