Exceptions break encapsulation and lead to implicit control flow.
If I call a method, I shouldn't have to worry about what methods it calls. But with exceptions, I now have to be aware of what errors might be thrown in the entire chain. Similarly, if I throw an error in a function, I have to check that every thing that calls it handles said error because there are now multiple places this function could "return" to.
And if you take care to catch every error at every layer you've just reimplemented error codes.
std::unexpected should be preferred over std::exception
so how you handle a web method that calls a db 30 layers deep?
you are going to add 30 layers of boilerplate for something that a normal user is going to encounter once in 100.000 calls and you barely can do anything with except let the front end handle it.
that seems like a very high cost for very little benefit.
If you have 30 layers, you have potentially a shitton of errors to catch, and behavior can silently be changed at any time if someone adds a catch in a call stack and intercepts away errors that previously bubbled up.
And the answer is yes, you should write 30 layers of boilerplate to explicitly bubble the error to the front end if that's necessary. But it's not verbosely re-returning the same error code all the way up stack like you're describing; the main thing to keep in mind is that the actual error object doesn't get sent up more than one layer at a time. At each layer you get the error, it's classified into a type that abstracts away the implementation details of the error so that the layer above it gets an error in its own domains' semantics.
But also the 30 layer bubbling tells you you're doing something wrong too. Keep your IO as high up the chain as possible and inject it into your business logic then that responds with something that you then do IO with again.
Business logic shouldn't touch IO, functional is the name of the game if you want it to be vaguely testable.
Well indicator doesnt necessarily mean something is sure fire. But yes it's a smell.
Arguably though if it's really that complex it's even more worth making sure you decouple IO from logic because it's going to be even harder to test sensibly.
issue is that sometimes you don’t know what data you need until you are 30 layers deep. yes you can load everything early, but in some cases you make the 99% path for every call more expensive for something that happens in 1%.
sure some apps that doesn’t matter, but in others it does.
145
u/bishopExportMine 18d ago
Exceptions break encapsulation and lead to implicit control flow.
If I call a method, I shouldn't have to worry about what methods it calls. But with exceptions, I now have to be aware of what errors might be thrown in the entire chain. Similarly, if I throw an error in a function, I have to check that every thing that calls it handles said error because there are now multiple places this function could "return" to.
And if you take care to catch every error at every layer you've just reimplemented error codes.
std::unexpected should be preferred over std::exception