Robert Nnamani left a comment under my last piece with a better example than anything in the post itself, and I have been carrying it around for a day.
He spent a week trusting an SES 200 OK. 200 means accepted, not delivered. The mail died downstream and no log said so. Then the line that made me put my coffee down: the expensive part was not the lost emails, it was the weeks he spent trusting a green dashboard.
That is the shape of the bug. Not an error that hides its cause. A success that overstates its outcome.
Errors get read. Successes don't.
An error message is read the moment it appears, by someone who is already annoyed, and every one of those readers is a potential bug report. So error copy gets pressure. It still ships bad ("An error occurred."), but at least someone looked at it.
A success message is read by nobody. It is written once, by the author of the happy path, on the day the happy path worked. Nothing ever files a ticket against it. A green checkmark is not a report of a problem, so the string under it is the least-reviewed text in the system.
Which is how you get a response that is perfectly true and completely useless:
-
200 OKis the server reporting its own step. - "Your message has been sent." is you reporting the user's situation, and it is usually a guess.
The response describes your step. The user is asking about their situation.
The same bug from two directions
An error that says "Not found" and a success that says "Sent" are the same defect. Both describe the server's work and leave the user's state unstated. The error fails by omission. The success fails by assertion, which is worse, because the user stops watching. You cannot alert on a system that reports success, so the lie is also a blindfold.
The test I actually use
Read the sentence cold, with no other context on screen. Do I know what happened to my thing, and what to do next?
- "Not found" - no. Not my thing, not my next step.
- "200 OK" - no. Something happened somewhere. Unknown which.
- "We couldn't find that file in this project. Check the name, or pick one from the list." - yes.
- "Accepted for delivery. If it hasn't arrived in five minutes, check your outbound log." - yes, and it tells you where the silence would come from.
"Or is it still enforced by taste?"
Robert asked whether a lint or a review checklist enforces this. My honest answer: no lint I have seen can hold it. A rule can require a noun and a next step and still pass "The file was not found," which is exactly the sentence that fails the cold read. A linter can see the shape of the sentence. It cannot see whose situation the sentence is about.
Two things do hold, and neither is taste:
- The string is written by the person who debugged the failure. Not a tech writer, not a ticket, not a follow-up sprint. Whoever watched the mail die at 1am writes the line the user will read, and it ships in the same commit as the fix. A guideline written by someone who has never watched a user fail degrades into "An error occurred."
- Put the sentence in the test. Assert on the response body. If the message is in the assertion, it is a spec, and it goes red the day someone rewords it carelessly.
That is the only enforcement I trust. Not a rule about your copy. A test that pins it. Everything else is habit, and habits get dropped the first week a deadline moves up.
Thanks to @robertpaschal for the SES case and for the question.
Top comments (0)