SLIDE 1 NODE.JS APPLICATIONS
BUILDING SCALABLE AND DEPENDABLE
JAMUND FERGUSON
SLIDE 2 The Mystery of the Missing Stack Trace
🕶
SLIDE 3 THE MYSTERY OF THE MISSING STACK TRACE
Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11) at ServerResponse.header (/web/mynodeapp/node_modules/express/lib/response.js:767:10) at ServerResponse.send (/web/mynodeapp/node_modules/express/lib/response.js:170:12) at ServerResponse.res.send (/web/mynodeapp/node_modules/pplogger/index.js:225:18) at done (/web/mynodeapp/node_modules/express/lib/response.js:1004:10) at Stub.callback (/web/mynodeapp/node_modules/adaro/lib/engine.js:137:22) at Stub.flush (/web/mynodeapp/pp/node_modules/dustjs-linkedin/lib/dust.js:513:10) at Chunk.end (/web/mynodeapp/node_modules/dustjs-linkedin/lib/dust.js:612:15) at /web/mynodeapp/node_modules/adaro/lib/patch/index.js:89:53 at /web/mynodeapp/node_modules/adaro/lib/reader/js.js:39:13 at /web/mynodeapp/node_modules/engine-munger/lib/munger.js:85:13 at /web/mynodeapp/node_modules/engine-munger/lib/cache.js:65:13 at /web/mynodeapp/node_modules/graceful-fs/graceful-fs.js:78:16 at /web/mynodeapp/node_modules/async-listener/glue.js:188:31 at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:380:3)
SLIDE 5 We had no idea why or where our apps were failing
SLIDE 6 WHAT CAN WE DO?
▸ Compare a diff between last known working code ▸ Look through other logs for more information (nginx logs, access logs, etc) ▸ Look at system metrics (is there a memory leak or CPU spike?) ▸ Add console.log statements somewhere??? ▸ Advanced debugging techniques (post-mortem debugging, heapdumps, etc.)
THE MYSTERY OF THE MISSING STACK TRACE
SLIDE 7
SLIDE 8
SLIDE 9 DECRYPT RETURNS A PROMISE ADDUSER EXPECTS A STRING LOG EXPECTS A SMALL OBJECT OR A STRING
SLIDE 10
💤
SLIDE 11 THE MYSTERY OF THE MISSING STACK TRACE
LESSONS LEARNED
▸ We need better static analysis ▸ We need better debugging tools ▸ We need a consistent way to handle errors ▸ We need to better understand our logging & monitoring💤
SLIDE 12 FLOWTYPE & ESLINT
STATIC ANALYSIS WITH
SLIDE 13 TYPE CHECKING COULD HAVE CAUGHT THAT BUG WITH 2-LINES OF CODE
STATIC ANALYSIS
SLIDE 14 STATIC ANALYSIS
PREVENTING BUGS WITH TYPES
SLIDE 15 STATIC ANALYSIS
PREVENTING BUGS WITH TYPES
SLIDE 16 STATIC ANALYSIS
FLOW WON’T LET THAT SLIDE
SLIDE 17 STATIC ANALYSIS
WHY ADD TYPES TO YOUR JS?
▸ Prevents large % of bugs ▸ Helps surface architectural problems ▸ Both Flow and TypeScript are well maintained, high quality tools ▸ Both syntaxes are light-weight and easy to use ▸ Both allow for gradual adoption
SLIDE 18
SLIDE 19
🙌 🍪 🚬
SLIDE 20 Type systems have a lot in common with linters
SLIDE 21 Each JS File Parser (Acorn) Abstract Syntax Tree (AST) Rules Warning Success Errors Architecture of JavaScript Linter (ESLint)
SLIDE 22 Linters can only think about one file at a time
SLIDE 23 All Your JS Files Flow Types & Relationships Graph Architecture of JavaScript Type System (Flow) Single JS File Warning Success Errors Flow Graph
SLIDE 24 COULD A LINTER HAVE HELPED US WITH OUR MYSTERY BUG?
SLIDE 25 STATIC ANALYSIS
SLIDE 26 STATIC ANALYSIS
SLIDE 27 STATIC ANALYSIS
STATIC ANALYZERS AND FORMATTERS ARE PRETTY COOL
FlowType ESLint
Prettier
SLIDE 28 Unfortunately, we still get bugs from time to time
SLIDE 29 INSPECTOR MODULE
DEBUGGING USING THE
SLIDE 30 THE BUILT-IN INSPECTOR MODULE
SLIDE 31
SLIDE 32 THE BUILT-IN INSPECTOR MODULE
SETTING UP A DEBUG MODE
TURN IT ON TURN IT OFF
SLIDE 33 THE BUILT-IN INSPECTOR MODULE
SLIDE 34 THE BUILT-IN INSPECTOR MODULE
Set Breakpoint
SLIDE 35 THE BUILT-IN INSPECTOR MODULE
PAUSE ON UNCAUGHT EXCEPTIONS
SLIDE 36 THE BUILT-IN INSPECTOR MODULE
PAUSE ON CAUGHT EXCEPTIONS
SLIDE 37 THE BUILT-IN INSPECTOR MODULE
SLIDE 38 THE BUILT-IN INSPECTOR MODULE
SLIDE 39 THE BUILT-IN INSPECTOR MODULE
SLIDE 40 THE BUILT-IN INSPECTOR MODULE
SLIDE 41 Don’t try this in production
STOP
SLIDE 42 THE BUILT-IN INSPECTOR MODULE
SLIDE 43 THE BUILT-IN INSPECTOR MODULE
SLIDE 44 THE BUILT-IN INSPECTOR MODULE
https://chromedevtools.github.io/devtools-protocol/
SLIDE 45 THE BUILT-IN INSPECTOR MODULE
Default Node Error Inspector Based Error
SLIDE 46 DEBUGGING NODE.JS APPS
FIND A DEBUGGING APPROACH THAT WORKS FOR YOU AND YOUR TEAM
SLIDE 47 ASYNC/AWAIT
ERROR HANDLING USING
SLIDE 48 ERROR HANDLING WITH ASYNC/AWAIT
SLIDE 49 Errors thrown inside async functions get converted into rejected Promises
💢
SLIDE 50 ERROR HANDLING WITH ASYNC/AWAIT
SLIDE 51 Async Middleware Pattern
SLIDE 52 ERROR HANDLING WITH ASYNC/AWAIT
T H I S I S P R E T T Y N I C E
SLIDE 53 ERROR HANDLING WITH ASYNC/AWAIT
B U T W E D O N ’ T A C T U A L LY C AT C H I T 💤 E R R O R S W I L L B U B B L E U P
SLIDE 54 ERROR HANDLING WITH ASYNC/AWAIT
💦
SLIDE 55 ERROR HANDLING WITH ASYNC/AWAIT
PASS IN YOUR ASYNC MIDDLEWARE RETURN A STANDARD MIDDLEWARE FUNCTION CATCH ANY ERRORS PASS THOSE TO THE EXPRESS ERROR HANDLER EXECUTE THE ASYNC MIDDLEWARE APPLY AS NEEDED
SLIDE 56 ERROR HANDLING WITH ASYNC/AWAIT
💦
SLIDE 57 Make it easy for your engineers
to do the right thing
SLIDE 58 ERROR HANDLING WITH ASYNC/AWAIT
SLIDE 59 Custom Error Classes
SLIDE 60 CUSTOM ERRORS
SLIDE 61 CUSTOM ERRORS
SLIDE 62 CUSTOM ERRORS
1. 2. 3.
SLIDE 63 CUSTOM ERRORS
SLIDE 64 CUSTOM ERRORS
SLIDE 65 CUSTOM ERRORS
SLIDE 66 CUSTOM ERRORS
SUMMARY
▸ Don’t use object literals or strings for errors (missing stack trace) ▸ Use the Error built-in object ▸ Subclass Error to add statusCodes or to convert error codes into user-
friendly error messages for localization, etc
▸ We basically have one error class per micro-service to handle parsing the
errors out of the response….
SLIDE 67 The Mystery of the Client-Side Errors
SLIDE 68 THE MYSTERY OF THE CLIENT-SIDE ERRORS
CLIENT-SIDE MONITORING
BUTTON DOESN’T WORK REAL ISSUE USUALLY IN DEV TOOLS
SLIDE 69 THE MYSTERY OF THE CLIENT-SIDE ERRORS
CLIENT-SIDE MONITORING
window.onerror = function (msg, url, line, col, error) {
// 1. clean up the data
// 2. log to server w/AJAX or sendBeacon() API
}
SLIDE 70 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE NOTICED A SPIKE DURING DEPLOY
SLIDE 71 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE CONGRATULATED OURSELVES…THEN ACTUALLY LOOKED INTO THE BUG
🏇 😯
SLIDE 72 THE MYSTERY OF THE CLIENT-SIDE ERRORS
SLIDE 73 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE HAVE A LOT OF SERVERS
SLIDE 74 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE HAVE A LOT OF SERVERS
SLIDE 75 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE HAVE A LOT OF SERVERS
SLIDE 76 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE HAVE A LOT OF SERVERS
SLIDE 77 THE MYSTERY OF THE CLIENT-SIDE ERRORS
WE HAVE A LOT OF SERVERS
SLIDE 78 THE MYSTERY OF THE CLIENT-SIDE ERRORS
UI Code Server code
A B
SLIDE 79 THE MYSTERY OF THE CLIENT-SIDE ERRORS
ROLLING BACK MADE THINGS WORSE
SLIDE 80 THE MYSTERY OF THE CLIENT-SIDE ERRORS
LESSONS LEARNED
▸ UI is a huge monitoring blind-spot ▸ Be aware of how the deploy process affects users ▸ Try to make your code changes backwards compatible ▸ Consider separating UI and server deploys
SLIDE 81 BUILDING SCALABLE AND DEPENDABLE NODE.JS APPLICATIONS
▸Use static analysis (including types) to catch bugs early ▸Have a plan for debugging apps in production ▸Adopt a consistent approach to error handling ▸Know how to access all of your logs ▸Don’t forget to monitor client-side errors
SLIDE 82
THE END