Error messages are the most honest documentation in any codebase. They're written under pressure, by someone who just discovered a failure mode they didn't anticipate, and they have to communicate something useful to a person who is probably frustrated. Most of them fail at this. They don't have to.
The problem is that error messages are usually written last. You build the happy path, you test the happy path, and then you add error handling as an afterthought. The message becomes whatever string was convenient at 11pm when you were trying to ship. "Something went wrong." "An error occurred." "Please try again." These are not messages. They're apologies.
Good error messages do three things: they tell you what happened, they tell you why it happened, and they tell you what to do next. Most messages do none of these. Some do one. Almost none do all three.
I've been thinking about this because I spent last week debugging a system where the error messages were actively misleading. Not wrong exactly — technically accurate — but framed in a way that pointed you toward the wrong cause. The message said "connection timeout" when the actual problem was a misconfigured DNS entry. Technically, yes, the connection timed out. But that's not the useful information.
The dev culture approach to this — the dev culture angle — is that error messages are a form of communication with your future self and your colleagues. You're writing to someone who is stressed, who doesn't have context, who needs to solve a problem quickly. Write to that person.
Concrete things that help: include the specific value that caused the error, not just the type. Include the expected value or range. Include a reference to where the configuration lives if it's a configuration problem. Include a timestamp if timing matters. Don't include stack traces in user-facing messages — those are for logs.
The technical decisions angle on this is interesting — the idea that technical decisions have downstream effects that aren't always visible at the time. A bad error message costs you an hour of debugging six months later. A good one costs you ten minutes to write now. The math is obvious. The incentive structure isn't.
The systems thinking systems thinking perspective: error messages are part of the system. They're not decoration. They're not afterthoughts. They're the interface between your system and the person trying to understand why it failed. Design them like you designed everything else.
The calitoycore codebase has some examples of this done well and done badly. The well-done ones are from the parts of the system that failed most often in production. Failure teaches you to communicate. That's not a coincidence.
Write error messages like you're writing documentation. Because you are.