An error can be categorized by many criteria, whether
- it's critical software or it's non-critical software
- it's user facing or developer facing
- there is a fallback or there is no fallback
- it's on the frontend or backend
- the code is synchronous or asynchronous
- a certain language or library is used (specific guides: Javascript)
These are general rules aimed to help making a decision about how to handle certain errors. For errors texts refer to the wording guideline. Also check the Javascript error handling guide.
There's always a fallback strategy. Errors are always reported. Possible bugs are considered for graceful error handling.
- Example: bank system
Bugs are not considered for graceful error handling. There isn't always a fallback strategy but errors should be reported.
Often happens on the frontend.
-
Example: a form validation failed on post. The validation error is shown, the field is higlighted and the previous value may or may not be removed.
-
Example: worst case, some unexpected error happens. A generic error is shown: "Something unexpected happened, save your changes offline and reload the page."
- Example: an async fethcing keeps the website up to date with other changes but it fails. This may fail silently in the bakckground (but must be handled explicitly). Fallback strategy could be retrying a few times and, if it keeps failing, showing a generic error to the user: "The page is running with limited functionality, reloading the page might help resolve it."
- Example: GOOD: "Something unexpected happened", BAD: "JSON.parseError"
- Example: GOOD: "The page is running with limited functionality", BAD: "XYZ feature is not available"
-
Example: BAD: An AJAX form validation failed on post, the field is highlighted. The user edits the field and resends the form. Throughout this, the error highlight is always visible. The user cannot tell if there's another error or if it's just "stuck".
-
Example: GOOD: An AJAX form validation failed on post, the field is highlighted. As the user changes the value the highlight is removed. Upon resubmission, if the value is still incorrect the highlight appears again.
-
Example: GOOD: Input validation failed on post, there's an error bubble. The bubble has a fade-in animation, it can be dismissed and it is automatically dismissed upon a new submission. If the value is still incorrect the bubble shows up again using the same fade-in animation.
Often happens on the backend. In certain cases a generic fallback strategy should be implemented (e.g. retry).
GOOD
thisMightThrow();
// continue...
BAD
try {
thisMightThrow();
}
catch (err) {
console.error(err);
}
// continue...
GOOD
function notifyNewUser(user) {
if (!user) throw "User doesn't exist";
}
BAD
function notifyUser(user) {
if (!user) return null;
}
And not every possible error.
GOOD
updateUser(err => {
if (!err) return;
if (err.name === "UserNotFound") {
console.error("Cannot update user", err);
handleUserNotFound();
} else {
console.error("Description of the error", err);
};
};
BAD
updateUser(err => {
if (!err) return;
console.error("Cannot update user", err);
handleUserNotFound();
};
GOOD
createUser(err => {
if (err) console.error("User couldn't be created", err);
};
GOOD
class CustomError extends Error {
constructor(error) {
// a proper copy constructor
}
}
// ...
createUser(err => {
if (err) throw new CustomError(err);
};
BAD
createUser(err => {
if (err) console.error("Cannot create user");
};
BAD
createUser(err => {
if (err) throw new CannotCreateUserError();
};
GOOD
class RecordAlreadyExistsError extends Error {
// ...
}
if (userExists(user)) {
throw new RecordAlreadyExistsError(JSON.stringify(user) + " already exists.");
}
BAD
class UserError extends Error {
// ...
}
if (userExists(user)) {
throw new UserError("Already exists.");
}
GOOD
if (err) {
if (err typeof RecordNotFound) {
res.status(404);
throw err;
} else {
throw err;
}
}
BAD
if (err) {
return res.status(400);
}