I really like the F# guidelines for exceptions, which boils down to "use exceptions for exceptional circumstances." For example, a validation error is not really exceptional in my book. I expect them to happen. So I had rather represent them as just a normal return value instead of throwing an exception. (Plus, I can only throw one exception but I might want to return all validation errors that I can find.)
Perhaps a dependent service being down is an exceptional circumstance, perhaps not. If you are writing an internal monolithic app, it probably is exceptional in the worst way for your database to be down. But if you are doing microservices, then maybe it's not that exceptional to be unable to contact another service, so you treat it as a normal condition and implement a Circuit Breaker to handle it.
IOW, what's "exceptional" depends on the situation.
In F# specifically, most of the libraries in the ecosystem are written in C# and use exceptions for any and every kind of error. For cases which are not actually exceptional for that program, I have to catch them and return a normal value instead.
We’re a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.