Debug code

Add focused diagnostic logs

Log event, request identity, state category, timing, and error without dumping secrets or entire objects.

10 minute lesson

~~~

When you cannot attach a debugger — production, a background worker, someone else’s machine — logs are how the process tells you what happened. But console.log('here') and console.log(user) produce noise you cannot search and dumps you cannot share. A useful log answers what happened and connects related work. Random values without event names create noise.

Log events, not values

Give every diagnostic line an event name and the identifiers that tie it to one request:

console.error({
  event: 'invoice_load_failed',
  requestId,
  invoiceId,
  error: error.message,
})

This one line is searchable (invoice_load_failed), joinable (the same requestId appears in the access log and in downstream calls), and specific (which invoice). Compare that to console.log('error!', error): you cannot grep for it, you cannot tell which request produced it, and it might print an entire response object.

Place logs at boundaries

The highest-value log points sit where data crosses a boundary: request received, external call started, external call finished with its duration, decision taken. A before/after pair around a suspicious call answers the two questions diagnostic logging is actually good at — did we get here, and with what:

console.error({ event: 'invoice_fetch_start', requestId, invoiceId })
const invoice = await fetchInvoice(invoiceId)
console.error({ event: 'invoice_fetch_done', requestId, ms: Date.now() - started })

Trigger one failure and locate the event by request ID. If you can follow one request through your logs from entry to error, the instrumentation works. If you find yourself scrolling, you logged too much or connected too little.

What must never be logged

Confirm the output avoids tokens, cookies, full customer records, and stack duplication. Log error.message and identifiers, not whole objects — a full user record in a log file is a data leak with a retention period. Production logs require access and retention controls, same as the database they describe.

Temporary logs need removal or deliberate promotion. Before closing the bug, either delete the diagnostic lines or keep them intentionally as permanent, named events. A codebase littered with forgotten debug 2 lines makes the next investigation harder, not easier.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →