Errors, Debugging and Testing

The skills that separate code that works on your machine from code you would put in front of users: throwing and catching deliberately, designing your own error types, validating everything that crosses a boundary, driving the debugger instead of sprinkling logs, and writing tests (including the test runner itself).

What you will be able to do

  • Throw, catch and rethrow deliberately, and explain what finally does to a return
  • Design an error taxonomy with Error subclasses and chain context with cause
  • Serialise an error for a log without losing its message, stack or cause
  • Write guard clauses that fail fast with the right error type
  • Validate untrusted input at the boundary and return a parsed shape instead of a boolean
  • Use the whole console API, not just log
  • Drive breakpoints, conditional breakpoints and logpoints instead of editing in console.log
  • Bisect a bug through the data, the pipeline and the history instead of guessing
  • Write arrange, act, assert tests and build a working test runner in about thirty lines
  • Choose between node:test and Vitest, and know what neither of them should test

Lessons

  1. Errors and try/catch — A throw is a second return path. try/catch is how you decide which function is responsible for handling it. (14 min)
  2. Custom Errors and cause — An error type is a label your handlers can match on. cause is the chain of "and this is why" that leads back to the first failure. (15 min)
  3. Defensive Code and Validation — Check at the door, then trust the room. Every value that crosses into your code gets parsed into a shape you know, or it does not get in. (15 min)
  4. Debugging Toolkit — Debugging is not guessing faster. It is halving the distance between the last place the value was right and the first place it was wrong. (16 min)
  5. Testing Fundamentals — A test is three lines: set up a situation, run one thing, compare the result to the answer you wrote down first. (18 min)