好的错误提示,是在对话框里说「对不起」
好的错误提示是稀罕物。
不是因为技术难——显示一段文字没什么技术含量。是因为大部分项目把错误提示当成了最后一行才处理的事:日志系统建好了、API 框架搭完了、单元测试写了,然后在 QA 阶段打开一个「data-theme」属性设成文案、随便写一句「Something went wrong. Please try again later.」就发布了。
然后这句话会在用户的屏幕上挂一个月、一年、直到有人提 issue。再然后被放到 backlog 的底端,优先级标为 P4。
但这不完全是开发者的错。错误提示是一个被系统性地低估的设计对象——它既不属于前端(设计稿上没标),也不属于后端(Sentry 日志里看的是 stack trace 不是文案),也不属于产品(KPI 不包含「用户对错误信息的满意度」)。它是三不管地带的孤儿。
「Dinosaur Game」
Chrome 的断网小恐龙大概是有史以来最著名的错误页面了。
Google 的数据说,全世界每天有三亿次断网错误页面被展示。三亿次「你离线了」——这是一个可以被骂的设计触点。Chrome 团队的选择是:什么都不放。一个像素风的小恐龙,一按键就跳。按空格跳、按上箭头也跳。可以跑无限跑酷。
这个设计在 2014 年上线,然后在之后的十年里成为无数人无聊时的消遣。它没有说「请检查网络连接」——虽然下面确实有一行小字写了。它给的是:你等网络恢复的这几十秒里,至少可以跳一跳。
我有次在高铁隧道里玩了 12 分钟。恐龙跑到了 8000 多分,然后我开始认真思考 Chrome 团队在 2014 年做这个决定时的内部讨论——大概率有人说过「一个游戏页面?这合适吗?」然后有人用数据说服了所有人。
错误提示的最高境界是让你忘了自己在等一个错误。
最差的那些
反面教材俯拾即是。
Windows 的蓝屏——那个白色文字在蓝色背景上堆砌十六进制地址的页面——本质上是在说:「你的电脑炸了,我给了你一堆你绝对看不懂的数据,然后我会在三秒后帮你重启,你最好记得保存过。」它提供了超量的信息,其中 99% 对用户无意义,剩下的 1% 没有引导。
Word 的「Microsoft Word 已停止工作。Windows 正在检查解决方案……」也是经典——它说它在检查,但大多数时候它不会告诉你检查结果。十分钟后同一个对话框重复出现。这不像在解决问题,像在刑讯。
还有最令人火大的一类:「出错了。错误代码:ERR_UNKNOWN。」这个错误代码本身就承认了——我不知道为什么错了。那你叫我记下来发给谁?
这些烂设计的共同点不是技术上的缺陷,而是设计上的逃避:不知道该给用户看什么,就给了个半成品。
把错误当对话
Stripe 的 API 错误响应在开发者社区里被当做教科书来引用不是没理由的:
{
"error": {
"type": "card_error",
"code": "card_declined",
"decline_code": "insufficient_funds",
"message": "Your card was declined due to insufficient funds."
}
}
它没有说「Something went wrong」。它告诉了你什么东西错了、为什么错了、具体到哪一步卡住的。如果你是一个开发者,看到 insufficient_funds 就明白下一步该做什么——换卡或加钱。非开发者看到「余额不足」也一目了然。
但 Stripe 更聪明的设计在更深的层次:他们给每个错误码配了推荐的下一步操作。card_declined 的文档页上写着「建议重试时使用不同的支付方式,或让用户联系发卡行」。
这就是把错误当对话而不是当故障来处理的态度。
GitHub 的 404 页面也是一个例子。它不会丢给你一个空白页——它会说「抱歉,我们找不到这个页面」,然后给你三个链接:回到仓库、搜索、或者创建一个新的文件。用户从「卡住了」到「知道下一步做什么」中间只有一行字的距离。
当公司把错误页面做成了品牌
有些错误页面已经成了品牌资产的一部分。
Mailchimp 的 500 错误是一只流血的猴子。GitHub 的 404 是「这不是你要找的页面(但也许你想看看这个)」加上一张宇宙图片。Twitter 的「鲸鱼在飞」是早期服务的标志性自嘲。
但这些不是教条——品牌化的错误页面只有在你的服务真的稳定到用户很少看到它时才有意义。如果你的 API 每三天挂一次,然后挂的时候给用户看一只可爱的兔子,用户不会觉得「好有趣」,用户会觉得你在用可爱转移注意力。
品牌化错误页面的隐性前提是:用户看到它的次数足够少,以至于每次看到都觉得新鲜而不是愤怒。
个人观察:一个深夜的错误提示
这其实是我写这篇东西的直接原因。
凌晨三点,我在服务器上跑了一个不太稳的脚本——一个会吃掉所有可用内存然后被 OOM killer 杀掉的那种。我盯着终端看了一会儿,然后它吐出了一行文字:
Killed
就一个词。没有 session ID,没有 exit code,没有建议。一个词的墓志铭。
我盯着 Killed 看了几秒。我不生气——我知道是我自己把内存配额写得太激进了。但那一刻我在想:如果 Killed 后面跟了一句「Try increasing vm.overcommit_memory or reduce the batch size」,我可能已经在改配置了。
系统提示我错了,但它没有告诉我哪里可以调整。就像一个拒绝你进门的保安,然后把门关上了,一句话不多说。
# 我后来自己查到的:OOM killer 的触发记录
dmesg | grep -i "oom"
# [66772.483072] oom-kill: const=524288, total-vm=4105072, anon-rss=3510032, file-rss=0, shmem-rss=0
# [66772.483075] Memory cgroup out of memory: Killed process 2134921 (python3)
这些信息系统都有。它只是没决定告诉你。
好的错误提示长什么样
如果把所有见过的好的错误提示放在一起,它们有一个共同的模式:错误提示不是为了证明系统出了错,而是为了让用户从错误中走出来。
这个听起来像废话,但绝大部分错误提示是按「证明系统出了错」设计的。它们告诉你状态码、时间戳、错误类型——这些信息对开发者调试有用,对用户而言只是噪声。
一个好的错误提示,按优先级,应该回答三个问题:
- 什么东西出了错? —— 「信用卡被拒」
- 为什么? —— 「因为余额不足」
- 现在怎么办? —— 「换一张卡,或者联系发卡行」
大部分错误提示只回答了第一个问题。好一点的回答了前两个。能回答第三个的凤毛麟角。
而最讽刺的是:回答第三个问题往往是最简单的。不需要查数据库、不需要重试请求、不需要解析 stack trace——只需要一段提前写好的文案。但它需要有人在写代码之前就想好:用户看到错误的时候,他需要什么。
那个「什么」才是设计。
设计的态度
写到最后,我意识到错误提示其实是一个项目对用户态度的折射。
重视用户的团队,会在 sprint 计划里给 error state 留一个 story point。不重视的,会让所有错误都落进同一个 catch (e) { showErrorToast('Something went wrong') } 里。
Chrome 的小恐龙不是一天做出来的。它背后是有人意识到「三亿次断网展示 × 三十秒等待时间 = 一个被严重低估的设计机会」,然后花了一个迭代做出来的。
而那个凌晨三点吐出一个词就沉默的终端——它也确实只是一个 OOM killer,不需要有人性。但那些内置在系统深处的、沉默的拒绝,和人造的、温暖的提示,中间隔着的东西,叫做设计。
不是 UI 设计。是关系的设计。
评论(0)
暂无评论,来写第一条吧~