Yeah. I think the given advice probably takes validation logic and floats it too high. It is of course nice to have early validation logic, but it is also nice when your functions don't mysteriously crap out with some weird error but instead shout a validation error at you.
Haskell solves this with newtypes, “here is a transparent container that certifies that you did the appropriate validation already,” that helps for this.
The advice that I really want to hammer into people's heads is, prefer “sad ifs.” That is, I will almost always find this
if (something_is_wrong_in_way_1) {
// fix it or abort
}
if (something_is_wrong_in_way_2) {
// fix it or abort
}
if (something_is_wrong_in_way_3) {
// fix it or abort
}
more readable and maintainable than this
if (things_are_ok_in_way_1) {
if (things_are_ok_in_way_2) {
if (things_are_ok_in_way_3) {
// do the happy path!
} else {
// fix or abort thing 3
// if fixed, do the happy path
}
} else {
// fix or abort thing 2
// if fixed, test way 3 again
// if way 3 is good do the happy path, else fix it
// ...
}
} else {
// ...
}
I feel like it's in human nature to focus on the expected case, I want everyone whose code I meet to do the exact opposite, focus primarily on the unexpected. Every “if” imposes a mental burden that I am keeping track of, and if you have to go to an external system to fetch that information, or you need to exit early with an error, I can immediately discharge that mental burden the moment I know about it, if the handling and the detection are right next to each other.