Throwing away Exceptions as a tool is a terrible idea. (“Go away Rust, you’re drunk”)
You’ve described several different types of problems in your example code and attributed all of them to … not being able to specify a code-enforced-equivalent of @throws
on an interface?
The action of sending an email, or any other secondary task, should be to push an item onto a queue. That queue handles failure notices and retries independent of the rest of the order processing.
Returning an error state can cause callers to accidentally ignore an error state from a returned object. If they’re Throwable then the caller can’t ignore them accidentally. I’m of the mind that it’s better to force things to be explicitly handled than to allow something to be accidentally ignored.
Using return
to transport errors encourages a ‘return hell’ similar to a ‘callback hell’, where every method now has to wrap every other method with an error state. Prior to Exceptions in PHP this was a massively repeated boiler-plate pattern in Gallery and was annoying to work with because almost none of calls checking for and returning the error cared beyond “I can’t continue working, return up the call stack” until it got to the top-level handler anyway.
Counter proposal: Create a base exception that allows including Context. Create an extension for each sub-system that codifies specific values (e.g. your failure reason).
There is merit in both. The return type is often used in functional programming languages for example and is favored over exceptions (which are considered to be a side effect). BUT those languages also have ways to force clients to deal with the error situations. The new match expression in PHP 8 comes close to this, and I’d love to experiment more with result types and matches in my code base once the upgrade to 8 is done within my company.
The action of sending an email, or any other secondary task, should be to push an item onto a queue.
You are picking on the example to dismiss the whole thing. Fine, you can use a queue, but that doesn’t change OP’s argument.
I do agree with your other arguments though, I just thought picking on that example to turn the discussion around wasn’t fair.
The email was just an example, I agree normally you would queue it.
If I were reviewing a PR with your suggestion, I would likely accept it, however I do think in a place where interop is the goal, there isn’t a way to enforce the exception to be thrown. Don’t misunderstand, I’m not saying throw away exceptions in all cases.
The counter to your point though is that in other places where errors are returned instead of thrown, it forces you to deal with it there, not allow it to bubble and get swallowed elsewhere. It is a problem either way you do the error handling, but at least in this way it is codified in the interface and not “hopeful” the correct exception is thrown. The exceptions for something like a vendor class should be handled as close to where they occur as possible, otherwise your consumer has to know details of the implementation.
FIG RFCs also allow for occasionally having some base exceptions to be thrown, but there’s no way to enforce that in the language.
However, in PHP, the interface can’t specify thrown exceptions. Comments (@throws
) don’t count.
If you use @throws
, which all frameworks and PSR do, the the whole article is pointless.
Why do you think we should not use @throws
? That’s exactly what it’s for. I think the article should start with that.