A STORY OF ERRORS INSTANT BUY Walter adds error handling an early feature of the web application. • Unauthenticated • Out of stock • CSRF • Offline • Unknown
A STORY OF ERRORS INSTANT BUY instantBuy(item) .then(() => thanksForBuying(item)) .catch(error => { }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: alert(`You seem to be offline. No worries…’); break;
A STORY OF ERRORS INSTANT BUY instantBuy(item) .then(() => thanksForBuying(item)) .catch(error => { }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: alert(`You seem to be offline. No worries…’); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; }
A STORY OF ERRORS INSTANT BUY instantBuy(item) .then(() => thanksForBuying(item)) .catch(error => { }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: alert(`You seem to be offline. No worries…’); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); }
A STORY OF ERRORS INSTANT BUY instantBuy(item) .then(() => thanksForBuying(item)) .catch(error => { }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: alert(`You seem to be offline. No worries…’); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); } logError(error);
A STORY OF ERRORS SAVE FOR LATER After a couple of code reviews, QA testing and a demo with bad WiFi • Unauthenticated • Out of stock • CSRF • Offline • Unknown
A STORY OF ERRORS SAVE FOR LATER After a couple of code reviews, QA testing and a demo with bad WiFi • Unauthenticated • Out of stock • CSRF • Offline • Unknown with a twist - silently sync when online
saveForLater(item) .catch(error => { A STORY OF ERRORS }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break;
saveForLater(item) .catch(error => { A STORY OF ERRORS }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break;
saveForLater(item) .catch(error => { A STORY OF ERRORS }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; }
saveForLater(item) .catch(error => { A STORY OF ERRORS }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); }
saveForLater(item) .catch(error => { A STORY OF ERRORS }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); } logError(error);
saveForLater(item) .catch(error => { A STORY OF ERRORS copy-paste has a twist - swallow & send when online }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); } logError(error);
We end up with.. Boilerplate, Repetition No standard way of handling errors Cognitive overhead Inefficient error reporting Friction in development cycles
DESIGNING FOR ERRORS KINDS OF ERRORS - Business ‣ authentication ‣ validation ‣ stale data - Non business ‣ network ‣ storage ‣ device i/o (camera/mic/speakers/GPS) ‣ bugs
DESIGNING FOR ERRORS KINDS OF ERRORS - Business ‣ authentication ‣ validation ‣ stale data - Non business ‣ network ‣ storage ‣ device i/o (camera/mic/speakers/GPS) ‣ bugs
DESIGNING FOR ERRORS KINDS OF ERRORS - Business ‣ authentication ‣ validation ‣ stale data - Non business ‣ network ‣ storage ‣ device i/o (camera/mic/speakers/GPS) ‣ bugs cross-cut, common errors
DESIGNING FOR ERRORS WE NEED ERROR HANDLING THAT IS… ▸ Comprehensible ▸ Actionable ▸ Targeted ▸ Reportable ▸ Is in place, timely and consistent ▸ Communicates errors only if necessary
DESIGNING FOR ERRORS WE NEED ERROR HANDLING THAT IS… ▸ Actionable ▸ Targeted ▸ Reportable ▸ Is in place, timely and consistent ▸ Communicates errors only if necessary ▸ Communicates errors in a comprehensible way
DESIGNING FOR ERRORS WE NEED ERROR HANDLING THAT IS… ▸ Targeted ▸ Reportable ▸ Is in place, timely and consistent ▸ Communicates errors only if necessary ▸ Communicates errors in a comprehensible way ▸ Suggests how to resolve if possible
DESIGNING FOR ERRORS WE NEED ERROR HANDLING THAT IS… ▸ Reportable ▸ Is in place, timely and consistent ▸ Communicates errors only if necessary ▸ Communicates errors in a comprehensible way ▸ Suggests how to resolve if possible ▸ Makes errors have the smallest area of effect
DESIGNING FOR ERRORS WE NEED ERROR HANDLING THAT IS… ▸ Is in place, timely and consistent ▸ Communicates errors only if necessary ▸ Communicates errors in a comprehensible way ▸ Suggests how to resolve if possible ▸ Makes errors have the smallest area of effect ▸ Lets the product owners & engineers know
“Can I have a burger with baked flour, salted cured pork, relatively hard off-white natural cheese, tomato fruit and minced beef meat please?” - noone ever
✓ “offline” vs “err.status === 0 || err.msg…” ✓ Presets of default actions for common errors ✓ Customise presets in a dynamic way ➡ Add actions ➡ Override actions ➡ Skip actions ➡ Log or… not What we actually want
saveForLater(item) .catch(error => { A STORY OF ERRORS copy-paste / default throughout the app has a twist }); switch (error.status) { case 401: requestSignIn(); break; case 430: // CSRF promptToRefreshSession(); break; case 0: onlineTasks.push(() => saveForLater(item)); break; case 422: if (error.response === 'out of stock') { this.showOutOfStockDialog(); break; } default: alert('The operation is unavailable'); } logError(error);
We get to.. No standard way of handling Cognitive overhead Inefficient error reporting Zero boilerplate, minimum repetition Friction in development cycles
We get to.. Cognitive overhead Inefficient error reporting Zero boilerplate, minimum repetition Very standard way of handling errors Friction in development cycles
We get to.. Inefficient error reporting Zero boilerplate, minimum repetition Very standard way of handling errors Readable domain related code Friction in development cycles
We get to.. Zero boilerplate, minimum repetition Very standard way of handling errors Readable domain related code Efficient error tracking Friction in development cycles
We get to.. Zero boilerplate, minimum repetition Very standard way of handling errors Readable domain related code Efficient error tracking One less problem to deal with
Any Questions? No error you can’t .handle() Swift - https://github.com/Workable/swift-error-handler.git Java - https://github.com/Workable/java-error-handler.git Javascript - coming soon