Your logs are useless until the one night you need them
Nobody reads logs on a normal day, so their quality never gets tested until an incident — at which point you discover they're unsearchable, unlinked, and missing the one field that would have told you what happened.
Hammad Iqbal
Software Engineer
Logging feels done the moment `console.log` prints something useful during development. Then a request fails for one customer at 1am, you open the logs, and you're staring at ten thousand lines of unstructured text with no way to isolate that user's request, no timestamp you can correlate across services, and no record of the input that caused the error. The logs existed the whole time. They just weren't built for the only situation that matters.
- Log quality is only ever tested during an incident, which is the worst time to find out it's bad.
- A request ID threaded through every log line is the difference between grep and guesswork.
- Log the inputs and the decision, not just the error — the stack trace tells you where, not why.
Unstructured logs don't survive scale
A line like `user login failed` is fine when you have one server and ten users. Once you have real traffic it's a needle in a haystack you can't query — you can't filter by user, by endpoint, or by error type, because none of those are fields, they're just words in a sentence. Structured logging, where each line is JSON with consistent keys, costs almost nothing to adopt and turns your log store into something you can actually ask questions of.
Without a correlation ID, you're reconstructing from memory
One user action fans out into a dozen log lines across the API, the worker queue, and two external calls. If nothing ties them together, an incident becomes an exercise in matching timestamps by eye and hoping you didn't miss one. Generate a request ID at the edge, attach it to every log line, and pass it downstream in a header.
- Generate the ID as early as possible — middleware, before any handler runs
- Put it in every log line automatically via the logger's context, not by hand
- Return it in the response headers so a user can quote it in a bug report
- Forward it to third-party calls so their support team can find the same request
Log the decision, not just the failure
A stack trace tells you which line threw. It rarely tells you why that line threw on this request and not the last thousand. The logs that actually shorten an incident record the inputs and the branch taken: which plan the user was on, which feature flag evaluated true, what the external API returned before you tried to parse it. That context is cheap to write when you're coding the path and impossible to recover once you're debugging it.
Treat the logs as a feature with exactly one user — the person debugging an incident at 1am — and build them for that person. On every other day it won't matter, and that's the point.
Have a project this kind of thinking applies to?
Tell me what you're building — I read every message myself.