Certainly is hard to read nested code, but returning earlier makes it harder because now our mind has to construct the execution path instead of reading it. What I usually do is writing the body in a new procedure. Dijkstra designed a language with no return statement with a very simple semantics made for proving the program behaves exactly as its specification mandates. Niklaus Wirth designed Oberon with no return statement. Currently I write Go code only using return at the end of the procedure, my code is easy to follow and has fewer errors than before https://en.wikipedia.org/wiki/Guarded_Command_Languagehttps://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.h...
Golang is not comparable, as you get to leave failures not handled explicitly because its goto-fail scheme. I mean, that's a very nice feature of Go, but not comparable.
The danger with early returns is failure to handle all cleanup. This is really a failure of many programming languages. Golang gets this right. But even so, I'd rather have early returns (or early goto-fails) than ever deeper if/else ladders.
Another option that works very well is this:
ret = thing1(...);
if (ret == 0)
ret = thing2(...);
if (ret == 0)
ret = thing3(...);
if (ret)
<handle failure>;
return ret;