to err is human

Download To Err Is Human

If you can't read please download the document

Upload: alex-liu

Post on 19-Jan-2017

2.797 views

Category:

Software


0 download

TRANSCRIPT

  • To Err Is HumanAlex Liu @stinkydofu [email protected]

  • avoidance acceptance

  • avoidance acceptance

  • The Road To Happiness

    Handling Errors and Exceptions Preserving History The Problem With Catch

  • Handling Errors and Exceptions

    1

  • Error

    > new Error();

  • kinds of errorstwo

  • Expected

  • Expected

  • Expected

    invalid user input bad JSON input file not found request timeout request 500

  • Unexpected

  • Unexpected

  • Unexpected

    programmer typos read property of undefined

    undefined is not a function

  • How do I communicate expected errors?

  • Exception

    > throw new Error();

  • an exception is a

    thrown error

  • You could throw anything

    > throw { foo: bar }; > throw 0; > throw 'my error';

  • > throw 0; undefined > throw 'something happened'; something happened > throw new Error('something happened'); Error: something happened at repl:1:7 at REPLServer.defaultEval (repl.js:132:27) at bound (domain.js:254:14) at REPLServer.runBound [as eval] (domain.js:267:12) at REPLServer. (repl.js:279:12) at REPLServer.emit (events.js:107:17) at REPLServer.Interface._onLine (readline.js:214:10) at REPLServer.Interface._line (readline.js:553:8) at REPLServer.Interface._ttyWrite (readline.js:830:14)

  • index.js

  • index.js data.js get.js

  • index.js data.js get.js

    throw!

  • index.js data.js get.js

    throw!catch?

  • index.js data.js get.js

    throw!catch?

  • index.js data.js get.js

    throw!catch?

  • catch (e) {// run!

    }

    index.js data.js get.js

    throw!

  • CRASH! throw!

    index.js data.js get.js

  • throw / try / catch

    try { var price = getStockPrice('NFLX'); document.write('NFLX: ' + price); } catch(err) { // err instanceof Error => true document.write('NFLX: unavailable'); }

  • Can we catch?

    try { getStockPrice('NFLX', function(err) { if (err) { throw (err); } /* continue as normal */ }); } catch(err) { /* error handling */ }

  • Error first callbacks (errbacks)

    getStockPrice('NFLX', function(err, price) { if (err) { /* handle err! */ } document.write('NFLX: ' + price); });

  • Error first callbacks (errbacks)

    getStockPrice('NFLX', function(err, price) { if (err) { /* handle err! */ } document.write('NFLX: ' + price); });

  • Error first callbacks (errbacks)

    getStockPrice('NFLX', function(err, price) { if (err) { document.write('NFLX: unavailable'); return; } document.write('NFLX: ' + price); });

  • Bubble up if you cant handle it

    function drawStockPrice(callback) { getStockPrice('NFLX', function(err, price) { return callback(err); }); }

  • index.js

  • index.js data.js get.js

    cb(err)

  • index.js data.js get.js

    cb(err) cb(err)

  • if (err) {...

    }

    index.js data.js get.js

    cb(err) cb(err)

  • SWALLOWED! cb(err)cb(err)

    index.js data.js get.js

  • errbacks are a

    channelfor expected errors

  • > throw err;

    > callback(err);

    sync

    async

  • nopenope

    nopenope

    nopenope

    nopenope

    nope

    nopenope

    nope

    nope

    nopenope

    nope

    nope

  • throw / try / catchwhen in async env

    sanelyyou cant

  • favor errbacks because of

    consistency

  • How do I communicate unexpected errors?

  • unexpected errors are thrown

    automatically

  • process.on('uncaughtException', function(err) { ... });

    Catch em all, even async

  • process.on('uncaughtException', function(err) { ... });

    Catch em all, even async

    window.onerror = function(err) { ... });

  • process.on('uncaughtException', function(err) { ... });

    Catch em all, even async

    window.onerror = function(err) { ... });_BAD

    IDEA_

  • Can you recover? function dispatch(doThings) { if (!Array.isArray(doThings)) { throw new TypeError('not an array'); } for (var i = 0; i < doThings.length; i++) { if (typeof doThings[i] !== 'function') { throw new TypeError('not a function'); } } for (var i = 0; i < doThings.length; i++) { doThings[i](); } }

  • Can you recover? function dispatch(doThings) { if (!Array.isArray(doThings)) { throw new TypeError('not an array'); } for (var i = 0; i < doThings.length; i++) { if (typeof doThings[i] !== 'function') { throw new TypeError('not a function'); } } for (var i = 0; i < doThings.length; i++) { doThings[i](); } }

  • Can you recover? function dispatch(doThings) { if (!Array.isArray(doThings)) { throw new TypeError('not an array'); } for (var i = 0; i < doThings.length; i++) { if (typeof doThings[i] !== 'function') { throw new TypeError('not a function'); } } for (var i = 0; i < doThings.length; i++) { doThings[i](); } }

  • Can you recover? function dispatch(doThings) { if (!Array.isArray(doThings)) { throw new TypeError('not an array'); } for (var i = 0; i < doThings.length; i++) { if (typeof doThings[i] !== 'function') { throw new TypeError('not a function'); } } for (var i = 0; i < doThings.length; i++) { doThings[i](); } }

  • Catch and do what?

    for (var i = 0; i < doThings.length; i++) { try { doThings[i](); } catch (e) { ... } }

  • Catch and do what?

    for (var i = 0; i < doThings.length; i++) { try { doThings[i](); } catch (e) { ... } }

  • programmer err : rollback ::

  • programmer err : rollback ::

    rollback err : ?

  • you cant fix unexpected errors because their existence is

    unexpected

  • in node.js,

    aborton unexpected errors

  • goal is to the blast radius

    (not to write bug free code)

    minimize

  • continuing after an

    unhandled exception is a

    trade off

  • _BROKEN_

  • program error : program crash ::

  • program error : program crash ::

    OS error : ?

  • program error : program crash ::

    OS error : OS crash

  • in the browser,

    reportall unexpected errors

  • avoidhandling errors in global exception handlers

  • Preserving History 2

  • Working backward

    function getEpicStory(cb) { database.get('iliad', function(err, data) { if (err) { return cb(err); } return cb(null, data); }); }

  • We can do better

    database.get('iliad', function(err, data) { if (err) { err.cause = 'db error!'; err.timestamp = new Date(); return cb(err); } return cb(null, data); });

  • VError: we can do even better

    database.get('iliad', function(err, data) { if (err) { return cb(new VError(err, 'db error!')); } return cb(null, data); });

  • VError: we can do even better

    // redis error [Error: missing key] // our verror { [VError: db error: missing key] jse_shortmsg: 'db error', jse_summary: 'db error: missing key', jse_cause: [Error: missing key], message: 'db error: missing key' }

  • [2015-10-02T06:41:54.389Z] ERROR: test/11497 on lgml-aliu: db error: missing key VError: db error: missing key at Object. (/Users/aliu/Desktop/test/a.js:6:12) at Module._compile (module.js:434:26) at Object.Module._extensions..js (module.js:452:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:475:10) at startup (node.js:117:18) at node.js:951:3 Caused by: Error: missing key at Object. (/Users/aliu/Desktop/test/a.js:5:12) at Module._compile (module.js:434:26) at Object.Module._extensions..js (module.js:452:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:475:10) at startup (node.js:117:18) at node.js:951:3

  • restify-errors: the bees knees

    var errs = require('restify-errors');

    errs.makeConstructor('DatabaseError', { fooProp: 'someVal' });

    var myErr = new errs.DatabaseError('db error!'); // myErr.fooProp => someVal

  • restify-errors: the bees knees

    var myErr = new errs.DatabaseError('db error!'); myErr instanceof errs.DatabaseError // => true myErr instanceof VError // => true myErr instanceof Error // => true

  • Serving out the UI

    request('/api/story', function(err, fpStoryData) { if (err instanceof HttpError) { return res.render('NetworkErrorPage'); } else if (err instanceof DatabaseError) { return res.render('UnavailablePage'); } else { return res.render('ErrorPage'); } res.render('iliad', fpStoryData); });

  • [2015-10-06T17:50:31.561Z] ERROR: test/64987 on lgml-aliu: rendering error! RenderError: rendering error page! at Object. (/Users/aliu/Desktop/test/test4.js:10:10) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:501:10) at startup (node.js:129:16) at node.js:814:3 Caused by: DataValidationError: data validation error at Object. (/Users/aliu/Desktop/test/test4.js:10:32) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:501:10) at startup (node.js:129:16) at node.js:814:3 Caused by: RequestError: request error at Object. (/Users/aliu/Desktop/test/test4.js:10:63) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:501:10) at startup (node.js:129:16) at node.js:814:3 Caused by: BadRequestError: http 400 at Object. (/Users/aliu/Desktop/test/test4.js:10:86) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10)

  • [2015-10-06] ERROR: test/64987 on lgml-aliu: rendering error!

    Error: something happened at repl:1:7 at REPLServer.defaultEval (repl.js:132:27) at bound (domain.js:254:14) at REPLServer.runBound [as eval] (domain.js:267:12) at REPLServer. (repl.js:279:12) at REPLServer.emit (events.js:107:17) at REPLServer.Interface._onLine (readline.js:214:10) at REPLServer.Interface._line (readline.js:553:8) at REPLServer.Interface._ttyWrite (readline.js:830:14) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:501:10) at startup (node.js:129:16) at node.js:814:3

  • use

    for expected errors

    typed errors

  • 3The problem with catch

  • But {abstraction} solved this already!?

  • try: catch em all

    try { if (Math.random() > 0.5) { JSON.parse(input); } else { var x = y + 1; } } catch (err) {...}

  • > [SyntaxError: Unexpected token b]

    > [ReferenceError: y is not defined]

    expected:

    unexpected:

    JSON.parse(input);

    var x = y + 1;

  • Catching typed errors

    catch (err) { if (err instanceof SyntaxError) { ... } }

  • Dont forget to rethrow!

    catch (err) { if (err instanceof SyntaxError) { ... } else { throw err; } }

  • Centralize throwing

    catch (err) { throwUnexpected(err); // handle err if not thrown }

  • expected vs unexpected?

    catch (err) { if (err instanceof SyntaxError) { ... } else { throw err; } }

  • expected vs unexpected?

    > JSON.parse('bad input');

    > vart x;

  • try/catch cannot

    differentiatesource of errors

  • try: reduce surface area

    try { JSON.parse(input); } catch (err) { ... }

    var x = y + 1;

  • be very

    targetedwith try / catch

  • promises: catch em all

    foo.then(foo2) .then(foo3) .catch(function(err) { ... });

  • promises: unexpected behavior

    foo.then(foo2) .then(fooError) .catch(function(err) { if (!(err instanceof ExpectedErr)) { throw err; } });

  • $ node promiseDemo.js Unhandled rejection ReferenceError: y is not defined at foo (/test/promiseDemo.js:14:13) at /test/promiseDemo.js:8:3 at processImmediate [as _immediateCallback] (timers.js:368:17) From previous event: at Object. (/test/promiseDemo.js:7:3) at Module._compile (module.js:435:26) at Object.Module._extensions..js (module.js:442:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:311:12) at Function.Module.runMain (module.js:467:10) at startup (node.js:134:18) at node.js:961:3

  • $

  • $ echo $?

  • $ echo $?0

  • promises: unexpected behavior

    foo.then(foo2) .then(fooError) .catch(function(err) { if (!(err instanceof ExpectedErr)) { throw err; } });

  • promises: call done()

    foo.then(foo2) .then(fooError) .catch(function(err) { if (!(err instanceof ExpectedErr)) { throw err; } }) .done();

  • bluebird: catch only expected

    foo.then(foo2) .then(fooError) .catch(ExpectedError, function(err) { ... }); // EVERYTHING else throws, exits 1!

  • bluebird: throw by default

    Promise.onPossiblyUnhandledRejection(function(e) { throw e; });

  • surfacing unexpected errors should NOT be

    opt-in

  • Making Catch Better

    You don't need to sell me -- we had } catch (e if ...) { in SpiderMonkey and proposed it for ES3 (in 1998).

    - Brendan Eich

    try { ... } catch (e if ...) { ... }

  • Refutable Pattern Matching

    GuardedPattern(refutable) ::= Pattern(refutable) "if" AssignmentExpression

  • Looking Forward

  • for rich error objects

    5 npm install verror npm install restify-errors

  • Handle every error from every async API

    4

  • Understand how your abstractions handle errors

    3

    (swallow? rethrow?)

  • Abort immediately on unexpected errors to minimize the blast radius

    2

  • Design and build applications with error handling in mind

    1

    (exceptions are NOT exceptional!)

  • Error handling bible https://www.joyent.com/developers/node/

    design/errors

    https://www.joyent.com/developers/node/design/errors

  • Aborting on unexpected errors https://github.com/nodejs/node-v0.x-archive/

    issues/5114 https://github.com/nodejs/node-v0.x-archive/

    issues/5149

    https://github.com/nodejs/node-v0.x-archive/issues/5114https://github.com/nodejs/node-v0.x-archive/issues/5149

  • Module up! verror

    https://github.com/davepacheco/node-verror restify-errors

    https://github.com/restify/errors

    https://github.com/davepacheco/node-verrorhttps://github.com/restify/errors

  • Get involved! https://esdiscuss.org/topic/try-catch-conditional-

    exceptions-in-light-of-generators http://wiki.ecmascript.org/doku.php?

    id=strawman:pattern_matching

    https://esdiscuss.org/topic/try-catch-conditional-exceptions-in-light-of-generatorshttp://wiki.ecmascript.org/doku.php?id=strawman:pattern_matching

  • FinAlex Liu @stinkydofu [email protected]

  • Image Credits

  • Image Credits

  • Image Credits

  • Image Credits http://lizclimo.tumblr.com/post/77531229510/youre-doing-it-wrong http://fantasio.deviantart.com/art/Godzilla-in-the-mountains-454304871 http://rockpapercynic.tumblr.com/post/85581052929/first-revealed-pagew-your-bad-

    idea-illustration

    http://lizclimo.tumblr.com/post/77531229510/youre-doing-it-wronghttp://fantasio.deviantart.com/art/Godzilla-in-the-mountains-454304871http://rockpapercynic.tumblr.com/post/85581052929/first-revealed-pagew-your-bad-idea-illustration