Context

A safety net has two jobs: it has to catch the user, and it has to talk to them when it does. The validation boundaries established in ADR-001, ADR-002, and ADR-004 handle the catching. This ADR concerns the speaking half, which is the part that usually goes unbudgeted.

The speaking half turns out to be the better-researched of the two, and the research is firm. People do read error messages: an eye-tracking study of developers (Barik et al.) found they allocate roughly a quarter of their visual attention to error text, and that reading a message is comparable in difficulty to reading source code. The message is not a footnote to the work, it is work imposed on the reader. Rewriting cryptic errors into enhanced ones (Becker et al.) reduced errors overall and, notably, reduced repeated errors, the signature of someone stuck in a loop, though the literature also shows that prettier wording alone is not the mechanism. Traver's HCI analysis frames the message as the primary interface between the system's internal state and the user's mental model. Later work shows readability is relative to the reader's experience. Nielsen's ninth heuristic (help users recognize, diagnose, and recover) and fifth heuristic (prevent the error in the first place) have held since the 1990s.

Problem

Perfect code chases the catching half and forgets the speaking half. A net that catches the user silently and then hands back a stack trace is only half a net. When the tool's audience is scientists rather than compiler engineers, a message written in the parser's vocabulary ("expected identifier") fails to bridge to the user's world ("this needs a variable name"), and the cost of that gap is measurable in wasted attention and repeated mistakes.

Decision

Error messages are treated as first-class outputs, designed and budgeted like code rather than appended as an afterthought. Seven rules, each mapped to the research above, govern diagnostics at validation boundaries:

  1. Budget the message like code. It consumes real reader attention, so it earns real design effort.

  2. Frontload the fix. The reader is spending attention, so lead with what to change, not with a preamble.

  3. Enhance with cause and action, not just nicer words. Name the likely cause and the next action, because rewording alone does not help.

  4. Speak the user's domain, not the parser's. Bridge to the reader's mental model of what they were trying to do.

  5. Write for the reader's level, and prove it. There is no universally clear message, only one clear to a specific audience, so the audience must be named and checked.

  6. Recognize, diagnose, recover, and prevent. Follow Nielsen's ninth heuristic, and prefer the fifth: the best error is the one made impossible.

  7. Never blame, and never claim certainty you do not have. State what is known and stop there.

Alternatives Considered

Emit raw stack traces or parser errors

Rejected. These describe the machine's internal state, not the user's intent, and shift the diagnostic burden onto the person least equipped to carry it.

Reword messages to be friendlier, and stop there

Rejected. The evidence shows tone alone does not reduce repeated errors. Naming the cause and the next action is what helps.

Assume developers skip the message and read the code

Rejected. Eye-tracking evidence directly contradicts this. Readers genuinely read the message.

Consequences

Positive

  • Diagnostics reduce repeated errors and time spent stuck.

  • Messages bridge to the user's domain rather than the parser's.

  • The message becomes a designed, reviewable artifact.

Trade-offs

  • Writing cause-and-action messages calibrated to an audience is ongoing work.

  • Message quality has to be maintained alongside the behavior it describes.

  • Domain-specific phrasing must track the domain as it evolves.

Implementation Evidence

The rules are applied to the diagnostics in Pyxel Config Lab, whose audience is scientists rather than tooling engineers.

  • Pyxel Config Lab:

https://gitlab.com/dobybaxter127/pyxel-config-lab
  • Related article:

https://dev.to/dobybaxter127/you-dont-need-perfect-code-you-need-a-safety-net-that-talks-back-3nl7

Relationship to the Broader Engineering Approach

ADR-001 catches invalid states at a boundary. This ADR governs what that boundary says when it catches one. A boundary that fails silently or unintelligibly protects the system but abandons the user, so clear diagnostics are part of what makes a validation boundary complete.

Decision Outcome

A caught failure is treated as a communication event, not just a rejection:

Invalid input → caught at boundary → message that names cause and next action

This ADR should be revisited if the primary audience of a tool changes, since the correct wording depends on who is reading.