Domain-Driven Design
> >
ReportingErrors

Mon Oct 28, 2002 9:04 am
I've become quite interested in error messages recently. I'm inclined
to think that a good error message tells the user three things:
1) What the program tried to do.
2) What went wrong.
3) What the user might do about it.

It's the "what went wrong" that interests me most. Let's say that the
user is expected to have some sort of mental model of how the program
fits into her domain and how it works. That model will be different
from how the program actually works.

For example, a user of Internet Explorer thinks of bookmarks as names
for URLs that persist from session to session. The implementation
thinks of bookmarks as files that contain URLs. That's a problem if
you can persuade IE to try to create a bookmark called "RUP: Unit
Testing", because that was not a valid filename on DOS. The error
message is in terms of filenames, which I think is completely
bewildering to too many users. (For more on this bug, see this:
<http://www.visibleworkings.com/papers/ready-to-hand-bugs.pdf>;.)

It seems that errors discovered deep in the bowels of the
implementation need to be translated back into the language of the
domain.

How can code be written to allow that? And can it be done without too
much pain? By that, I mean that any mechanism must, it seems to me,
require bookkeeping work of programmers - some executable statement
or declaration in the code that says, "See that bookmark name there?
Henceforth, it's to be considered also a filename." Pragmatically, if
the bookkeeping is too painful, programmers won't do it. Even if they
do, we'd like to avoid bookkeeping that makes the code harder to
understand and change.

I'd love to discuss this with people at OOPSLA next week or through
this list. (Eric said it's relevant.) I'm also on the hunt for
references, patterns, idioms, stories of success and failure. (I'm
familiar with aspects, though not with any example that works through
how AOP might be used for this problem, and also with some of the
literature on getting expert systems to explain their reasoning,
though that familiarity is out of date - so specific cites always
welcome.)

BrianMarick



Sat Nov 16, 2002 1:48 pm
This is a good topic for discussion. Brian actually brought this
issue up with me and I asked him to repost it here because I'd like
to hear what others have to say. Here are some of my thoughts.

> For example, a user of Internet Explorer thinks of bookmarks as
names
> for URLs that persist from session to session. The implementation
> thinks of bookmarks as files that contain URLs. That's a problem if
> you can persuade IE to try to create a bookmark called "RUP: Unit
> Testing", because that was not a valid filename on DOS. The error
> message is in terms of filenames, which I think is completely
> bewildering to too many users. (For more on this bug, see this:
> <http://www.visibleworkings.com/papers/ready-to-hand-bugs.pdf>;.)

One of my strong opinions is that we should be working with only one
model (within any single context, as discussed in Chapter 14). Most
of my rants in the book on this topic point out the problems of
having separate analysis models and design models, but here we have a
problem arising from a different pair of models: the user model and
the design/implementation model.

Now, I don't want to say that the user has to be shown an unadorned
view of the domain model. That would definitely not be convenient in
most cases. What I do think is that the model underlying the UI is
the domain model. This is a good example. If bookmarks are actually
files, then expose this to the user. Not only will it be less
confusing with error messages, the user can then leverage what he
knows about the file-system to deal with bookmarks. He can go find
them in the file browser and organize them, for example, rather than
using awkward tools built into the browser. Why make the user learn a
new model when the programmers felt the old model was good enough?

Now, if bookmarks were actually being stored in a different way, say
in a datafile, they could have their own rules. Those rules, it seems
to me, should be the naming rules that apply to websites. That would
be a single model, again, that tells the user that everything they
know about naming websites applies to bookmarks.

There were other good issues raised in your message, but I wanted to
make my point about the risks of trying to map between multiple
models within the same context.

EricEvans


Sun Nov 24, 2002 8:42 am
At 9:48 PM +0000 11/16/02, Eric Evans wrote: (excerpted from below)
>Now, I don't want to say that the user has to be shown an unadorned
>view of the domain model. That would definitely not be convenient in
>most cases. What I do think is that the model underlying the UI is
>the domain model. This is a good example. If bookmarks are actually
>files, then expose this to the user.

Would this be something like what you mean?

The average user would think of bookmarks as named URLs stored in a
list gotten to by the Bookmarks menu entry. However, when the
file-open error happened, the message would look like this:

Explorer tried to create the bookmark 'RUP: Domain model'.
That meant creating a file named 'RUP: Domain model' in folder 'blah'.
But these characters are not allowed in a file name: :\"^'.
Please change the bookmark name so that it doesn't contain those characters.

That way, the user gets a bit of "just in time" education about the
underlying domain model when an error happens.


I'm trying to support that style, while making it relatively
unobtrusive to the programmer. When programmers are reading code
that implements some function, they want to see as little about error
handling as possible. I could separate error handling into
aspect-style wrappers, but I find that doing error checking only on
function-call boundaries is too coarse-grained. Either the
error-handling is bad (because the messages to the user can't have
the right information, like the values of locals) or you have to make
artificial divisions between functions so that you have a place to
hook on wrappers (but those divisions are inexplicable to someone not
thinking about error handling, so error handling again distracts the
programmer, just in a more obscure way).

I'm curious about people's reaction to the following scheme.

Each user interface command hands its name and argument off to an
"attempt" method. That method wraps a call to the implementation of
the UI command with a try-catch block. The implementation method is
oblivious to all that error-handling setup. For example, I recently
changed the implementation of the "pause_day" command in an
application I'm working on. The (Ruby) code looks like this:


def pause_day(when_to_pause = "now")
pause_time = DesiredTime.at(checking_time_from(when_to_pause))
paused = @session.pause_without_resumption(pause_time)
@quick_start_action = proc {
start(paused.full_name)
}
pause_description(paused, pause_time)
end


This looks unconcerned with error-handling because it is. All
checking is done on the server side (by lower level code) and there
are no translations the user needs to know about.

When a command needs to make a translation, it uses a "step" method.
That looks like this:


def start_day(when_to_start = "now")
step(:start_background_job) {
... stuff that starts the day
}
end


Step pushes the name of the step (and any relevant arguments) onto a
stack, executes the code within its curly-braces, then pops the
stack. My hope is that it's relatively easy to ignore the steps,
consider them just syntax, when reading the code to see what it
normally does.

Error checking looks like this:


def start_background_job(desired_time)
whine_if (@active_job_manager.any_active?,
:start_background_but_jobs_are_active)

background = @jobs.background_job
...


Whine_if throws an exception if its condition is true. The exception
bundles up the name of the complaint
(:start_background_but_jobs_are_active) and any relevant arguments
(none, in this case). The exception travels up the call stack, across
the network connection, to the client, up its stack and finally gets
caught by the original attempt() method. That method now has a series
of name+arguments:
1) the original attempt.
2) a stack of steps taken on the way to a place an error happened.
3) the complaint.

It takes those and translates them into a message to the user. It
tacks on a suggestion if there's one that applies to that attempt and
that complaint.

As a little refinement, it filters out steps that are known not to be
relevant to a particular complaint. So, if it was discovered that the
URL was bad within the bookmark-name-to-URL step, you wouldn't get:

Explorer tried to create the bookmark 'RUP: Domain model'.
That meant creating a file named 'RUP: Domain model' in folder 'blah'.
But the URL does not begin with "http".
Please pick a different URL.

People don't much like non-sequiters from computers.

BrianMarick


Tue Nov 19, 2002 5:52 am
>One of my strong opinions is that we should be working with only one
>model (within any single context, as discussed in Chapter 14). Most
>of my rants in the book on this topic point out the problems of
>having separate analysis models and design models, but here we have a
>problem arising from a different pair of models: the user model and
>the design/implementation model.

>Now, I don't want to say that the user has to be shown an unadorned
>view of the domain model. That would definitely not be convenient in
>most cases. What I do think is that the model underlying the UI is
>the domain model. This is a good example. If bookmarks are actually
>files, then expose this to the user. Not only will it be less
>confusing with error messages, the user can then leverage what he
>knows about the file-system to deal with bookmarks. He can go find
>them in the file browser and organize them, for example, rather than
>using awkward tools built into the browser. Why make the user learn a
>new model when the programmers felt the old model was good enough?

>Now, if bookmarks were actually being stored in a different way, say
>in a datafile, they could have their own rules. Those rules, it seems
>to me, should be the naming rules that apply to websites. That would
>be a single model, again, that tells the user that everything they
>know about naming websites applies to bookmarks.

This is a very good point, and a good example to make that point.
You should include it in your book, because your book does not now
make clearly the point that your belief in one model applies to
the implementation, as well. Unfortunately, I don't know where you
should put this discussion.

RalphJohnson


Tue Nov 19, 2002 4:52 pm
Hmmm. I thought I had made the point in the Model-Driven Design
pattern. I did talk about the problems of separate "analysis models".
But maybe if I use this example and make a direct statement...

Actually, I guess I need to make sure that a point this important is
made in bold print somewhere, don't I.

EricEvans


Wed Nov 20, 2002 3:28 am
When you made your point, I realized it was implicit in what you
had said. However, even after having read your book twice, it
was not obvious to me that you would say that.

RalphJohnson



ReportingErrors is mentioned on: ThreadView


VeryQuickWiki Version 2.6.3 - HTML Export