design discussion: when to throw exceptions

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • cat

    #1

    design discussion: when to throw exceptions

    I had a long and heated discussion with other developers on my team on when
    it makes sense to throw an exception and when to use an alternate solution.
    The .NET documentation recommends that an exception should be thrown only in
    exceptional situations. It turned out that each of my colleagues had their
    own interpretation about what an "exceptiona l situation" may actually be.
    First of all, myself I’m against using exceptions extensively, because for
    complex business applications they make the code very complicated and because
    of a long list of other issues I could post here if someone is interested.
    The only arguments pro using exceptions that I was presented were that they
    force the caller to not ignore them and “this is the trend in the industry so
    we must follow”.

    To exemplify what an exceptional situation might be, we debated a lot on
    this piece of code:

    protected void login_Click()
    {
    try
    {
    UserSession objSession = _manager.StartU serSession(strU sername,
    strPassword);
    Session[“userSessionâ €] = objSession;
    }
    catch ( InvalidUsername Exception ex1 ) // username does not comply to
    requirements
    {
    … further processing
    }
    catch ( InvalidPassword Exception ex2 ) // password does not comply to
    requirements
    {
    … further processing
    }
    catch ( AuthenticationE xception ex3 ) // user account not found, locked,
    disabled, etc.
    {
    … further processing
    }
    catch ( Exception ex4 ) // system error or any other exception we don’t
    need to handle separately
    {
    _logger.LogTheE rror(ex4);
    … further processing
    }
    }

    My point was that the above is an example of abusing exceptions as it’s
    using them for non-exceptional situations, and it’s using them exactly like
    some return values. A user mistyping his password is just one of the
    alternate paths in a login/authentication use case, it’s simply part of the
    flow. It is expected that from time to time users mistype their username, or
    some administrator may lock some account. The counterargument was that user
    providing the proper username and password is the normal case while user
    mistyping the username or account being locked is the exception, so we should
    throw an exception in such cases.

    I extensively went through all sorts of information and websites and blogs
    on the Internet. However, I couldn’t find much on such difficult and
    important issue. Everybody seems to agree that opening a non-existent file or
    stuffing a non-numeric string into an integer should throw an exception. But
    that’s pretty much it. Most of the applications on the market actually deal
    with cases like the above one, yet there is no clear guideline on what could
    justify throwing an exception in a business application and what not. What is
    your opinion?
  • Light

    #2
    Re: design discussion: when to throw exceptions

    One logic is that exceptions should be thrown when they can't be
    predicted as a natural course of program flow. Another is that
    exceptions should be productive in helping the programmer create more
    robust code. I think exceptions would be the correct solution this
    case because a user not found could occur for many different reasons
    but also it depends on how this works in a larger context simply
    because it can fail for many different reasons.

    If a user not found could only fail because the user made the mistake
    in typing their name in wrong then it would seem rational to not make
    it an exception. The problem being that the user not found could occur
    for many different reasons - perhaps the database is down, perhaps a
    network connection is down, and so forth.

    And if you follow this logic then by not making those cases exceptions
    and treating them as normal then it may hide potential problems in the
    code. By using exceptions then you can think around the code better
    and when erroneous conditions do occur they are evident.

    It is also a matter of style and choice, in C# exceptions are the first
    class but in C return values may be better.
    Curtis

    Premier Popup Notepad & Help System For Developers

    PS: robust code typically isn't the most easy to understand - I agree.
    In the same fashion, a real world code example is much more difficult
    to understand then a stripped down conceptual model.

    Comment

    • Jeff Louie

      #3
      Re: design discussion: when to throw exceptions

      One approach is to throw an exception when an explicitly stated
      pre-condition,
      post-condition or invariant is violated. The corollary is that any
      pre-condition,
      post-condition or invariant that may engender an exception must be
      explicitly
      stated and documented. This begs the question of when you should
      explicitly
      state a pre-condition, post-condition or invariant, but it is a start.

      Regards,
      Jeff

      *** Sent via Developersdex http://www.developersdex.com ***

      Comment

      • Bruce Wood

        #4
        Re: design discussion: when to throw exceptions

        Your colleagues are wrong. You are right.

        By definition, anything that the user can type or do is not
        "exceptiona l." It's the user being the user. Mistyping a password is
        not "exceptiona l."

        Failing to find the password in the database because the database is
        down is "exceptiona l."

        Of course, it's not always so cut-and-dried. What is "exceptiona l"
        often depends upon context. In particular, if you're writing a library
        class that is expected to enjoy widespread use, what may be an
        exceptional case for one client application may be business as usual
        for another.

        In _those_ sorts of cases it becomes a question of style, but MS is now
        recommending that in cases of validation, for example, you provide an
        alternate method that checks valid true / false (viz the new TryParse
        methods in 2.0).

        Anyway, it's mostly in deeply-buried common code that the exception
        versus return value argument becomes one of style. The example you gave
        is clearly an abuse of the exception idiom.

        Comment

        • Naraendira Kuma R.R.

          #5
          Re: design discussion: when to throw exceptions

          Invariably, I think it is possible to replace exceptions/try-catch, with
          return-code/if-then-else blocks & vice versa. Some scenarios are easier to

          The advantage of exceptions is that they can be bubbled up the function call
          stack. This allows for a high level caller to be aware of failure/reason
          way deep in the call stack. Often you can examine the stack trace of an
          exception & tell exactly what code flow sequence caused a failure. I &
          many others I know, often deliberately take advantage of this, by designing
          deep method calls.

          To your specific example, I think I agree with you. It is overkill.
          Part of the reason you might have trouble articulating it is because, your
          example suffers from a different object-design problem.[color=blue][color=green]
          >> _manager.StartU serSession(strU sername, strPassword);[/color][/color]
          is doing multiple things -- Authentication, input validation, Session
          creation etc. Break them apart might lead to a more semantically
          meaningful & fruitful discussion.

          E.g.
          ~~~~~~~~~~~
          int iRtnCode
          try{
          iRtnCode = _manager.Authen ticate(strUserN ame, strPassword)
          }
          catch( DomainControlle rUnavailableExc eption ex1 ) {
          //Oh! Oh! What do we do now?
          }
          if (iRtnCode == "Authentica tion Failure"){
          //further processing.
          }

          UserSession objSession = _manager.Create UserSession()
          ~~~~~~~~~~~~

          * I noticed your example of error handling is from the perspective of the
          caller. In this particular case, I've found it useful to steer the design
          discussion, towards the callee's perspective.

          * Writing code with exceptions can be hard to relate to initially. I
          believe for most people it feels natural, after a little getting used to.
          Of course, abuse - as you've called it out - is always bad & painful.

          --
          Naraendira Kumar R.R.
          ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
          "cat" <cat@discussion s.microsoft.com > wrote in message
          news:95D41C55-A397-4A6C-8323-351523C2255C@mi crosoft.com...[color=blue]
          >I had a long and heated discussion with other developers on my team on when
          > it makes sense to throw an exception and when to use an alternate
          > solution.
          > The .NET documentation recommends that an exception should be thrown only
          > in
          > exceptional situations. It turned out that each of my colleagues had their
          > own interpretation about what an "exceptiona l situation" may actually be.
          > First of all, myself I'm against using exceptions extensively, because for
          > complex business applications they make the code very complicated and
          > because
          > of a long list of other issues I could post here if someone is interested.
          > The only arguments pro using exceptions that I was presented were that
          > they
          > force the caller to not ignore them and "this is the trend in the industry
          > so
          > we must follow".
          >
          > To exemplify what an exceptional situation might be, we debated a lot on
          > this piece of code:
          >
          > protected void login_Click()
          > {
          > try
          > {
          > UserSession objSession = _manager.StartU serSession(strU sername,
          > strPassword);
          > Session["userSessio n"] = objSession;
          > }
          > catch ( InvalidUsername Exception ex1 ) // username does not comply to
          > requirements
          > {
          > . further processing
          > }
          > catch ( InvalidPassword Exception ex2 ) // password does not comply to
          > requirements
          > {
          > . further processing
          > }
          > catch ( AuthenticationE xception ex3 ) // user account not found, locked,
          > disabled, etc.
          > {
          > . further processing
          > }
          > catch ( Exception ex4 ) // system error or any other exception we don't
          > need to handle separately
          > {
          > _logger.LogTheE rror(ex4);
          > . further processing
          > }
          > }
          >
          > My point was that the above is an example of abusing exceptions as it's
          > using them for non-exceptional situations, and it's using them exactly
          > like
          > some return values. A user mistyping his password is just one of the
          > alternate paths in a login/authentication use case, it's simply part of
          > the
          > flow. It is expected that from time to time users mistype their username,
          > or
          > some administrator may lock some account. The counterargument was that
          > user
          > providing the proper username and password is the normal case while user
          > mistyping the username or account being locked is the exception, so we
          > should
          > throw an exception in such cases.
          >
          > I extensively went through all sorts of information and websites and blogs
          > on the Internet. However, I couldn't find much on such difficult and
          > important issue. Everybody seems to agree that opening a non-existent file
          > or
          > stuffing a non-numeric string into an integer should throw an exception.
          > But
          > that's pretty much it. Most of the applications on the market actually
          > deal
          > with cases like the above one, yet there is no clear guideline on what
          > could
          > justify throwing an exception in a business application and what not. What
          > is
          > your opinion?[/color]


          Comment

          • cat

            #6
            Re: design discussion: when to throw exceptions

            Dear Naraendira,

            Interesting enough, I presented to my colleagues the following alternate
            solution to the exception based code, which is quite similar to what you
            proposed:

            protected void login_Click()
            {
            UserSession objSession;
            switch ( _manager.StartU serSession(ref objSession,strU sername,strPass word) )
            {
            case enmSessionResul ts.InvalidUsern ameException:
            case enmSessionResul ts.InvalidPassw ordException:
            … processing specific to invalid user provided info
            goto case enmSessionResul ts.Authenticati onException
            case enmSessionResul ts.Authenticati onException:
            ... processing specific to no log in
            break;
            default:
            Session["userSessio n"] = objSession;
            break;
            }
            }

            The drawback to the above solution is that people are more used to a factory
            approach ( like CClass obj = factory.GetSome Object() ) in which case they are
            pretty much stuck to throwing exceptions. This could be mitigated by the 2
            steps approach suggested by you, where first the authentication is performed
            then a reference to a session object is returned. My example is addressing
            some of the big problems I found about throwing exceptions: potential for
            terrible errors that only during production will be caught because of lack of
            type checking, and complex and difficult code. For simple projects, the
            exception-based approach is just fine and I won’t argue against it. However,
            for large projects containing many classes exceptions spiral up all sorts of
            problems:

            a. For proper handling of exceptions most of the application thrown
            exceptions will have to be custom exceptions. That will tremendously increase
            the number of classes in a project. Think of projects with a couple hundred
            of classes how complicated they would become.

            b. There is no compiler type checking for exceptions (.NET is using the
            so-called unchecked exceptions). If one developer adds a new exception or
            changes something in one of the existing exceptions, no one else in the team
            will know until the code explodes in production. It is like declaring that a
            method returns an integer but you tell no one what are the values and what
            they mean. As opposed, the above enumeration based approach is safe in the
            way that the compiler could detect any change in the returned values so such
            changes if undocumented will be still trapped early in the game.

            And this is to name just a few of them, but the list is much bigger.


            "Naraendira Kuma R.R." wrote:
            [color=blue]
            > Invariably, I think it is possible to replace exceptions/try-catch, with
            > return-code/if-then-else blocks & vice versa. Some scenarios are easier to
            >
            > The advantage of exceptions is that they can be bubbled up the function call
            > stack. This allows for a high level caller to be aware of failure/reason
            > way deep in the call stack. Often you can examine the stack trace of an
            > exception & tell exactly what code flow sequence caused a failure. I &
            > many others I know, often deliberately take advantage of this, by designing
            > deep method calls.
            >
            > To your specific example, I think I agree with you. It is overkill.
            > Part of the reason you might have trouble articulating it is because, your
            > example suffers from a different object-design problem.[color=green][color=darkred]
            > >> _manager.StartU serSession(strU sername, strPassword);[/color][/color]
            > is doing multiple things -- Authentication, input validation, Session
            > creation etc. Break them apart might lead to a more semantically
            > meaningful & fruitful discussion.
            >
            > E.g.
            > ~~~~~~~~~~~
            > int iRtnCode
            > try{
            > iRtnCode = _manager.Authen ticate(strUserN ame, strPassword)
            > }
            > catch( DomainControlle rUnavailableExc eption ex1 ) {
            > //Oh! Oh! What do we do now?
            > }
            > if (iRtnCode == "Authentica tion Failure"){
            > //further processing.
            > }
            >
            > UserSession objSession = _manager.Create UserSession()
            > ~~~~~~~~~~~~
            >
            > * I noticed your example of error handling is from the perspective of the
            > caller. In this particular case, I've found it useful to steer the design
            > discussion, towards the callee's perspective.
            >
            > * Writing code with exceptions can be hard to relate to initially. I
            > believe for most people it feels natural, after a little getting used to.
            > Of course, abuse - as you've called it out - is always bad & painful.
            >
            > --
            > Naraendira Kumar R.R.
            > ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
            > "cat" <cat@discussion s.microsoft.com > wrote in message
            > news:95D41C55-A397-4A6C-8323-351523C2255C@mi crosoft.com...[color=green]
            > >I had a long and heated discussion with other developers on my team on when
            > > it makes sense to throw an exception and when to use an alternate
            > > solution.
            > > The .NET documentation recommends that an exception should be thrown only
            > > in
            > > exceptional situations. It turned out that each of my colleagues had their
            > > own interpretation about what an "exceptiona l situation" may actually be.
            > > First of all, myself I'm against using exceptions extensively, because for
            > > complex business applications they make the code very complicated and
            > > because
            > > of a long list of other issues I could post here if someone is interested.
            > > The only arguments pro using exceptions that I was presented were that
            > > they
            > > force the caller to not ignore them and "this is the trend in the industry
            > > so
            > > we must follow".
            > >
            > > To exemplify what an exceptional situation might be, we debated a lot on
            > > this piece of code:
            > >
            > > protected void login_Click()
            > > {
            > > try
            > > {
            > > UserSession objSession = _manager.StartU serSession(strU sername,
            > > strPassword);
            > > Session["userSessio n"] = objSession;
            > > }
            > > catch ( InvalidUsername Exception ex1 ) // username does not comply to
            > > requirements
            > > {
            > > . further processing
            > > }
            > > catch ( InvalidPassword Exception ex2 ) // password does not comply to
            > > requirements
            > > {
            > > . further processing
            > > }
            > > catch ( AuthenticationE xception ex3 ) // user account not found, locked,
            > > disabled, etc.
            > > {
            > > . further processing
            > > }
            > > catch ( Exception ex4 ) // system error or any other exception we don't
            > > need to handle separately
            > > {
            > > _logger.LogTheE rror(ex4);
            > > . further processing
            > > }
            > > }
            > >
            > > My point was that the above is an example of abusing exceptions as it's
            > > using them for non-exceptional situations, and it's using them exactly
            > > like
            > > some return values. A user mistyping his password is just one of the
            > > alternate paths in a login/authentication use case, it's simply part of
            > > the
            > > flow. It is expected that from time to time users mistype their username,
            > > or
            > > some administrator may lock some account. The counterargument was that
            > > user
            > > providing the proper username and password is the normal case while user
            > > mistyping the username or account being locked is the exception, so we
            > > should
            > > throw an exception in such cases.
            > >
            > > I extensively went through all sorts of information and websites and blogs
            > > on the Internet. However, I couldn't find much on such difficult and
            > > important issue. Everybody seems to agree that opening a non-existent file
            > > or
            > > stuffing a non-numeric string into an integer should throw an exception.
            > > But
            > > that's pretty much it. Most of the applications on the market actually
            > > deal
            > > with cases like the above one, yet there is no clear guideline on what
            > > could
            > > justify throwing an exception in a business application and what not. What
            > > is
            > > your opinion?[/color]
            >
            >
            >[/color]

            Comment

            • Kevin Spencer

              #7
              Re: design discussion: when to throw exceptions

              An "exceptiona l situation" is when the software misbehaves. User error does
              not constitute an "exceptiona l situation." Good software *expects* and deals
              with user error. Exceptions are expensive in terms of performance. They
              should be considered a necessary evil, and therefore, to be avoided when
              possible.

              Now, as to how to architect your software, there are many ways to skin the
              proverbial cat. But if you keep the basic underlying principles in mind, you
              will do well. The trick is to know what the underlying principles are, and
              to be mindful of them. It is more difficult to work from a basis of
              principle than to simply follow a set of pre-written rules. To work from a
              basis of principle means that one must calculate the outcome of each
              situation based upon all of the underlying principles and factors involved.
              The alternative (working from an existing set of rules) is much easier (in
              the short run). As humans are inherently lazy, there is often a trend toward
              the latter. There are 2 very serious problems with the latter approach,
              however:

              1. When a situation arises that is outside the bounds of the set of rules
              (which will happen eventually), one will not have the understanding of the
              underlying principles involved in order to solve the problem correctly.
              2. Sets of rules must be memorized, as must underlying principles. However,
              where there are relatively few underlying principles, there are many rules
              associated with the use of blueprints. In the long run, people often become
              so involved with all the rules that they forget the principles.

              Case in point: HTML was originally created without extensibility in mind. It
              was created with a set of rules. As demand for functionality increased,
              more rules were added, and exceptions to those rules proliferated.
              Eventually, the rules of HTML became so complex that it is nearly impossible
              to parse any HTML document without error. As a reaction to this, XML was
              created. XML is strict, and has very few rules. However, like principles,
              the rules of XML are without exception. It is nearly infinitely extensible
              because it was designed to be so. The DTD used defines the rules of any XML
              document, making it self-describing, and independent of any large set of
              rules governing all XML documents. Because of this, it is easy to parse any
              XML document without error. And it is highly likely that XHTML will
              eventually replace HTML worldwide.

              There has been a proliferation of patterns and blueprints in the software
              world, and many of these have been adopted simply because of their
              popularity. They are used without knowledge or understanding by many people.
              These patterns and blueprints are sound because they are based upon the
              underlying principles. They are simply collections of rules that, within
              certain parameters, are derivations of the underlying principles. However,
              it is important to note that they are by no means any substitute for
              knowledge of those principles. When they become so, these sorts of
              Lilliputian arguments are the outcome.

              Your instincts are right. All you need to do is to be able to explain what
              you're feeling by tying your ideas to the underlying principles, and apply
              those principles to a solution.

              --
              HTH,

              Kevin Spencer
              Microsoft MVP
              ..Net Developer
              You can lead a fish to a bicycle,
              but it takes a very long time,
              and the bicycle has to *want* to change.

              "cat" <cat@discussion s.microsoft.com > wrote in message
              news:95D41C55-A397-4A6C-8323-351523C2255C@mi crosoft.com...[color=blue]
              >I had a long and heated discussion with other developers on my team on when
              > it makes sense to throw an exception and when to use an alternate
              > solution.
              > The .NET documentation recommends that an exception should be thrown only
              > in
              > exceptional situations. It turned out that each of my colleagues had their
              > own interpretation about what an "exceptiona l situation" may actually be.
              > First of all, myself I'm against using exceptions extensively, because for
              > complex business applications they make the code very complicated and
              > because
              > of a long list of other issues I could post here if someone is interested.
              > The only arguments pro using exceptions that I was presented were that
              > they
              > force the caller to not ignore them and "this is the trend in the industry
              > so
              > we must follow".
              >
              > To exemplify what an exceptional situation might be, we debated a lot on
              > this piece of code:
              >
              > protected void login_Click()
              > {
              > try
              > {
              > UserSession objSession = _manager.StartU serSession(strU sername,
              > strPassword);
              > Session["userSessio n"] = objSession;
              > }
              > catch ( InvalidUsername Exception ex1 ) // username does not comply to
              > requirements
              > {
              > . further processing
              > }
              > catch ( InvalidPassword Exception ex2 ) // password does not comply to
              > requirements
              > {
              > . further processing
              > }
              > catch ( AuthenticationE xception ex3 ) // user account not found, locked,
              > disabled, etc.
              > {
              > . further processing
              > }
              > catch ( Exception ex4 ) // system error or any other exception we don't
              > need to handle separately
              > {
              > _logger.LogTheE rror(ex4);
              > . further processing
              > }
              > }
              >
              > My point was that the above is an example of abusing exceptions as it's
              > using them for non-exceptional situations, and it's using them exactly
              > like
              > some return values. A user mistyping his password is just one of the
              > alternate paths in a login/authentication use case, it's simply part of
              > the
              > flow. It is expected that from time to time users mistype their username,
              > or
              > some administrator may lock some account. The counterargument was that
              > user
              > providing the proper username and password is the normal case while user
              > mistyping the username or account being locked is the exception, so we
              > should
              > throw an exception in such cases.
              >
              > I extensively went through all sorts of information and websites and blogs
              > on the Internet. However, I couldn't find much on such difficult and
              > important issue. Everybody seems to agree that opening a non-existent file
              > or
              > stuffing a non-numeric string into an integer should throw an exception.
              > But
              > that's pretty much it. Most of the applications on the market actually
              > deal
              > with cases like the above one, yet there is no clear guideline on what
              > could
              > justify throwing an exception in a business application and what not. What
              > is
              > your opinion?[/color]


              Comment

              • Bruce Wood

                #8
                Re: design discussion: when to throw exceptions

                These two reasons are exactly why exceptions should be reserved for
                "exceptiona l situations" in which you most likely want to take the
                whole app down.

                Comment

                • Bruce Wood

                  #9
                  Re: design discussion: when to throw exceptions

                  There is another way to approach this sort of problem.

                  You could use the factory approach, exactly as you outlined it.
                  However, your UserSession object would contain the possibility of being
                  "Invalid" or "NotAuthenticat ed" or some property like that. It could
                  then expose a property or properties that clients could call to
                  discover _why_ the session was not authenticated.

                  That way, you just call the factory and always get a UserSession back.
                  All the client has to do is ask if myUserSession.I sAuthenticated or
                  some such thing, and if not, ask myUserSession what went wrong.

                  The only time that this doesn't work is if the result coming back from
                  the factory is a Framework object that you cannot extend, but even in
                  that case you could wrap the object in your own wrapper class that can
                  hold failure / error message information, and then unwrap the Framework
                  class as soon as you've determined that the action succeeded.

                  Comment

                  Working...