Handling unhappy paths is a healthy practice to be in the habit of, since it is guaranteed that not everything is going to work 100% of the time. I believe that this mindset is a key aspect that separates quality-oriented engineers from others. In my experience, these conversations create the most meaningful conversations with stakeholders, and provide the most valuable insight into the system's intended design. So long as a boundary is tied to one explicit exception that the business code defines, you will avoid any interface rewrites that are mentioned in this video. If a unit of code cannot handle the failure, pass it along. At the end of the day, somebody must pay the piper; and if the code doesn't, you will.
Somebody declaring exceptions because they don’t yet understand how the system will behave is not a problem with checked exceptions. They’ll watch this video and replace an unnecessary exception with an error status added to the return type and it won’t be any better. The fact that something can be used badly doesn’t mean that it doesn’t have its place.
error status codes are terrible.. exceptions were invented to solve horrible error management pattern. I'm dismayed that it's making a comeback in some of the new lame languages. It means their code will be riddled with error checking like in the old C days rather than a clean separation of error handling and business logic.
@@acasualviewer5861 It's a tricky one because on the one hand you have "this code might or might not go wrong and you can opt to handle some of the ways it might (or might not) fail if you feel like it" and on the other hand you have "these are all the ways this code can fail, you must do something about every single one of them". The first one leads to sloppy code where critical failures aren't well handled, and the second option leads to verbose code and error-handling exhaustion. In general, I think I'd rather have verbose code than miss seeing the problems that actually exist but it's definitely far from ideal. Ideally I'd have the editor summarising for me everything that could go wrong with each statement and then opt-in to handling the ones I care about but that becomes practically impossible as soon as things start getting tucked away behind interfaces that don't specify how they can fail. So far no language has come up with a really good middle-ground solution to this.
Since checked exceptions become part of the possible ways a method can respond to a message, saying not to use them until you know exactly what you want sounds like an argument not to us strongly typed return values. Should `String someMethod` not be `int someMethod`? APIs evolve necessarily as we learn more about the problem.
6:54 the problem here is you're talking leaky abstraction "oh now we've changed the implementation details, so the interface has changed". You're supposed to map the exceptions in the implementation to an exception, or set of exceptions, that are to be expressed by the interface to the caller. Without checked exceptions **you still have the leaky abstraction, it's just worse**
I agree entirely.. you must wrap the exceptions to the level of your business logic.. You should throw InventoryException, BankingException, etc.. not IOException or SQLException.
@@ToadalChaos not all exceptions need to be caught.. checked exceptions are for expected errors such as the network cutting out. Unchecked exceptions are for bugs that should never happen, and therefore should NOT be part of the interface. Examples: NullPointerException, DivideByZeroException, etc.. Your code should never produce them, but if they are produced there's a general error handler that will log them.
Forcing the caller to know all of the gory details about what specific internal thing went wrong and explicitly deal with it made for extremely fragile APIs. Most of the time, if it failed, there's little you can do besides report it and, if the API is idempotent, schedule a retry.
I agree that putting the exceptions into the signature is a bit clunky, but when early in my career I was (based on employer preferences) moved from Java to .NET one of the matters I missed was the compiler telling me that I had missed handling an exception (rather than having to read through documentation). I am not sure I would still miss it.
It's said to see supposed experts talk about this topic in such a superficial level. Java had clear intentions with their Checked Exceptions which is to enforce checks where the JVM could not give you a guarantee of safety. IOException is the best example here because the same method call can fail from one moment to the next. In other words, it is a type of exception caused by externalities which don't care about your API contract. If the caller breaks the API contact and provides a wrong variable, it is the callers fault and you deserve a runtime error. But then there are a host of issues which are not transparent and Checked Exceptions help you find out unexpected runtime error states. In languages without exception I often see no documentation on exceptions they can throw whatsoever and it frustrates me to no end to be faced with errors which nobody could anticipate and were not documented in the API or why they are thrown. But most of all, if you find your API design changing due to low-level details I start to notice a funny smell how you encapsulate and design your modules.
Idk. I would easily agree if methods without checked exceptions were guaranteed to not have this sort of external problem. And yet they often do. RuntimeException can happen and often does happen in places where you call standard Java libraries and they have some sort of external error. Lack of documentation in APIs goes both ways, I've seen APIs with checked exceptions that also don't bother explaining what the errors could be or that once again can actually break with RuntimeExceptions outside of the checked exceptions they publish. I've also seen APIs like mongodbs where all exceptions are RuntimeExceptions and yet they do explain all the exceptions that can go wrong. I do use and encourage checked exceptions, but the intention is really to mark most methods as "this one doesn't deal with exceptions" and others as "this one does". Usually I have a gigantic block in the 'main' method of some bounded context that handles the exceptions and makes sure things don't crash just because of them, and unfortunately, it has to be able to deal with RuntimeExceptions as well. The checked exceptions exist more as just a warning that the method is not doing dealing with that sort of thing.
Checked Exceptions are great. They leverage the compiler to ensure there's proper error checking. However, your business logic methods shouldn't throw IOException, SQLException, etc.. They should throw exceptions at the correct abstraction level. For example CouldNotCreateUserException, SaleNotCompletedException, InventoryException. It's pretty simple. It goes with the idea of leveraging the compiler to reduce bugs in your code. Frankly I can't understand why people dislike them (though the Java library does have some examples where exceptions aren't done well).
@@acasualviewer5861 i don't like them because they force me to pollute the majority of my code with "throws x" because exceptions often can't be reasonably handled anywhere except at "the outer shell". so, i use only unchecked exception and catch it at the end
Hi, the problem with runtime exceptions is when you are call an undocumented method and it throws a runtime exception that when you weren’t expecting it to throw a runtime exception at all.
I only use runtime exceptions for bug detection.. like NullPointerException or IllegalArgumentException. They shouldn't be caught except by error logging code. CheckedExceptions are used for known possible problems like a hard disk running out of space while writing, or a network glitch.
My main problem with how checked exceptions are implemented is that they force a design decision of someone else into my work. It's no longer possible to say "I don't care about this here, I will deal with it elsewhere". I have to deal with it right away or it will leak into many places. That is a decision which someone else can't and should not try to make. Software is design. Design the process to find a sweet spot between N incompatible goals. Any decision influences my position in this N-dimensional space and my ability to achieve my goals. If you force a fixed position in a certain dimension on me, you limit my freedom in many way and it's simply not your place to do so.
Does any language have inferred checked exceptions? Maybe that would be a nice compromise, so on each method you could choose to either specify the type of checked exception it throws (including none) or leave it unspecified and let the compiler work it out, similar to how e.g. the TS compiler can work out the return type of a function if it isn't specified explicitly. Then if your function starts to throw a new type of exception the compiler might still force to handle it but only in one place (and you'd choose where) instead of at every level of the call stack.
Rust doesn't support exceptions but it will return Result where E is an error. The compiler then collects all the possible values of E for you. Go might have something similar. I'm not aware of any language with Exceptions that can automatically compile an accurate list for the developer which is a big oversight in my opinion. It shouldn't be hard to add to the compiler; the only problem I see is that you might get problems when you replace a JAR dependency (by overwriting it) without recompiling. Not a huge problem in my experience.
Exceptions in Java is way better than exit(int) in C. All IO calls and GUI can be done with exceptions, but this should be done with care. Exceptions are quite similar to events and events have important role too.
Errors that need to be handled at one level are things that should propagate at another. Java exceptions are clunky because you can't express that cleanly. Java also couldn't support errors as return values nicely back in the day because you couldn't return multiple values from a call, and all non primitives were heap allocated when the vm itself could easily support multivalue returns directly on the stack. So checked exceptions seem to always make error handling the responsibility of everyone that can't do anything about it, and virtually everything you would do about it involves allocating new objects through the garbage collector.
You should think of data validation first. Not as an afterthought. It will always come to bite your behind when you least expect it. User of your API always should provide valid data, and it should be checked on input from API user. In other words, if they give you a hot potato, give it right back and shower them with some exception napalm. Your code should work even GIGO mode, as long as garbage input meets the validation.
Yes, check before use, but that's not the purpose of Exceptions, even though that's where it has trended to. You can predict (and defend) against something returning a null where that should in no sane way ever happen. But you CAN'T predict someone pulling a cable. That's what Exceptions are for, they are after all... exceptional.
@@ErazerPTThat is what IOException is automatically thrown for and you should have API user in their client code properly guarded against. And having null where null should not be is as much of an exception, and depending on how input is made, you have a much bigger problem in predicting those.
@@ErazerPTdata validation is a situation which you can recover from in general, so that would be checked exception. Runtime exceptions are meant in places where it would be fatal and no recovery is possible like null pointer execption.
Disagree partly if I get it rightly what is meant, the good thing about checked exceptions IS you get forced in principle to think about your design where in the tree you want it to be handled. Like controller in MVC as a single point and single responsibility. But I agree that it is better to not have many different types (perhaps even 1 generic, in the example below ProtocolException) of them so no signature change. And have it parameterized with a message describing why it happened (possibly with some more information) which then can be logged and act upon. But it is better than unchecked/runtime exceptions! As an example, if you implement a custom protocol, implement the error handling on behaviour level as a statemachine (state design pattern alike) and lower constructs, like (de)serialization errors of messages, which should not to be expected to happen, as an checked exception, in which the circustances can be logged, and then let the statemachine takes care of the further error or recovery handling of the protocol behaviour. In practice I indeed have seen bad practices acrosss juniors not thinking about design, because of lack of knowledge/experience or lazyness, and for example converting all over the place checked exceptions to runtime exceptions so they dont need to bother with it, creating a mess/technical debt for their own and their collegues.
The drawback of "throws ProtocolException" everywhere is that it gives you no information about which specific exceptions can happen, so you again have to look at the documentation (which can be outdated) or the source (which is tedious and error prone) when you can handle certain errors. That means you force people to handle exceptions that they usually don't care about while keeping vital information from them. I would prefer the compiler to collect this information automatically (so I get an accurate and complete list when I ask my IDE) and just forget about checked exceptions.
Checked exceptions sound like right biased result types in functional programming, however I have no complaint about the later. I wonder if there is a difference in the paradigms that make the same artefact a pain or a blessing. One thing that caught my attention is that you don't know how something may behave. The things you don't know are not recoverable errors, they are defects and should not be tracked, only logged at the edge of the program (and maybe some time later you will learn how to deal with them and define them out of existence or track tem).
The Result kind of approach works well because the compiler can give you a correct list of all type/values for "Error". The way exceptions in Java are designed makes it problematic to get an accurate list.
I rejected Checked Exceptions right out of the gate when I started with Java in 2002. I was saying to people that checked exceptions litter your APIs in ways that you can't properly predict what they should be in advance. Not to mention, it creates extra work. Back then, people were not happy with me about it.
Why not just make a checked exception that returns an Exception instead of the specific exception type then? Calling a method that you don't know can throw is common and dangerous. If you just add "throws Exception" to the signature, then as long as that method can still throw an exception (not likely to change unless your code changes drastically), the interface won't change even if the exception type does.
@@chriss3404 It's not dangerous, because the point of exceptions is primarily to identify bugs and handle system problems that might happen like network errors and what not. In either case, I want central error handling in my application, and I want there to be no actual bugs that could throw exceptions. So, when an exception occurs that is due to a bug, that bug gets fixed. System errors, nothing we can do about that.
I presume that the code you worked on could still fail in various ways - eg user passed invalid data, or purchased a product that is now sold out. So how did a function that detected such an error report the problem to its caller? If by having a return-type of form (error|success) then yes, that's a valid pattern. Functional programming often calls this type Either, Rust calls it Result, etc. This does have a performance impact as every call to such a function requires an instance of this type to be allocated, initialised, then evaluated and garbage-collected. But in "enterprise software" that may not be a measurable overhead except for very frequently called functions. It's a shame that Java doesn't have a native Either type, or equivalent. If however your code deals with such errors by throwing unchecked exceptions I'd have to disagree with this. Unchecked exceptions are good for cases where the caller really has no good way of dealing with the issue (eg OutOfMemory) but extensive use of these leads to unreliable software IMO.
Exceptions are great and can also simplify code, but they are also a double-aged sword. Great power comes with great responsibility, as is true for every language feature, paradigm and tool. Exceptions should not be involved in control flow for expected behaviour. After all, they are called exceptions for a reason. But they can reduce error checking code and allow for graceful handling of unexpected errors which cannot be resolved internally by the implemention. But using Exceptions as part of the API shifts the responsibility to the caller and breaks encapsulation (of behaviour, not state). I think that is the topic here, not that checked Exceptions are a bad idea.
You know, the further down the road i get, the more i hate exceptions. Not their existence, but their use. Exceptions should guard against exceptional circumstances, not as a "tshirt catch all umbrella" kludge. So much so, in all code parts i have "ownership" there a big try catch at every entry point. If is see something logged to that, we're having a talk. And if there's exceptions popping in the logs in methods were they didn't happen, we're having a talk too. Receivers handle their own ish, inform their callers. Callers check their results, do "whatever is appropriate". 99% of the code in use doesn't run on "extreme performance" scenarios, you save nothing by not being diligent and waste TONS of resources dealing with poor quality. The 1% that needs extreme performance is written by people that know wtf they're doing.
Seems that your problem is not exceptions but your problem is lack of software design: no single responsibility or place where things happen to be logged.
@@bjorn1761 How about brushing up on your reading and compression skills and not simply blurting out nonsense? If we didn't have really good logging (and monitoring) i wouldn't know if things were happening in the wrong place, would i? And if we didn't strive for good design, i wouldn't be on my team mates case for not handling their own issues and letting it blow on someone else's face, would i?
How can you ever build reliable software if you embrace emergent design while sticking your fingers in your ears when it comes to exceptions. If putting "throws FrameworkException" in your signature is the same as saying "nothing at all", then what does using unchecked exceptions say? (hint: it's absolutely nothing at all). You better hope it is documented and that you care to check it because the compiler won't save your ass.
What a bad take. In what way are the checked exceptions any different from returning a value that is either ok or failed? Arguing for ‘ignore error paths until we know the system in detail’ seems … strange.
Agreed. Checked exceptions are equivalent to signatures that return (error|success) or equivalent. The only alternative I know is failure-reporting via "unchecked exceptions" which is very good for those cases where the correct place to handle the failure is "somewhere central" - and very bad in all other cases.
Kotlin also introduced a bunch of new methods to their standard runtime which return errors instead of throwing exceptions - eg an "open file" operation which returns a failure status rather than throwing a FileNotFoundException. If you're writing Kotlin, you should use these and then deal with the returned status. If you continue using the "java-style" exception APIs with their (now) unchecked exceptions, you're asking for trouble. Or in other words, there are two valid error-handling styles: method-returns-status or method-throws-checked-exception. The third option (method-throws-unchecked-exception) is not a path to quality software IMO.
@simonk1844 when we deal with older Java APIs that throw exceptions, we have helper methods for those which also return Error results, so we show an appropriate error to user. It's much better than dealing with exceptions in Java (silently catching or constantly rethrowing), so much boilerplate.
Every enterprise system with which I have ever worked has used runtime exceptions, not checked exceptions. Say what you will, I have never, ever seen a large Java platform operate with checked exceptions (other than those thrown by the libraries we used). I think these guys are preaching to the choir.
Checked exceptions had clear and good intentions at first, sure, but they've been frowned upon for years already. In practice, checked exceptions have often led to excessive boilerplate code without necessarily improving error handling. In fact, a common misuse of exceptions (checked or unchecked) is using them to manage expected conditions or drive application logic. Ideally, exceptions should signal truly exceptional or irrecoverable situations-unexpected states where the application can’t simply “carry on.” When exceptions are used for controlling business logic, it tends to obscure the code’s readability, makes the flow harder to follow, and can lead to performance inefficiencies. For recoverable errors, patterns like result objects, error codes, or even optional types offer clearer, more predictable ways to manage alternative flows without masking application logic behind exception handling. No surprise no other mainstream language has copied Java’s checked exceptions, and even languages that run on the JVM, like Kotlin, Scala, and Groovy, chose to bypass them entirely.
i mostly agree. but i usually consider a violation of business logic, to be worthy of throwing an exception. i say "usually", because the frameworks i've worked with have a few places where they expect Error Values (like result objects). the problem with Error Values is that you typically get the Arrow Anti-Pattern
@@xybersurfer That's true. An example is the Result object of Kotlin that can end up in a very nested implementation. Usually a flatMap would solve this problem though and fortunately, Kotlin can be enhanced with one when using the Arrow library.
I disagree. Firstly, there is a role for unchecked exceptions - throw them when you are sure that the caller won't be able to recover from this problem. Things like OutOfMemory are a fine example but there may also be some application-specific cases where you just want to "give up on the current request" or similar. But for other cases, checked exceptions are exactly equivalent to returning a "status" from a method-call. Yes, dealing with checked exceptions is a nuisance, but dealing with status-codes is equally annoying - and equally important if you want to write quality software. Other languages have taken the path of "return status" which as I noted is exactly equivalent to checked exceptions. Well, except that they have poorer performance but may be more elegant to deal with using functional-programming-style techniques (monadic Either types, etc). That's why Kotlin and Scala have unchecked exceptions - not because you should _use them_ but because you should always use the "return status" approach instead.
@@simonk1844 you should use what is supported by your platform. functional programming languages typically have a compiler that forces you to do something with the return values. but most languages are imperative and have don't support this. if you use return values in those languages just because you like that style, then you will forget to check return values leading to silent errors. also partial function applications for monadic style error handling that is typical in functional programming, is not always supported, which will probably also result in the Arrow Anti-Pattern. so if you want reliable software, then the choice has typically already been made for you. i think the interesting discussion, is why checked exceptions are such a nuisance. the problem with checked exceptions is that they make the assumption that the caller always cares which specific error occurred. this is almost always a wrong assumption. not only because it assumes that the calling function is responsible for handling the error. most of the time, programs only care in it's decisions, whether the call failed or and pass on the error details to a human. it would be better if checked exception just advertise the possible exceptions and allow the caller to switch "something" on that forces them to check these for the few cases it actually matters, instead of introducing tight coupling that breaks all callers who don't even care about which specific errors can occur. technically, the fact that unchecked exceptions exist, already means that there is a hole in reliability of checked exceptions making them useless.
@@simonk1844 you should use what is supported by your platform. functional programming languages typically have a compiler that forces you to do something with the return values. but most languages are imperative and have don't support this. if you use return values in those languages just because you like that style, then you will forget to check return values leading to silent errors. also partial function applications for monadic style error handling that is typical in functional programming, is not always supported, which will probably also result in the Arrow Anti-Pattern. so if you want reliable software, then the choice has typically already been made for you. (editing...)
Handling unhappy paths is a healthy practice to be in the habit of, since it is guaranteed that not everything is going to work 100% of the time. I believe that this mindset is a key aspect that separates quality-oriented engineers from others. In my experience, these conversations create the most meaningful conversations with stakeholders, and provide the most valuable insight into the system's intended design.
So long as a boundary is tied to one explicit exception that the business code defines, you will avoid any interface rewrites that are mentioned in this video. If a unit of code cannot handle the failure, pass it along. At the end of the day, somebody must pay the piper; and if the code doesn't, you will.
Somebody declaring exceptions because they don’t yet understand how the system will behave is not a problem with checked exceptions. They’ll watch this video and replace an unnecessary exception with an error status added to the return type and it won’t be any better.
The fact that something can be used badly doesn’t mean that it doesn’t have its place.
it also doesn't mean that it does have a place
error status codes are terrible.. exceptions were invented to solve horrible error management pattern. I'm dismayed that it's making a comeback in some of the new lame languages.
It means their code will be riddled with error checking like in the old C days rather than a clean separation of error handling and business logic.
@@acasualviewer5861 It's a tricky one because on the one hand you have "this code might or might not go wrong and you can opt to handle some of the ways it might (or might not) fail if you feel like it" and on the other hand you have "these are all the ways this code can fail, you must do something about every single one of them". The first one leads to sloppy code where critical failures aren't well handled, and the second option leads to verbose code and error-handling exhaustion.
In general, I think I'd rather have verbose code than miss seeing the problems that actually exist but it's definitely far from ideal.
Ideally I'd have the editor summarising for me everything that could go wrong with each statement and then opt-in to handling the ones I care about but that becomes practically impossible as soon as things start getting tucked away behind interfaces that don't specify how they can fail.
So far no language has come up with a really good middle-ground solution to this.
Since checked exceptions become part of the possible ways a method can respond to a message, saying not to use them until you know exactly what you want sounds like an argument not to us strongly typed return values. Should `String someMethod` not be `int someMethod`?
APIs evolve necessarily as we learn more about the problem.
6:54 the problem here is you're talking leaky abstraction "oh now we've changed the implementation details, so the interface has changed". You're supposed to map the exceptions in the implementation to an exception, or set of exceptions, that are to be expressed by the interface to the caller.
Without checked exceptions **you still have the leaky abstraction, it's just worse**
I agree entirely.. you must wrap the exceptions to the level of your business logic.. You should throw InventoryException, BankingException, etc.. not IOException or SQLException.
Why worse? Asking for a friend.
@@alrightsquinky7798 because you're still throwing the (wrong) exceptions, but in a way that it harder for the client to handle appropriately
This.
Just because you're not declaring the exceptions in the function signature doesn't mean they're not part of the interface.
@@ToadalChaos not all exceptions need to be caught.. checked exceptions are for expected errors such as the network cutting out.
Unchecked exceptions are for bugs that should never happen, and therefore should NOT be part of the interface.
Examples: NullPointerException, DivideByZeroException, etc..
Your code should never produce them, but if they are produced there's a general error handler that will log them.
Forcing the caller to know all of the gory details about what specific internal thing went wrong and explicitly deal with it made for extremely fragile APIs. Most of the time, if it failed, there's little you can do besides report it and, if the API is idempotent, schedule a retry.
I agree that putting the exceptions into the signature is a bit clunky, but when early in my career I was (based on employer preferences) moved from Java to .NET one of the matters I missed was the compiler telling me that I had missed handling an exception (rather than having to read through documentation). I am not sure I would still miss it.
It's said to see supposed experts talk about this topic in such a superficial level. Java had clear intentions with their Checked Exceptions which is to enforce checks where the JVM could not give you a guarantee of safety. IOException is the best example here because the same method call can fail from one moment to the next.
In other words, it is a type of exception caused by externalities which don't care about your API contract. If the caller breaks the API contact and provides a wrong variable, it is the callers fault and you deserve a runtime error. But then there are a host of issues which are not transparent and Checked Exceptions help you find out unexpected runtime error states.
In languages without exception I often see no documentation on exceptions they can throw whatsoever and it frustrates me to no end to be faced with errors which nobody could anticipate and were not documented in the API or why they are thrown.
But most of all, if you find your API design changing due to low-level details I start to notice a funny smell how you encapsulate and design your modules.
Idk. I would easily agree if methods without checked exceptions were guaranteed to not have this sort of external problem. And yet they often do. RuntimeException can happen and often does happen in places where you call standard Java libraries and they have some sort of external error.
Lack of documentation in APIs goes both ways, I've seen APIs with checked exceptions that also don't bother explaining what the errors could be or that once again can actually break with RuntimeExceptions outside of the checked exceptions they publish. I've also seen APIs like mongodbs where all exceptions are RuntimeExceptions and yet they do explain all the exceptions that can go wrong.
I do use and encourage checked exceptions, but the intention is really to mark most methods as "this one doesn't deal with exceptions" and others as "this one does". Usually I have a gigantic block in the 'main' method of some bounded context that handles the exceptions and makes sure things don't crash just because of them, and unfortunately, it has to be able to deal with RuntimeExceptions as well. The checked exceptions exist more as just a warning that the method is not doing dealing with that sort of thing.
Checked Exceptions are great. They leverage the compiler to ensure there's proper error checking.
However, your business logic methods shouldn't throw IOException, SQLException, etc.. They should throw exceptions at the correct abstraction level. For example CouldNotCreateUserException, SaleNotCompletedException, InventoryException.
It's pretty simple. It goes with the idea of leveraging the compiler to reduce bugs in your code. Frankly I can't understand why people dislike them (though the Java library does have some examples where exceptions aren't done well).
@@acasualviewer5861 i don't like them because they force me to pollute the majority of my code with "throws x" because exceptions often can't be reasonably handled anywhere except at "the outer shell". so, i use only unchecked exception and catch it at the end
@@acasualviewer5861 having always to convert the abstraction level introduces more churn
ask yourself why ArithmeticException are unchecked. you should always be ready for an unexpected error
Hi, the problem with runtime exceptions is when you are call an undocumented method and it throws a runtime exception that when you weren’t expecting it to throw a runtime exception at all.
I only use runtime exceptions for bug detection.. like NullPointerException or IllegalArgumentException. They shouldn't be caught except by error logging code.
CheckedExceptions are used for known possible problems like a hard disk running out of space while writing, or a network glitch.
My main problem with how checked exceptions are implemented is that they force a design decision of someone else into my work. It's no longer possible to say "I don't care about this here, I will deal with it elsewhere". I have to deal with it right away or it will leak into many places. That is a decision which someone else can't and should not try to make. Software is design. Design the process to find a sweet spot between N incompatible goals. Any decision influences my position in this N-dimensional space and my ability to achieve my goals. If you force a fixed position in a certain dimension on me, you limit my freedom in many way and it's simply not your place to do so.
This is a really bad take. The topic is declaration of errors, but the. Guest talks about bad api change practices. Bad Choice for a yt snippet.
You think? Ok I thought that divided space between understood and less solid and understood methods or classes made perfect sense to me.
Does any language have inferred checked exceptions? Maybe that would be a nice compromise, so on each method you could choose to either specify the type of checked exception it throws (including none) or leave it unspecified and let the compiler work it out, similar to how e.g. the TS compiler can work out the return type of a function if it isn't specified explicitly.
Then if your function starts to throw a new type of exception the compiler might still force to handle it but only in one place (and you'd choose where) instead of at every level of the call stack.
Rust doesn't support exceptions but it will return Result where E is an error. The compiler then collects all the possible values of E for you. Go might have something similar. I'm not aware of any language with Exceptions that can automatically compile an accurate list for the developer which is a big oversight in my opinion. It shouldn't be hard to add to the compiler; the only problem I see is that you might get problems when you replace a JAR dependency (by overwriting it) without recompiling. Not a huge problem in my experience.
Note: The compiler might even be able to get a correct list of wrapped exceptions (i.e. what you might expect in the "cause" field).
Exceptions in Java is way better than exit(int) in C. All IO calls and GUI can be done with exceptions, but this should be done with care. Exceptions are quite similar to events and events have important role too.
Errors that need to be handled at one level are things that should propagate at another. Java exceptions are clunky because you can't express that cleanly. Java also couldn't support errors as return values nicely back in the day because you couldn't return multiple values from a call, and all non primitives were heap allocated when the vm itself could easily support multivalue returns directly on the stack. So checked exceptions seem to always make error handling the responsibility of everyone that can't do anything about it, and virtually everything you would do about it involves allocating new objects through the garbage collector.
You should think of data validation first. Not as an afterthought. It will always come to bite your behind when you least expect it. User of your API always should provide valid data, and it should be checked on input from API user. In other words, if they give you a hot potato, give it right back and shower them with some exception napalm. Your code should work even GIGO mode, as long as garbage input meets the validation.
Yes, check before use, but that's not the purpose of Exceptions, even though that's where it has trended to. You can predict (and defend) against something returning a null where that should in no sane way ever happen. But you CAN'T predict someone pulling a cable. That's what Exceptions are for, they are after all... exceptional.
@@ErazerPTThat is what IOException is automatically thrown for and you should have API user in their client code properly guarded against. And having null where null should not be is as much of an exception, and depending on how input is made, you have a much bigger problem in predicting those.
@@ErazerPTdata validation is a situation which you can recover from in general, so that would be checked exception. Runtime exceptions are meant in places where it would be fatal and no recovery is possible like null pointer execption.
Disagree partly if I get it rightly what is meant, the good thing about checked exceptions IS you get forced in principle to think about your design where in the tree you want it to be handled. Like controller in MVC as a single point and single responsibility. But I agree that it is better to not have many different types (perhaps even 1 generic, in the example below ProtocolException) of them so no signature change. And have it parameterized with a message describing why it happened (possibly with some more information) which then can be logged and act upon. But it is better than unchecked/runtime exceptions! As an example, if you implement a custom protocol, implement the error handling on behaviour level as a statemachine (state design pattern alike) and lower constructs, like (de)serialization errors of messages, which should not to be expected to happen, as an checked exception, in which the circustances can be logged, and then let the statemachine takes care of the further error or recovery handling of the protocol behaviour. In practice I indeed have seen bad practices acrosss juniors not thinking about design, because of lack of knowledge/experience or lazyness, and for example converting all over the place checked exceptions to runtime exceptions so they dont need to bother with it, creating a mess/technical debt for their own and their collegues.
The drawback of "throws ProtocolException" everywhere is that it gives you no information about which specific exceptions can happen, so you again have to look at the documentation (which can be outdated) or the source (which is tedious and error prone) when you can handle certain errors. That means you force people to handle exceptions that they usually don't care about while keeping vital information from them. I would prefer the compiler to collect this information automatically (so I get an accurate and complete list when I ask my IDE) and just forget about checked exceptions.
Checked exceptions sound like right biased result types in functional programming, however I have no complaint about the later. I wonder if there is a difference in the paradigms that make the same artefact a pain or a blessing.
One thing that caught my attention is that you don't know how something may behave. The things you don't know are not recoverable errors, they are defects and should not be tracked, only logged at the edge of the program (and maybe some time later you will learn how to deal with them and define them out of existence or track tem).
The Result kind of approach works well because the compiler can give you a correct list of all type/values for "Error". The way exceptions in Java are designed makes it problematic to get an accurate list.
I rejected Checked Exceptions right out of the gate when I started with Java in 2002. I was saying to people that checked exceptions litter your APIs in ways that you can't properly predict what they should be in advance. Not to mention, it creates extra work. Back then, people were not happy with me about it.
Why not just make a checked exception that returns an Exception instead of the specific exception type then?
Calling a method that you don't know can throw is common and dangerous.
If you just add "throws Exception" to the signature, then as long as that method can still throw an exception (not likely to change unless your code changes drastically), the interface won't change even if the exception type does.
@@chriss3404 It's not dangerous, because the point of exceptions is primarily to identify bugs and handle system problems that might happen like network errors and what not. In either case, I want central error handling in my application, and I want there to be no actual bugs that could throw exceptions. So, when an exception occurs that is due to a bug, that bug gets fixed.
System errors, nothing we can do about that.
I presume that the code you worked on could still fail in various ways - eg user passed invalid data, or purchased a product that is now sold out. So how did a function that detected such an error report the problem to its caller?
If by having a return-type of form (error|success) then yes, that's a valid pattern. Functional programming often calls this type Either, Rust calls it Result, etc. This does have a performance impact as every call to such a function requires an instance of this type to be allocated, initialised, then evaluated and garbage-collected. But in "enterprise software" that may not be a measurable overhead except for very frequently called functions. It's a shame that Java doesn't have a native Either type, or equivalent.
If however your code deals with such errors by throwing unchecked exceptions I'd have to disagree with this. Unchecked exceptions are good for cases where the caller really has no good way of dealing with the issue (eg OutOfMemory) but extensive use of these leads to unreliable software IMO.
If time available is less than 10min then read the transcript.
I deployed Dynatrace 11 years ago
Exceptions are great and can also simplify code, but they are also a double-aged sword.
Great power comes with great responsibility, as is true for every language feature, paradigm and tool.
Exceptions should not be involved in control flow for expected behaviour. After all, they are called exceptions for a reason.
But they can reduce error checking code and allow for graceful handling of unexpected errors which cannot be resolved internally by the implemention.
But using Exceptions as part of the API shifts the responsibility to the caller and breaks encapsulation (of behaviour, not state). I think that is the topic here, not that checked Exceptions are a bad idea.
You know, the further down the road i get, the more i hate exceptions. Not their existence, but their use. Exceptions should guard against exceptional circumstances, not as a "tshirt catch all umbrella" kludge. So much so, in all code parts i have "ownership" there a big try catch at every entry point. If is see something logged to that, we're having a talk. And if there's exceptions popping in the logs in methods were they didn't happen, we're having a talk too. Receivers handle their own ish, inform their callers. Callers check their results, do "whatever is appropriate".
99% of the code in use doesn't run on "extreme performance" scenarios, you save nothing by not being diligent and waste TONS of resources dealing with poor quality. The 1% that needs extreme performance is written by people that know wtf they're doing.
Seems that your problem is not exceptions but your problem is lack of software design: no single responsibility or place where things happen to be logged.
@@bjorn1761 How about brushing up on your reading and compression skills and not simply blurting out nonsense? If we didn't have really good logging (and monitoring) i wouldn't know if things were happening in the wrong place, would i? And if we didn't strive for good design, i wouldn't be on my team mates case for not handling their own issues and letting it blow on someone else's face, would i?
Finally somebody is talking about it
How can you ever build reliable software if you embrace emergent design while sticking your fingers in your ears when it comes to exceptions. If putting "throws FrameworkException" in your signature is the same as saying "nothing at all", then what does using unchecked exceptions say? (hint: it's absolutely nothing at all). You better hope it is documented and that you care to check it because the compiler won't save your ass.
What a bad take. In what way are the checked exceptions any different from returning a value that is either ok or failed? Arguing for ‘ignore error paths until we know the system in detail’ seems … strange.
Agreed. Checked exceptions are equivalent to signatures that return (error|success) or equivalent. The only alternative I know is failure-reporting via "unchecked exceptions" which is very good for those cases where the correct place to handle the failure is "somewhere central" - and very bad in all other cases.
That's why Kotlin creators moved away from the idea of using checked exceptions
Kotlin also introduced a bunch of new methods to their standard runtime which return errors instead of throwing exceptions - eg an "open file" operation which returns a failure status rather than throwing a FileNotFoundException. If you're writing Kotlin, you should use these and then deal with the returned status. If you continue using the "java-style" exception APIs with their (now) unchecked exceptions, you're asking for trouble.
Or in other words, there are two valid error-handling styles: method-returns-status or method-throws-checked-exception. The third option (method-throws-unchecked-exception) is not a path to quality software IMO.
@simonk1844 when we deal with older Java APIs that throw exceptions, we have helper methods for those which also return Error results, so we show an appropriate error to user.
It's much better than dealing with exceptions in Java (silently catching or constantly rethrowing), so much boilerplate.
Every enterprise system with which I have ever worked has used runtime exceptions, not checked exceptions. Say what you will, I have never, ever seen a large Java platform operate with checked exceptions (other than those thrown by the libraries we used). I think these guys are preaching to the choir.
the comments here suggest otherwise
Do we really need to have all this subway-surfer-for-boomers stuff floating around in the background?
Hah terrible distracting isn't it? :)
Let it crash!
Checked exceptions had clear and good intentions at first, sure, but they've been frowned upon for years already. In practice, checked exceptions have often led to excessive boilerplate code without necessarily improving error handling.
In fact, a common misuse of exceptions (checked or unchecked) is using them to manage expected conditions or drive application logic. Ideally, exceptions should signal truly exceptional or irrecoverable situations-unexpected states where the application can’t simply “carry on.” When exceptions are used for controlling business logic, it tends to obscure the code’s readability, makes the flow harder to follow, and can lead to performance inefficiencies.
For recoverable errors, patterns like result objects, error codes, or even optional types offer clearer, more predictable ways to manage alternative flows without masking application logic behind exception handling.
No surprise no other mainstream language has copied Java’s checked exceptions, and even languages that run on the JVM, like Kotlin, Scala, and Groovy, chose to bypass them entirely.
i mostly agree. but i usually consider a violation of business logic, to be worthy of throwing an exception. i say "usually", because the frameworks i've worked with have a few places where they expect Error Values (like result objects). the problem with Error Values is that you typically get the Arrow Anti-Pattern
@@xybersurfer That's true. An example is the Result object of Kotlin that can end up in a very nested implementation. Usually a flatMap would solve this problem though and fortunately, Kotlin can be enhanced with one when using the Arrow library.
I disagree.
Firstly, there is a role for unchecked exceptions - throw them when you are sure that the caller won't be able to recover from this problem. Things like OutOfMemory are a fine example but there may also be some application-specific cases where you just want to "give up on the current request" or similar.
But for other cases, checked exceptions are exactly equivalent to returning a "status" from a method-call. Yes, dealing with checked exceptions is a nuisance, but dealing with status-codes is equally annoying - and equally important if you want to write quality software.
Other languages have taken the path of "return status" which as I noted is exactly equivalent to checked exceptions. Well, except that they have poorer performance but may be more elegant to deal with using functional-programming-style techniques (monadic Either types, etc). That's why Kotlin and Scala have unchecked exceptions - not because you should _use them_ but because you should always use the "return status" approach instead.
@@simonk1844 you should use what is supported by your platform. functional programming languages typically have a compiler that forces you to do something with the return values. but most languages are imperative and have don't support this. if you use return values in those languages just because you like that style, then you will forget to check return values leading to silent errors. also partial function applications for monadic style error handling that is typical in functional programming, is not always supported, which will probably also result in the Arrow Anti-Pattern. so if you want reliable software, then the choice has typically already been made for you.
i think the interesting discussion, is why checked exceptions are such a nuisance. the problem with checked exceptions is that they make the assumption that the caller always cares which specific error occurred. this is almost always a wrong assumption. not only because it assumes that the calling function is responsible for handling the error. most of the time, programs only care in it's decisions, whether the call failed or and pass on the error details to a human.
it would be better if checked exception just advertise the possible exceptions and allow the caller to switch "something" on that forces them to check these for the few cases it actually matters, instead of introducing tight coupling that breaks all callers who don't even care about which specific errors can occur. technically, the fact that unchecked exceptions exist, already means that there is a hole in reliability of checked exceptions making them useless.
@@simonk1844 you should use what is supported by your platform. functional programming languages typically have a compiler that forces you to do something with the return values. but most languages are imperative and have don't support this. if you use return values in those languages just because you like that style, then you will forget to check return values leading to silent errors. also partial function applications for monadic style error handling that is typical in functional programming, is not always supported, which will probably also result in the Arrow Anti-Pattern. so if you want reliable software, then the choice has typically already been made for you. (editing...)