Execption Handling disection

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Alvin Bruney [MVP - ASP.NET]

    #31
    Re: Execption Handling disection

    >> Well, I have been using this methodology on a fairly large project (6[color=blue][color=green]
    >> years, more than 10 developpers involved, multi-tier application,
    >> multi-threaded kernel, probably over a million lines of code at this
    >> stage) and it does scale up![/color][/color]

    You making this up Bruno? You guys hiring :-)

    --
    Regards,
    Alvin Bruney - ASP.NET MVP

    [Shameless Author Plug]
    The Microsoft Office Web Components Black Book with .NET
    Now available @ www.lulu.com/owc
    "David Levine" <noSpam12dlevin eNNTP2@wi.rr.co m> wrote in message
    news:esAk8F0QFH A.2972@TK2MSFTN GP14.phx.gbl...[color=blue][color=green]
    > >
    >> Well, I have been using this methodology on a fairly large project (6
    >> years, more than 10 developpers involved, multi-tier application,
    >> multi-threaded kernel, probably over a million lines of code at this
    >> stage) and it does scale up![/color]
    >
    > Well, I must admit that is a good argument. Does it require a lot of
    > developer discipline, or does it tend to be self-regulating?
    >[color=green]
    >>
    >> It does not mean doubling every API, there are actually just a few calls
    >> that need to be doubled, mostly calls that parse strings, lookup things
    >> by name, open files, load resources by name, etc. This is a very small
    >> fraction of the APIs, and the overhead of doubling these calls is really
    >> not a problem, especially if you have a good naming convention (we use
    >> Find for the methods that throw and Lookup for the methods that return
    >> null). The rest of the API only comes in one flavor.[/color]
    >
    > What other conventions have you adopted to support this?
    >[color=green]
    >>
    >> Also, you don't always need to duplicate the entry points. Sometimes, it
    >> is better to pass an extra parameter to indicate whether errors should be
    >> signaled through exception or whether they should be returned through
    >> some kind of error object. For example, most of our parsing routines take
    >> an "errors" argument. If you pass null, the parser will throw exceptions
    >> and will always return a valid parse tree. If you pass an errors object,
    >> the parser will collect the errors into it, and will return null if the
    >> parsing fails. This is a typical example of clever API design, that gives
    >> us the two flavors in one call without adding much complexity to the API
    >> (I did not invent it, there are plenty of examples in the LISP APIs of
    >> emacs).[/color]
    >
    > I've seen other APIs that take a "throwOnErr or" argument but I am not fond
    > of it (yet). I prefer a single path throw the code, not two. Have you ever
    > encountered problems related to this?
    >[color=green][color=darkred]
    >>> It also does not address the problems developers face with 3rd party
    >>> libraries that do not supply the either/or API - a wrapper for each API
    >>> would need to be written that wrapped the one that threw the exception
    >>> and returned a sentinel value (or vice-versa), otherwise the try-catch
    >>> flow control code goes back into the main body of code.[/color]
    >>
    >> Yes, this is a problem, and we setup such wrappers for calls that are
    >> used in many places in our code (fortunately, this does not happen very
    >> often).
    >>[/color]
    >[color=green]
    >>
    >> In 95% of the cases, there is not much you can do "locally" about the
    >> special case/exception (your functional analysis should tell you that).
    >> So, the right thing to do is to call the "non-Try" version and let the
    >> exception bubble up. In the remaining 5%, you know that you have to deal
    >> with a special case (your functional analysis should tell you that) and
    >> you call the "Try" version.[/color]
    >
    > What kind of functional analysis are you referring to? Perhaps you analyze
    > things a bit differently then what I am accustomed to.
    >[color=green][color=darkred]
    >>>
    >>> It is not necessary to log at each try-catch handler. I recommend
    >>> logging at the initial catch site and again (if necessary) if the
    >>> exception is about to leave the module boundary.[/color]
    >>
    >> No, we only log when we don't rethrow, this way you know that every
    >> exception will be logged and logged only once.[/color]
    >
    >
    > I tend to disagree but perhaps for practical reasons that probably don't
    > apply to most situations today. When we first transitioned from C/C++
    > Win32 to managed code no one really knew what best practices to apply...it
    > evolved. As a result the original code base was littered with empty
    > try-catches and exceptions were getting swallowed, converted, etc. all
    > over. My reaction to that was to establish requirements to never allow an
    > exception to get silently dropped again. The result was double-logging -
    > the first time when it was initially thrown and the last time when it was
    > handled or left the module boundary - this way if it got dropped somewhere
    > in the middle we would be able to detect it.
    >
    > The 2nd practical result was that I made it a requirement that all
    > swallowed exceptions must call a central method (called SwallowExceptio n)
    > that by default printed out the exception message to the Trace - one of
    > the arguments to the method is the reason why it was ok to swallow the
    > exception. As a result we found a lot of places in the code that needed
    > work to either remove the source of the exception or do some other
    > rewrite. There are circumstances when swallowing an exception makes sense,
    > and most of those fall into the category that we are discussing - when to
    > throw versus return some other value. IOW, wrapping the API into a call
    > that does not throw would accomplish the same thing, and I'll probably
    > switch over to using that mechanism - it makes sense.
    >
    >
    >[color=green]
    >> Notes: in both patterns, we catch "all exceptions". So, we are always
    >> violating the rule that says that you should only catch "specific"
    >> exceptions (this is one of FxCop's rule). This rule is stupid because it
    >> is an encouragement to use exceptions as flow control in application
    >> logic. If you don't use exception as flow control for special cases that
    >> should be tested by your application logic, they should all bubble up the
    >> same way (get wrapped with higher level message, and then logged).
    >>[/color]
    >
    > Agreed. It is a silly rule.
    >
    >[color=green]
    >> There is nevertheless one case where we log and rethrow, this is when we
    >> design an API that someone else will be using, and when we know that this
    >> someone else is not very rigorous about logging exceptions. In this case,
    >> we do our own logging in the entry points of our component and we rethrow
    >> the exception (so that the someone else still gets an exception). But
    >> this is just so that we don't loose the information if the client of our
    >> component does not follow the rules (does not log every exception that he
    >> gets from our component).
    >>[/color]
    >
    > That sounds like the same sort of rule I use, which is to log when an
    > exception leaves the boundaries of the module.
    >
    >
    >[/color]


    Comment

    • Bruno Jouhier [MVP]

      #32
      Re: Execption Handling disection


      "Alvin Bruney [MVP - ASP.NET]" <www.lulu.com/owc> wrote in message
      news:OHcJrBURFH A.576@TK2MSFTNG P15.phx.gbl...[color=blue][color=green][color=darkred]
      >>> Well, I have been using this methodology on a fairly large project (6
      >>> years, more than 10 developpers involved, multi-tier application,
      >>> multi-threaded kernel, probably over a million lines of code at this
      >>> stage) and it does scale up![/color][/color]
      >
      > You making this up Bruno? You guys hiring :-)[/color]

      I was actually underestimating . I just ran wc to count the lines and I get
      1.7 MLines J# + 280 KLines C# (don't know if I should feel good or bad about
      these figures). Not all was written by hand, fortunately, the boring stuff
      was generated by a modeling tool.

      You'd like to move to France?

      Bruno.
      [color=blue]
      >
      > --
      > Regards,
      > Alvin Bruney - ASP.NET MVP
      >
      > [Shameless Author Plug]
      > The Microsoft Office Web Components Black Book with .NET
      > Now available @ www.lulu.com/owc
      > "David Levine" <noSpam12dlevin eNNTP2@wi.rr.co m> wrote in message
      > news:esAk8F0QFH A.2972@TK2MSFTN GP14.phx.gbl...[color=green][color=darkred]
      >> >
      >>> Well, I have been using this methodology on a fairly large project (6
      >>> years, more than 10 developpers involved, multi-tier application,
      >>> multi-threaded kernel, probably over a million lines of code at this
      >>> stage) and it does scale up![/color]
      >>
      >> Well, I must admit that is a good argument. Does it require a lot of
      >> developer discipline, or does it tend to be self-regulating?
      >>[color=darkred]
      >>>
      >>> It does not mean doubling every API, there are actually just a few calls
      >>> that need to be doubled, mostly calls that parse strings, lookup things
      >>> by name, open files, load resources by name, etc. This is a very small
      >>> fraction of the APIs, and the overhead of doubling these calls is really
      >>> not a problem, especially if you have a good naming convention (we use
      >>> Find for the methods that throw and Lookup for the methods that return
      >>> null). The rest of the API only comes in one flavor.[/color]
      >>
      >> What other conventions have you adopted to support this?
      >>[color=darkred]
      >>>
      >>> Also, you don't always need to duplicate the entry points. Sometimes, it
      >>> is better to pass an extra parameter to indicate whether errors should
      >>> be signaled through exception or whether they should be returned through
      >>> some kind of error object. For example, most of our parsing routines
      >>> take an "errors" argument. If you pass null, the parser will throw
      >>> exceptions and will always return a valid parse tree. If you pass an
      >>> errors object, the parser will collect the errors into it, and will
      >>> return null if the parsing fails. This is a typical example of clever
      >>> API design, that gives us the two flavors in one call without adding
      >>> much complexity to the API (I did not invent it, there are plenty of
      >>> examples in the LISP APIs of emacs).[/color]
      >>
      >> I've seen other APIs that take a "throwOnErr or" argument but I am not
      >> fond of it (yet). I prefer a single path throw the code, not two. Have
      >> you ever encountered problems related to this?
      >>[color=darkred]
      >>>> It also does not address the problems developers face with 3rd party
      >>>> libraries that do not supply the either/or API - a wrapper for each API
      >>>> would need to be written that wrapped the one that threw the exception
      >>>> and returned a sentinel value (or vice-versa), otherwise the try-catch
      >>>> flow control code goes back into the main body of code.
      >>>
      >>> Yes, this is a problem, and we setup such wrappers for calls that are
      >>> used in many places in our code (fortunately, this does not happen very
      >>> often).
      >>>[/color]
      >>[color=darkred]
      >>>
      >>> In 95% of the cases, there is not much you can do "locally" about the
      >>> special case/exception (your functional analysis should tell you that).
      >>> So, the right thing to do is to call the "non-Try" version and let the
      >>> exception bubble up. In the remaining 5%, you know that you have to deal
      >>> with a special case (your functional analysis should tell you that) and
      >>> you call the "Try" version.[/color]
      >>
      >> What kind of functional analysis are you referring to? Perhaps you
      >> analyze things a bit differently then what I am accustomed to.
      >>[color=darkred]
      >>>>
      >>>> It is not necessary to log at each try-catch handler. I recommend
      >>>> logging at the initial catch site and again (if necessary) if the
      >>>> exception is about to leave the module boundary.
      >>>
      >>> No, we only log when we don't rethrow, this way you know that every
      >>> exception will be logged and logged only once.[/color]
      >>
      >>
      >> I tend to disagree but perhaps for practical reasons that probably don't
      >> apply to most situations today. When we first transitioned from C/C++
      >> Win32 to managed code no one really knew what best practices to
      >> apply...it evolved. As a result the original code base was littered with
      >> empty try-catches and exceptions were getting swallowed, converted, etc.
      >> all over. My reaction to that was to establish requirements to never
      >> allow an exception to get silently dropped again. The result was
      >> double-logging - the first time when it was initially thrown and the last
      >> time when it was handled or left the module boundary - this way if it got
      >> dropped somewhere in the middle we would be able to detect it.
      >>
      >> The 2nd practical result was that I made it a requirement that all
      >> swallowed exceptions must call a central method (called SwallowExceptio n)
      >> that by default printed out the exception message to the Trace - one of
      >> the arguments to the method is the reason why it was ok to swallow the
      >> exception. As a result we found a lot of places in the code that needed
      >> work to either remove the source of the exception or do some other
      >> rewrite. There are circumstances when swallowing an exception makes
      >> sense, and most of those fall into the category that we are discussing -
      >> when to throw versus return some other value. IOW, wrapping the API into
      >> a call that does not throw would accomplish the same thing, and I'll
      >> probably switch over to using that mechanism - it makes sense.
      >>
      >>
      >>[color=darkred]
      >>> Notes: in both patterns, we catch "all exceptions". So, we are always
      >>> violating the rule that says that you should only catch "specific"
      >>> exceptions (this is one of FxCop's rule). This rule is stupid because it
      >>> is an encouragement to use exceptions as flow control in application
      >>> logic. If you don't use exception as flow control for special cases that
      >>> should be tested by your application logic, they should all bubble up
      >>> the same way (get wrapped with higher level message, and then logged).
      >>>[/color]
      >>
      >> Agreed. It is a silly rule.
      >>
      >>[color=darkred]
      >>> There is nevertheless one case where we log and rethrow, this is when we
      >>> design an API that someone else will be using, and when we know that
      >>> this someone else is not very rigorous about logging exceptions. In this
      >>> case, we do our own logging in the entry points of our component and we
      >>> rethrow the exception (so that the someone else still gets an
      >>> exception). But this is just so that we don't loose the information if
      >>> the client of our component does not follow the rules (does not log
      >>> every exception that he gets from our component).
      >>>[/color]
      >>
      >> That sounds like the same sort of rule I use, which is to log when an
      >> exception leaves the boundaries of the module.
      >>
      >>
      >>[/color]
      >
      >[/color]


      Comment

      Working...