错误提示通过三段式结构(发生了什么、为什么、怎么解决)替代原始异常文本,并建立「重复的支持问题 = 界面 Bug」的内省机制。
错误提示为什么要一直改到陌生人都能看懂
Every RAXXO error message has to pass a one-rule test before it ships: a stranger with zero context has to know what broke and what to do next
每一条 RAXXO 错误提示在发布前都必须通过一条规则的检验:一个完全不了解产品的人,必须能看懂哪里出了问题以及下一步该怎么做。
The three-part shape I use now, what happened, why, what to try, replaced years of generic error text across Git Dojo, OhNine, and Statusline Builder
我现在使用的「发生了什么—为什么—接下来怎么办」三段式结构,替换掉了 Git Dojo、OhNine 和 Statusline Builder 多年来那些千篇一律的错误提示文字。
A support question that repeats twice is treated as a copy bug, not a documentation gap, and gets fixed in the interface itself
同一个支持问题如果出现两次,不再视为文档缺失,而是视为产品本身的文案 bug,直接在界面里修复。
Rewriting error copy after shipping is now routine maintenance, not a rare emergency pass
发布后回头重写错误提示文案,如今已是常规维护工作,而不是偶尔紧急突击。
错误提示文案为何获得的关注远低于其应有的程度
Error messages are the part of a product almost nobody designs on purpose. The happy path gets wireframes, gets copy passes, gets tested against real users before anything ships. The error states get whatever text was fastest to type while the actual feature was still being built, usually something close to the raw exception, and then everyone moves on because the feature works and the deadline is closer than the polish. I did exactly that for the first version of more than one RAXXO tool, and I did not think about it again until support questions started arriving that had an obvious pattern: someone had hit an error, read the message, and still had no idea what to do.
错误提示是产品中几乎没有人会主动花心思设计的部分。happy path 有线框图、有文案审核、有上线前的真实用户测试。而错误状态呢?用的就是功能开发期间打起来最快的文字,通常就是接近原始异常信息的那类东西,然后所有人就继续往前走了——因为功能能跑、截止日期比打磨更重要。我自己在不止一个 RAXXO 工具的第一版就是这么做的,一直没再想过这个问题,直到收到支持问题开始呈现出一种明显的模式:有人遇到了错误,看完了提示,还是完全不知道接下来该怎么办。
That pattern is the real cost of a bad error message. It is not that the tool broke, tools break, that is normal and expected. The cost is that a broken moment turned into a support message instead of a self-solved problem, and a support message is expensive in a way a clean recovery is not. It takes my time to answer, it takes the user's patience to wait for an answer, and it leaves them holding a worse impression of the tool than the actual bug deserved. A confusing error message can do more damage to trust than the failure that triggered it.
这种模式才是糟糕错误提示的真正代价。工具坏了不是问题,工具会坏,这很正常。真正的代价在于:一个坏掉的瞬间变成了一个支持工单,而不是用户自己解决的问颚,而支持工单的成本是干净利落的自助恢复所不具备的。回答需要花我的时间,等待回答需要花用户的耐心,而且最终留给用户的印象比实际 bug 应该带来的要差得多。一条令人困惑的错误提示,对信任的损害可以超过触发它的那次故障本身。
Once I started treating error text as a real design surface instead of a leftover, the fix was less about cleverness and more about discipline. Every error message a RAXXO tool shows now has to pass one test before it ships: could a stranger who has never seen this tool before, who has no idea what a stack trace is, read this message and know two things, what just happened and what to try next. If the answer is no, the message is not done, regardless of whether it is technically accurate. Technical accuracy and usefulness are not the same property, and I used to confuse them constantly.
一旦我开始把错误提示文字当作真正的设计面来处理、而不是当作遗留物,解决方案就更多靠的是纪律而不是耍聪明。现在每一个 RAXXO 工具显示的错误提示,在发布前都必须通过一条测试:一个从未见过这个工具的人、一个完全不知道 stack trace 是什么的人,读完这条消息后能否知道两件事——刚刚发生了什么,以及接下来可以试什么。如果答案是否,消息就没有完成,无论它在技术层面有多准确。技术准确性和有用性不是同一回事,而我以前经常把两者混为一谈。
现在每条消息都遵循的三段式结构
The shape I settled on has three parts, in a fixed order, and I apply it the same way whether the error is a failed network request in OhNine or a bad input in Git Dojo. First, what happened, stated in plain language, no jargon, no internal names for internal systems. Second, why it happened, only if the reason is something the user can actually act on, and left out entirely if it is not. Third, what to try next, always concrete, never a vague instruction like "please try again later" with nothing else attached.
我最终确定的三段式结构有三个部分,按固定顺序排列,无论错误是 OhNine 中的网络请求失败还是 Git Dojo 中的输入错误,我都用同样的方式套用。第一,发生了什么,用通俗的语言表述,不用术语,不用内部系统的内部名称。第二,为什么会发生,只在原因用户确实可以采取行动时才写,如果没有就完全省略。第三,接下来可以试什么,总是给出具体的步骤,绝不是"请稍后重试"这样空洞的指示外加什么都没有。
The "why" part is the one I cut most often when I catch myself drafting an error message, because it is the easiest place to smuggle in a technical explanation that feels honest but helps nobody. A user does not need to know that a request timed out because of an upstream rate limit. They need to know whether trying again will work, whether they should wait, or whether the thing they were trying to do is not going to work at all right now. Explaining the mechanism without explaining the consequence is a habit I had to actively train myself out of.
"为什么"这一部分是我在写错误提示时最常删掉的部分,因为它是最容易偷偷夹带技术解释的地方——看起来很诚实,但实际上帮不到任何人。用户不需要知道请求因为上游限速而超时了。他们需要知道的是重试有没有用、需不需要等、还是他们刚才想做的事情现在根本做不了。只解释机制不解释后果,是我必须主动训练自己改掉的习惯。
The "what to try next" part is where most of the actual improvement happened. A message that ends on the problem and stops is a dead end, and dead ends are exactly what turn into support questions. Every error state I write now ends on an action, even if that action is as small as "check your connection and try again" or "this file needs to be under 10MB." A user who has a next step rarely writes in. A user staring at a wall with no door does.
"接下来怎么办"这一部分是实际改进发生最多的地方。一条消息如果停在问题描述就结束,就是一条死路,而死路正是变成支持工单的原因。我现在写的每一种错误状态都以一个行动结尾,哪怕这个行动小到"检查一下网络连接然后重试"或者"这个文件需要小于 10MB"。有下一步可走的用户很少会写工单。站在一堵没有门的墙前面的用户才会。
Statusline Builder is the tool where this shift is easiest to see because the surface area for errors is small and specific, mostly malformed input and failed previews. The old version of the tool would show something close to a parser error. The current version names the exact part of the input that failed and shows a corrected example inline, right next to the broken one. That single change, showing a correct example next to the failure instead of just describing the failure, cut the related support questions to close to zero. People do not always need an explanation. Sometimes they just need to see the shape of the right answer next to the shape of what they typed.
Statusline Builder 是这种转变最容易观察到的工具,因为错误的涉及面小而具体,主要就是格式错误的输入和失败的预览。旧版工具会显示接近解析器错误的信息。新版则直接指出输入中具体哪个部分出了问题,并在旁边直接显示一个修正后的示例,挨着错误的那一个。这一处改动——在错误旁边展示正确答案的样式而不是仅仅描述错误——使相关支持问题几乎降到了零。人们不总是需要解释。有时候他们只是需要看到正确答案的形状紧挨着自己输入的形状。
把重复出现的问题当作文案 bug 来处理
The rule that keeps this from decaying back into leftover error text is simple: if the same confused support question comes in twice, that is not a documentation problem, it is a copy bug in the product, and it gets fixed in the interface, not explained away in a reply. I used to answer the same question a second and third time with slightly better phrasing each time, treating it as a communication problem on my end that a better-written reply would eventually solve. It never did, because the reply was never the thing the user saw at the moment of confusion. The error message was, and the error message was still wrong.
防止错误提示退化回遗留文字的规则很简单:如果同一个令人困惑的支持问题出现两次,这不是文档问题,而是产品本身的文案 bug,应该在界面里修复,而不是在回复里解释掉。我以前会第二、第三次回答同一个问题,每次措辞稍好一点,把它当作是我这边的沟通问题,认为写得更好的回复最终能解决。它从来没有解决过,因为用户在困惑的那一刻看到的从来不是回复。错误提示才是,而错误提示仍然是错的。
This reframing changed how I read support messages. A repeated question is not noise to answer faster, it is a signal pointing directly at a specific string in a specific file that needs to change. I keep a short running list, nothing elaborate, just a note every time a question repeats, and I treat that list the same way I treat any other backlog, something that gets worked through instead of something that gets an apologetic reply and no follow-up. Most weeks the list has one or two entries. Most fixes take minutes once I know exactly which message is the problem, because the hard part was never writing the better sentence, it was noticing which sentence needed rewriting.
这种重新框架改变了我看待支持工单的方式。重复的问题不是需要更快回答的噪音,而是一个信号,直指某个具体文件里的某段具体文字,需要改。我维护一个简短的运行列表,不复杂,只是在每次问题重复时记一笔,我把这份列表和其他所有待办一样对待——是需要被处理掉的东西,而不是收到一条道歉回复然后没有后续的东西。大部分时候列表每周有一两条。大多数修复一旦确定具体是哪条消息有问题,只需要几分钟就能搞定,因为难点从来不是写出更好的句子,而是注意到哪句话需要被改写。
The changelog habit I already run across every RAXXO tool made this easier to stick to, because a copy fix to an error message is a small enough change that it is tempting to skip logging it. I log it anyway, one line, because the pattern across those one-line entries is its own kind of evidence. Looking back at a month of changelog entries and seeing four separate error-copy fixes for the same tool tells me something a single incident never would, that the whole error surface for that feature probably needs a second pass, not just another patch.
我在每一个 RAXXO 工具上都保持写 changelog 的习惯,这让坚持这件事变得更容易,因为错误提示的文案修复是一个足够小的改动,很容易就不记录了。但我还是记录它,就一行,因为把这些一行行记录汇集起来的模式本身就是一种证据。回头看一个月的 changelog 条目,发现同一个工具有四条独立的 error-copy 修复,这告诉我一个单一事件永远不会告诉我的信息——那个功能的整个错误提示面可能需要再过一遍,而不是再打一个补丁。
测试在哪里会失效,以及我如何应对
The one-rule test, could a stranger understand this and know what to do, is not perfectly applicable everywhere, and pretending otherwise would be dishonest. Some errors are genuinely ambiguous even to me. A network failure between a user's browser and a service I do not control does not always come with a clean, specific reason, and inventing one just to fill the "why" slot would be worse than leaving it out. In those cases I write the most honest version of "something did not connect" I can manage, skip the fake specificity, and put the entire weight of the message on the "what to try next" part instead, since that is the part still fully within my control regardless of what actually failed upstream.
这条单规则测试——陌生人能否看懂并知道接下来怎么办——并不是哪里都完美适用,假装不是这样是不诚实的。有些错误在我自己看来也确实是模糊的。用户浏览器和我无法控制的服务之间的网络故障,并不总是附带一个干净、具体的原因,而编造一个来填充"为什么"这一栏,比直接省略更糟糕。在这些情况下,我会写出一个我能做到的、最诚实的"某些东西没有连接上"的版本,跳过虚假的特异性,把整条消息的分量放到"接下来怎么办"这部分上,因为无论上游实际发生了什么,这一部分仍然完全在我的控制范围内。
The other place the test breaks down is timing. An error message that reads perfectly clearly on its own can still land badly if it shows up at the wrong moment, mid-task, with no indication of whether the user's progress up to that point was saved. I learned to check not just the wording of a message but the moment it appears, because a technically well-written error shown at a moment of real anxiety, did I just lose my work, reads as cold no matter how plain the language is. The fix there is usually not more words, it is reordering what the message leads with. State the safe part first, your progress is saved, before the problem part. People read the first clause under stress and often stop there, so the first clause has to carry the reassurance, not the diagnosis.
另一个测试会失效的地方是时机。一条单独看完全清晰的消息,如果出现在错误的时刻——正在任务中间,没有任何提示表明用户之前的进度是否已保存——仍然可能造成很坏的效果。我学会不仅检查消息的措辞,还要检查它出现的时机,因为一条技术层面上写得很好的错误提示,如果出现在用户真正焦虑的时刻——我刚才的工作是不是丢了——无论语言多通俗,读起来都是冰冷的。那里的修复通常不是加更多文字,而是调整消息的引导顺序。先说安全的那部分,你的进度已保存,再说问题部分。人们在压力下看第一句就停了,所以第一句必须承载安慰,而不是诊断。
I also stopped trying to make error messages clever. Early on I wrote a few that tried to be a little funny, a little on-brand, softening a bad moment with personality. Almost none of them survived contact with a real frustrated user. A joke lands fine when nothing is actually wrong and falls flat, sometimes badly, when someone is stuck and just wants to finish what they were doing. Plain, direct, and quick to read beats charming every time a user is actually annoyed, and I would rather a message be forgettable than be remembered for the wrong reason.
我也停止了试图让错误提示显得巧妙。早年我写过一些试图带点幽默、有点品牌调性的,在糟糕的时刻用个性来缓和气氛。几乎没有一条在真正沮丧的用户面前存活下来。没出问题时笑话效果很好,但当有人卡住了、只想把事情做完时,笑话就变得干巴巴的,有时候还很糟糕。简单、直接、快速可读,在用户真正恼火的时候永远比讨巧更有效。我宁愿一条消息被人遗忘,也不愿它因为错误的原因被记住。
Error text used to be the last thing I wrote and the first thing I forgot about once a tool shipped. It is now a maintenance category of its own, checked the same way I check anything else across the five RAXXO tools, with a simple test behind every message: could a stranger read this and know what happened and what to do next. Most of the actual improvement came from cutting things, cutting fake technical explanations that helped nobody, cutting vague endings that left people stuck, cutting attempts at humor that only worked when nothing was actually wrong.
错误提示文字曾经是我最后写的东西,也是工具发布后最先忘记的东西。现在它成了自己的一个维护类别,和我检查其他所有东西一样检查这五款 RAXXO 工具,每条消息背后都有一道简单的测试:陌生人能否读懂并知道发生了什么以及接下来怎么办。大部分实际改进来自删除:删掉对谁都没帮助的虚假技术解释,删掉让人无路可走的模糊结尾,删掉只在什么都没真的出错时才有效的幽默尝试。
None of this required a redesign or a big rewrite pass. It required treating a repeated support question as a bug report pointed at a specific sentence, and being willing to open that file and fix the sentence the same day. A tool that fails clearly, and tells someone exactly what to try next, loses very little trust when it fails. A tool that fails and goes quiet, or worse, talks past the person trying to use it, loses more than the moment. That is the whole reason this is worth the small, recurring effort it takes.
这一切都不需要重新设计或大范围重写。它需要的是把一个重复出现的支持问题当作一份指向特定句子的 bug 报告,然后愿意打开那个文件、在同一天修复那句话。一个失败时清晰告知、告诉人确切下一步该怎么办的工具,在失败时几乎不会失去信任。一个失败时一声不吭、或者更糟、说一些跟试图用它的人擦肩而过的话的工具,损失的不仅仅是在那一刻。这就是这件事值得去做那小小的、持续不断的努力的全部原因。