Proposed new dependency: github.com/juju/errors (and github.com/juju/errgo)

William Reade william.reade at canonical.com
Thu May 29 20:48:39 UTC 2014


Masking is often necessary, and information hiding is valuable, and
depending on spooky action at a distance is generally foolish. But the
granularity with which we want to track the propagation of a given error is
*much* finer than the granularity of judicious information hiding: the
error is likely to pass through a lot more stack frames than it is package
boundaries, and the former transitions are the ones we need to track to
properly diagnose errors [0], while the latter are the ones we need to mask
to properly maintain global sanity [1].

To mask by default, at every annotation point, leads quickly to code whose
action is characterised by bizarre and mystifying errors [2]. To *fail* to
mask at sensible boundaries also leads to bugs, and we've seen one or two
of those as well; the trouble is that masking is *always easy*, while pure
annotation is (relatively) hard. To use a library that makes a point of
keeping preservation comparatively verbose and complex is to fundamentally
misunderstand the problem.

Cheers
William

[0] it's not a perfect one-to-one match, but it's a reasonable heuristic.

[1] it's not a perfect one-to-one match, but it's a reasonable heuristic.

[2] this because developers are forced to choose between obscuring their
logic with verbose workarounds for the masking... or to just avoid
annotation entirely, and hence come to habitually avoid masking even when
it's necessary. My lived experience indicates that they will overwhelmingly
choose the latter, and that the consequences are unpretty.


On Thu, May 29, 2014 at 9:16 PM, Nate Finch <nate.finch at canonical.com>wrote:

> What Roger is talking about, is when an error is implementation-specific
> and may change if the implementation changes.
>
> For example, if you have a black box service that saves data, it might
> write to the network or it might write to a filesystem, but where it saves
> is an implementation detail.  Let's say in the current implementation,
> Save() returns a FileNotFoundError if you haven't called Initialize()
> first, then people may write code that handles a FileNotFoundError by
> calling Initialize().... now if you change the implementation to write to
> the network, so now if they haven't called Initialize(), you return a
> NetworkNotFoundError in the same situation.... their code will break.   If
> you had instead written your own error, or just masked the type of the
> error to something generic, then they wouldn't have been able to write
> their code that makes bad assumptions.
>
> Note that this is the most simple case of the problem.  In reality, it can
> be that some sub-sub-sub package was returning that specific error, and
> your code had no idea, it just passed it on up unchanged.  This is where it
> gets insidious, because *your* code didn't change.  Some dependency of a
> dependency changed that you might not even realize you were calling.  And
> now people using your code are broken.  If you masked all returned errors
> except the ones you explicitly wanted to return, then this couldn't happen.
>
>
>
>
>
>
> On Thu, May 29, 2014 at 3:00 PM, Jeroen Vermeulen <
> jeroen.vermeulen at canonical.com> wrote:
>
>> On 2014-05-29 09:50, roger peppe wrote:
>>
>>> On 29 May 2014 04:03, Tim Penhey <tim.penhey at canonical.com> wrote:
>>>
>>
>>  Errors are worth treating specially here because they're
>>> they pass up through multiple layers, so it's very easy to break
>>> abstraction
>>> boundaries by relying on specific error types. I believe it's very
>>> important
>>> to *think* about the possible errors that can be returned from a given
>>> function, and not just throw up our hands and say "you guys 5 layers
>>> down,
>>> why don't you just communicate *arbitrary stuff* to the guy 3 layers
>>> above".
>>>
>>
>> It may help to consider this as two problems.
>>
>>
>> One: caller needs to know about a specific failure — e.g. because it's a
>> failure to the callee but not to the caller.  Definitely part of the
>> contract.  You either:
>>
>> (a) define a super-specific error (exception class, error code, etc.), or
>>
>> (b) document that standard error X means failure Y in this case, and the
>> caller picks up the error as close as possible to its origin.
>>
>> With these errors you have to make sure that the information isn't
>> diluted as it propagates, but usually you don't have to take it too far up
>> the call chain.
>>
>>
>> Two: a caller can deal better with some errors, given more detailed
>> information.  You can help by attaching more information to the error
>> (tags, taxonomy, properties) but only on a best-effort basis.  You accept
>> that you don't know exactly which errors can come out of the code further
>> down.
>>
>> For example, if you're writing code which speaks to another program over
>> the network, you may want to know: is this connection still usable?  Do I
>> know what happened to the operation I requested?  Were we able to connect
>> in the first place?  Am I doing something that shouldn't ever work, such as
>> mis-spell a command?
>>
>> With these errors you act on the best information you have, but you can
>> always just fail.
>>
>>
>> Jeroen
>>
>>
>> --
>> Juju-dev mailing list
>> Juju-dev at lists.ubuntu.com
>> Modify settings or unsubscribe at: https://lists.ubuntu.com/
>> mailman/listinfo/juju-dev
>>
>
>
> --
> Juju-dev mailing list
> Juju-dev at lists.ubuntu.com
> Modify settings or unsubscribe at:
> https://lists.ubuntu.com/mailman/listinfo/juju-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ubuntu.com/archives/juju-dev/attachments/20140529/1c8d2d02/attachment.html>


More information about the Juju-dev mailing list