Discussions

News: Subbu Allamaraju on "Exception Design and Usability"

  1. Subbu Allamaraju, in "Exception Design and Usability," has written up a discussion of proper exception design versus what often gets encountered in the field.
    Most of us consider exception management one of the simplest issues in programming. Since exceptions are easy to throw and catch in programming languages like Java, exception design rarely takes priority in design discussions. Not surprisingly, one of the least considered aspects of designing exceptions is usability. Yes, usability of the API being designed should be the primary focus when designing and implementing exceptions. This is more so when the software is layered, like most software today is.
    He ends with a note about unchecked exceptions, saying that they're not bad, but they "deemphasize usability."

    Threaded Messages (46)

  2. ...when written by a developer who names interfaces like "ICoffee"? :)
  3. Like someone also noted in a comment at the blog itself, I’m particularly disturbed by the last example ( {…} catch(x) { throw y;} ). What he’s doing there provides the most difficult to debug errors imaginable. Even worse than getting an NPE. For someone who’s trying to advocate giving more thought to designing your exceptions I find this quite embarrassing.
  4. Ok, I admit, {...} catch (x) {} is even worse :p
  5. That is precisely the point[ Go to top ]

    The developer needs to be equally careful when throwing exceptions. Secondly, exception paths are less frequently tested while writing tests, and coding errors like this can leak release guidelines easily.
  6. Exception handling[ Go to top ]

    Like someone also noted in a comment at the blog itself, I’m particularly disturbed by the last example ( {…} catch(x) { throw y;} ). What he’s doing there provides the most difficult to debug errors imaginable. Even worse than getting an NPE. For someone who’s trying to advocate giving more thought to designing your exceptions I find this quite embarrassing.

    I don't think ( {…} catch(x) { throw y;} ) is wrong, in fact sometimes is quite a good practice. Imagine you're developing a framework that exposes some API to work with. Now some method states that it throws XYZExeption. The developer who uses your framework certaily wouldn't want to catch a RuntimeException or even bother about any other exception besides the one(s) that your API specifies. So using this approach you can guarantee that you're not throwing any other exceptions besides the ones declared. More then that the underlying component that uses your code becomes more robust.
  7. Exception handling[ Go to top ]

    I don't think so either. I think it's normal practice to encapsulate exceptions through application layers - sth like: catch(x) { /* do sth */ throw new Y(x); }.
  8. Ahem. Those are two different principles:
    catch(X e) { throw new Y() } // non-nesting, hides original exception
    vs.
    catch(X e) { throw new Y(x) } // nesting, original included in stack trace

    First is evil and has no reason to be used. Second is much better, although exceptions nested 5 times or more are quite ugly and are usually the result of code being split into too many layers.
  9. As usually happnes a minute after pressing "Reply" button, I remembered a situation in which non-nesting is preferred: original exception's class is not in the (remote) client's classpath.
  10. As usually happnes a minute after pressing "Reply" button, I remembered a situation in which non-nesting is preferred: original exception's class is not in the (remote) client's classpath.
    Yes I have seen this one too. One way around it is to declare the nested exception variable reference in the wrapping exception as transient. This will fix the client classpath problem because it will not be propogated (serialized) to the client, but can still get logged on the server just before crossing the boundary.
  11. As usually happnes a minute after pressing "Reply" button, I remembered a situation in which non-nesting is preferred: original exception's class is not in the (remote) client's classpath.

    Or when original exception isn't serializable.
  12. Ahem. Those are two different principles:catch(X e) { throw new Y() } // non-nesting, hides original exception vs. catch(X e) { throw new Y(x) } // nesting, original included in stack traceFirst is evil and has no reason to be used. Second is much better, although exceptions nested 5 times or more are quite ugly and are usually the result of code being split into too many layers.

    A non-nested exception is evil only if the the newly thrown exception isn't verbose enought to explain the exception condition. If you are catching something like a NPE and there is a specific use case that creates that NPE then I would prefer a non-nested and verbose custom exception. That is more readable to me than a seemingly infinate stack trace polluting log files.

    John Murray
    Sobetech
  13. Nesting vs. non-nesting exceptions[ Go to top ]

    A non-nested exception is evil only if the the newly thrown exception isn't verbose enought to explain the exception condition. If you are catching something like a NPE and there is a specific use case that creates that NPE then I would prefer a non-nested and verbose custom exception. That is more readable to me than a seemingly infinate stack trace polluting log files.John MurraySobetech

    I disagree. I'd strongly suggest nesting exceptions that are caught and re-thrown in a custom exception.

    While I couldn't agree more that the custom exception's message should attempt to be as helpful as possible. This can include the values of method parameters and state information at the time of the exception. That said, I'd still rather have the whole story end up in my log file. Many times the stack trace will tell you exactly the line or method that created the exception.

    Exceptions, used only as exceptions, should be rare in a stable code base. Even when there are many (say the exception was thrown in a loop), filtering out the message portion of a exception is usually trivial.

    Anyway, I'm sure there are exceptions to my guidelines but it is general-case advice.
  14. Nesting vs. non-nesting exceptions[ Go to top ]

    Imho not nesting is (almost) never a good idea. What you want to do is hide these details from the end user. What you don't want to do is hide them from the person who has to figure out what’s wrong, or the developer that wants to catch and analyze the exceptions. And in the example it’s pretty bad I think: the author is catching an NPE (or perhaps the three dots mean catch everything) and assumes that this indicates a configuration exception. And NPE could indicate a lot of other things besides an error in the configuration (a bug in the code for example) and it is imho one of the exceptions that should never ever be trashed. But more in general: just because you can’t see any particular use for the nested exception when you write the code doesn’t mean there isn’t one. I’ve had many debugging-sessions that could have been avoided if the root-cause of the problem had been propagated down to the logfile. The most memorable one was with ant (I think version 1.5; they might have changed it now). When they catch a ClassNotFoundException they trash it and throw their own exception containing a page-long explanation that probably there is a library missing or there’s an error in your classpath etc… Very interesting but it’s kind of hard to figure out which library is missing if they don’t tell you which class was not found. Now you might argue that if they had included the classname in their message all would have been ok. But who is to say that in JDK6 a ClassNotFoundException won’t contain additional useful info? Or that I’m writing an IDE and want to inspect the exception in order to handle it intelligently?
  15. Checked exceptions only[ Go to top ]

    I don’t understand how “usability” can be applied to exceptions and the last example is very bad, but a proper exception handling can definitely help to manage the code and resolve problems. The design of exception handling should be considered from full life cycle point of view (where coding is not the biggest or most expensive part) and is linked to logging strategy.
    After I was involved in troubleshooting of an application with threading problems I will never use runtime exceptions because they hide important information regarding application’s state.
    Imagine that we have a framework, which should catch exceptions and leave appropriate log records. This framework calls class A, which holds some important information regarding application’s state. The class A calls class B, where exception occurs. If class B throws runtime exception, the valuable information from class A won’t be included into exceptions and therefore lost from the logging. Switching on debug option in a logging framework often does not help because it will decrease performance, pollute log file, information will be spread in log file because of multithreading, etc.
    The better option would be to use nested checked exceptions with simple coding rules, such as:
    1. If we don’t know how to handle an exception and it’s not defined in our method’s signature – chain it and add all valuable information. Don’t just throw a new exception taking a message from the caught one.
    2. Log exceptions only when chaining is terminated or not possible (for instance leaving a container).
    3. Obviously, java.lang.Exception should not be used, but generic package/service level exceptions are totally acceptable.
    This approach lets exception chains to accumulate all useful information and leave a single log record only when an exception occurs. So, it produces an “ideal” log file and it’s free.
    And I agree with a point of view that exceptions are part of an API and should be defined in method signatures. Otherwise, the API introduces a culture of ignorance of exceptions and is an example of unrespectable development. It’s much easier to use something like Checkstyle and identify all empty catch blocks than to identify a threading problem in a production environment when runtime exceptions are used and a log file does not really help.
  16. UnChecked exceptions only[ Go to top ]

    I don’t understand how “usability” can be applied to exceptions and the last example is very bad, but a proper exception handling can definitely help to manage the code and resolve problems.

    I agree with most of what you say IF one is authoring the entire application software body of code.

    However, when code re-use is involved, it is impossible to pass a proprietary checked exception through a third-party body of code.

    A case in point is if one is writing a SAX component. One cannot just add the checked exception MyUseDidSomethingDumb to say endElement(). However, if the exception is unchecked, it can be thrown and caught on the other side.

    As larger applications are authored, this become a more dominate issue.

    Steve Punte
    JXReports
  17. Checked exceptions only[ Go to top ]

    I think it is a very bad idea to use checked exceptions just to help caller to log error. This kind of "aspect" like unhandled error logging doe's not need to be repeated after every method call. Just throw error as runtime exception or checked exceptions if you like it more, but log all errors including runtime exceptions in single palace like this "AOP" code:
     
    abstract class MyThread extends Thread(

      public final void run(){

           try
             {
               doRun();
              }catch(Throwable unhandledError){
                  log(unhandledError);
             }
         

      }
        protected
        abstract
        void doRun();

    )
  18. Checked exceptions only[ Go to top ]

    I think it is a very bad idea to use checked exceptions just to help caller to log error. This kind of "aspect" like unhandled error logging doe's not need to be repeated after every method call. Just throw error as runtime exception or checked exceptions if you like it more, but log all errors including runtime exceptions in single palace like this "AOP" code...
    I agree that you should log errors in a single place, however, the question is how much information will be accumulated by the time you log a error. A runtime exception will contain information from a class where the exception occurs only. Other classes from the stack will contribute in the stack trace only and their state will be missing.
  19. Checked exceptions only[ Go to top ]

    Probably I do not understand it, do you have some usefull information in exception wrappers ? I found original error only is usefull for error diagnostic, wrappers more confuse than help.
  20. Checked exceptions only[ Go to top ]

    I guess that original poster was saying is that by catching and re-throwing exception each class can log it's arguments. With runtime exception you will see the call stack, but not the arguments for each method in the call stack.
    Having said that I think that AOP might allow to implement this functionality easier. In AspectJ it's possible to apply advice on all methods when these method return by throwing an exception. As exception propagates (bubbles-up) aspects can use reflection to detect method arguments and log them (if so desired, it can be controlled via external switch). Tris way you have a best of both worlds: <br/>
    • Runtime exception is used, thus coders do not need to bother wasting time writing exception propagation code
    • A full information about the exception can still be obtained for each method in the call stack (including arguments).
    <br/>
    The only drawback is the fact that AOP is used.
  21. Checked exceptions only[ Go to top ]

    One of good ways to solve debug problem is to use debuger, this is development time "aspect" anyway. I use it every day and I do not have any problems with debug, it doe's not pollute code with antipatterns and it gives more information than wrapped error. I do not think error driven debug or AOP can replace this tool. If I need to debug some "real time" code then "trace" helps better then error too.
  22. Checked exceptions only[ Go to top ]

    I forget about assertions in this context, it helps to debug, but it is not related to error driven design too. As I understand checked exceptions is just useless and misused feature in JAVA language and in standard API design.
  23. What we need here is Rod Johnson to chime in and tell us about unchecked exceptions :-)
    I am a very strong proponent of unchecked exceptions if application cannot recover from them. What is the point to force a client to catch some API-specific exception if client cannot do anything about it? If client needs to distinguish between errors as a result, it could catch some Runtime-derivative API exception. Alternatively, if recovery is possible, I believe checked exception is appropriate. Don't take out your machine guns and shoot me over this, however, this is my (and other's) preference and is a matter of your style.
  24. I am a very strong proponent of unchecked exceptions if application cannot recover from them.

    That should cover about 98% or more of all exceptions thrown in an application. In my experience, checked exceptions are a good way to annoy a developer, keeping him busy with try-catch-rethrowUncheckedException exercise.

    There were though a couple of cases when I found checked exceptions to be helpful.

    --
    Igor Zavialov, Factoreal Corp.
    Factoreal Web Service API for Financial Data
  25. One of the guidelines on generating exceptions says that, exceptions should not be used for the purpose of flow control or other "logical" errors. But, the line between an "error" and an "exception" is blurred, so it is not always easy to determine how the situation should be handled. In many APIs with checked exceptions, the exception is really an error condition. Consider for example, getMethod() API from Java reflection, which throws NoSuchMethodException. Is this really a case for using an exception? If the method doesn't exist, it should return null or a predefined value, since this is actually an acceptable behavior of the API. In other words, the case for checked exceptions is not always clear (IMHO anyway!) and in many cases, they are used incorrectly. In dictionary, exception is defined as, "An instance that does not conform to a rule or generalization." But, we don't use it that way in programming, at least, in case of checked exceptions. Taking the getMethod() example again, it also throws SecutityException, which is a valid case for using exceptions (though it is still questionable how the client is supposed to respond to this exception)

    Exceptions are probably one of the areas in software arena, which were not well thought of.

    Peace.
  26. In my experience, checked exceptions are a good way to annoy a developer, keeping him busy with try-catch-rethrowUncheckedException exercise.
    This is exactly what developers think simply because they DO NOT CARE about anything beyond the coding.
    There were though a couple of cases when I found checked exceptions to be helpful.
    One of the cases I see is when the code needs to be maintained or to be deployed into live environment. Otherwise, you're right.
  27. This is exactly what developers think simply because they DO NOT CARE about anything beyond the coding.

    I think your argument is too generic to be meaningfully discussed. Perhaps the title of this article says it all and it'd be best for us to just agree that we disagree in our views on checked exceptions.

    Regards,

    --
    Igor Zavialov, Factoreal Corp.
    Financial Data and Technical Analysis solutions.
  28. This is exactly what developers think simply because they DO NOT CARE about anything beyond the coding.
    One of the cases I see is when the code needs to be maintained or to be deployed into live environment. Otherwise, you're right.

    :-)

    +1.

    Nikita.
    GridGain Systems.
  29. One of the cases I see is when the code needs to be maintained or to be deployed into live environment. Otherwise, you're right.
    It is a good joke, but it just proves the point, checked exceptions are usefull jokes only.
  30. What is the point to force a client to catch some API-specific exception if client cannot do anything about it? If client needs to distinguish between errors as a result, it could catch some Runtime-derivative API exception. Alternatively, if recovery is possible, I believe checked exception is appropriate.

    I think this is one of the most common antipatterns in Java programming: people catch too much. Most of the time you don't have to catch (checked) exceptions if you don't know how to handle them: just add them to your method's throws statement.

    Implementing interface methods (like run in Runnable) is more problematic, because you are not allowed to throw checked exceptions. But I think that's more of a design bug in the interface than something to blame checked exceptions for.
  31. But I think that's more of a design bug in the interface than something to blame checked exceptions for.
    I do not think it is an interface design bug, there are many ways to implement interface and it can be many implementation related exeptions. Caller has no chance to handle or recovery from implementation related exception, implementation can be replaced using configuration (runtime).
     All exceptions are fatal in this case (runtime) and checked exceptions force to use antipatterns like error wrappers (or crap like GenericError in interface declaration). Probably checked exeptions have some good use case, but I have never sow it in paractice.
  32. Subbus's rebuttal :)[ Go to top ]

    http://www.subbu.org/weblogs/welcome/2005/08/more_on_excepti_1.html#more
  33. Subbus's rebuttal :)[ Go to top ]

    Yes, "exception design" or "design by exception" is error prone and it is an error itself. This programming mistake in example doe's not surprise too.
  34. Subbus's rebuttal :)[ Go to top ]

    BTW it looks like author just forget about debug level logging for usability and tries to solve the same problem using error.
  35. Probably checked exeptions have some good use case, but I have never sow it in paractice.

    As someone pointed out, exceptions are a fundamental part of a method's signature: the method takes in x and either returns y or throws z.

    Of course many unanticipated situations are best handled by unchecked exceptions, but let's take the DAO pattern as an example. It is perfectly natural and even probable that the DAO layer will fail at some point.

    Adding "throws DaoException" to each method in the DAO interfaces clearly states this and makes it harder to forget that persistence can and will fail sometimes. Why not utilize the power of the compiler to make sure each "normal" error condition is handled.

    Sure, many (most) programmers catch too much and maybe that's the reason checked exceptions are avoided (pearls for pigs?). But if used correctly, they are really useful.
  36. It is perfectly natural and even probable that the DAO layer will fail at some point.Adding "throws DaoException" to each method in the DAO interfaces clearly states this and makes it harder to forget that persistence can and will fail sometimes.
    "throws DaoException" doe's not make any sence, it doe's not tell how to recovery from error, any method can fail, but you do not declare "Throwable" in every method. If implementation can not recovery from "UnknownDOError" then caller has less chances to do it too.

    BTW this article just demonstrates flawed design by error. All "coffeeMaker" problems can be solved using classic creation design patterns, design must help to avoid errors not to force error prone code (factory must return object with valid state or just declare parameters in constructor ).
  37. "throws DaoException" doe's not make any sence, it doe's not tell how to recovery from error, any method can fail, but you do not declare "Throwable" in every method.

    I think you missed my point entirely.

    You don't have to know how to recover to be allowed to throw exceptions. If your boss tells you to buy a computer and you find out the company has no credit, are you allowed to report that to your boss even if you don't know how to solve your company's financial problems?

    And no, in the sense I mean it, not any method can fail. If you are referring to OutOfMemoryError or something unexpected like that, it's another problem and out of scope here. I'm talking about anticipated exceptions.
    design must help to avoid errors not to force error prone code

    I'm sorry to disappoint you, but some problem domains do contain error conditions as first class citizens :(

    Network failure is a good example: it's not a software bug, it's a real phenomenom in the real world, and you must handle it just like other, more "normal" phenomena.
  38. I'm sorry to disappoint you, but some problem domains do contain error conditions as first class citizens :(Network failure is a good example: it's not a software bug, it's a real phenomenom in the real world, and you must handle it just like other, more "normal" phenomena.
    Ok, if it is a good example then probably you know how to recovery from network failure, how doe's error handler code fixe network ? If it just logs error then it means this code just crashes in the user friendly way, it doe's not recovery from failure, so it is an runtime error too.
  39. Good design doe's not cause recoverable errors (see Go4 design patterns, all of them avoid errors), if it happens then you can not recovery from error (or design flawed design doe's not prevent errors ) So all exceptions are "unchecked" if API is designed in the right way.
  40. Ok, if it is a good example then probably you know how to recovery from network failure, how doe's error handler code fixe network ? If it just logs error then it means this code just crashes in the user friendly way, it doe's not recovery from failure, so it is an runtime error too.

    Logging as a replacement for proper error handling is another antipattern; I didn't suggest it.

    Just look around and you'll see how programs recover from network failures. Depending on the situation they either retry later or show an error message and exit. It's quite everyday if you think of it: "I can't buy a computer, we don't have credit. What shall I do?"
  41. Ok, if it is a good example then probably you know how to recovery from network failure, how doe's error handler code fixe network ? If it just logs error then it means this code just crashes in the user friendly way, it doe's not recovery from failure, so it is an runtime error too.
    Logging as a replacement for proper error handling is another antipattern; I didn't suggest it.Just look around and you'll see how programs recover from network failures. Depending on the situation they either retry later or show an error message and exit. It's quite everyday if you think of it: "I can't buy a computer, we don't have credit. What shall I do?"
    Classic command design pattern is used for retry and it doe's need any design by error, "show an error message and exit" is a crash, it is not a recovery. Design Patterns are about common practice, "exception design pattern" is an antipattern by definition, it is an exceptional case, exception is not a rule and it can not be a design pattern. I am sure it is possible to avoid all "checked" exceptions without any design by error and new design science, old plain Go4 dessign patterns solve "recoverable" or "checked" exeption problem.
  42. Controlled failure != crash[ Go to top ]

    "show an error message and exit" is a crash, it is not a recovery.

    !!!??

    Tell me this: if you try to download a file using wget, and the re is no network connection, how would you like wget to act? Keep on trying?

    I'm not the only one happy with the current functionality: it tries a few times and then it exits.

    See

    http://www.faqs.org/docs/artu/ch01s06.html#id2878538 (Rule of Repair: Repair what you can — but when you must fail, fail noisily and as soon as possible)

    and

    http://www.faqs.org/docs/artu/ch01s06.html#id2878145 (Rule of Robustness: Robustness is the child of transparency and simplicity)
  43. Controlled failure != crash[ Go to top ]

    Tell me this: if you try to download a file using wget, and the re is no network connection, how would you like wget to act? Keep on trying?I'm not the only one happy with the current functionality: it tries a few times and then it exits.
    Sorry, I do not understand how it is related to exceptions and design, are you sure wget uses checked exceptions for "Controlled failure" and this design makes it more robust ?
    BTW all software failures are controlled in some way.
    Seehttp://www.faqs.org/docs/artu/ch01s06.html#id2878538 (Rule of Repair: Repair what you can — but when you must fail, fail noisily and as soon as possible)andhttp://www.faqs.org/docs/artu/ch01s06.html#id2878145 (Rule of Robustness: Robustness is the child of transparency and simplicity)
    Yes, I agree with this guidnes too, but I do not get your point, this is valid for any progeramming language without checked exceptions. Do you know any "real world" example to help me understand how checked exceptions can be usefull ?
  44. Controlled failure != crash[ Go to top ]

    Tell me this: if you try to download a file using wget, and the re is no network connection, how would you like wget to act? Keep on trying?I'm not the only one happy with the current functionality: it tries a few times and then it exits.
    Sorry, I do not understand how it is related to exceptions and design, are you sure wget uses checked exceptions for "Controlled failure" and this design makes it more robust ?BTW all software failures are controlled in some way.

    I'm starting to see a cycle in this conversation...

    My wget example was an analogy of a Java API:

    wget: an API

    wget user: an API user

    wget exiting with error message: API call throwing an exception

    the chapter in wget documentation that mentions the possibility of wget exiting with an error message: the throws clause in the API method (i.e. the exception being checked)
     
    I tried to demonstrate that there are situations in which the only sane thing to do is to give up and throw an exception, and it's convenient to document this anticipated behaviour to the user (and the compiler!) by using checked exceptions.

    But now I think I have explained myself enough for one conversation :)
  45. Controlled failure != crash[ Go to top ]

    It is possible to prove everything using analogy, probably "crash" or "halt" sounds as something "evil" and "failure" sounds better, but it is the same thing, I can prove it by analogy :)
  46. how about this one[ Go to top ]

    I encountered this one in an API I had to debug.

    try{

    //do something

    catch(SomeException se){
      throw new Exception("an error has occurred"+se);
    }
    (note the + in stead of , )
    Now that was fun!

    Marc
  47. how about this one[ Go to top ]

    I encountered this one in an API I had to debug.try{//do somethingcatch(SomeException se){&nbsp;&nbsp;throw new Exception("an error has occurred"+se);}(note the + in stead of , )Now that was fun!Marc
    Yes, it is better not to catch exception in this case, probably wrapper is better, but it is useless anyway
    (it doe's not help for recovery or error diagnostic).
    it sounds like :

    throw new ExceptionWrapper("Sorry, unexpected error has occurred, I do not know how to handle this stuff, try it yourself, I just need to cacth it to make compiler happy", cause);