Handling errors consistently
Errors can take the form of a thrown exception, a proper Error object, a plain object, an optional argument to a callback, a Promise, a special return value, a simple string, etc. At some point, all of these have to be converted into some message for the user. How do you convert between all the various forms, where does this conversion happen, where should it be handled/forwarded to the UI, and what should an error be/contain?
From a practical perspective, I think it makes sense to do the conversion in one place, maybe just before the error is sent to the user. In the case of web applications, this would be middleware called after a response is generated, but before it is sent to the browser. This middleware could normalize the response so it follows some common form (like a JSON object with
backtrace properties - see "Effectively communicating error to users and to developers" below), and log the relevant information for debugging purposes.
Also, sometimes multiple errors occur simultaneously (such as validation errors), so error objects/responses/etc. should represent a list of errors – in most cases, there will probably be a single item in this list.
To complicate matters, sometimes the UI code has errors, and this needs to be recorded for debugging purposes. Again, in the case of the web application, the webserver could include an HTTP route that simple accepts error data from the browser and logs this information to the same place as the errors raised by the server.
Effectively communicating error to users and to developers
It's probably useful to distinguish between an error message for developers/support, and an error message for users, but the root cause is still just a single error. So this single error should contain enough information for developers/support to quickly identify the problem, but also enough information so a message can be shown to users that makes sense – hopefully with steps to workaround/fix the issue.
That being said, it seems like a good error would contain a technical description for developers (with backtrace if possible), a brief message for users, and a detailed description of the problem (with workarounds if possible). Security should be considered (backtraces can reveal inner-workings of your program).
Since JSON is a language-/protocol-agnostic format, here's what an error from a REST API may look like:
"title": "User already exists",
"description": "Somebody's already using that username - please try a different username",
"message": "DatabaseError entity exists",
"backtrace": "... use your imagination ..."
"title": "Cannot use temporary email accounts",
"description": "Email addresses with the disposable email provider, mailinator.com, cannot be used. Please use a different address."
"original-error": "DocumentValidationError mailinator.com is not supported"