无尘阁日记

无尘阁日记

让新手一键开始,让老手拆开重装
silcano

「用户永远是对的」,这句话我信了很多年,后来发现它最要命的地方是——它对,但它没用。

你听完这句,回去照做,做出来的东西大概率是夹生饭。

因为「用户永远是对的」说的是他的感受是对的,不是他的方案是对的。一个用户说「你这软件太复杂」,这句话百分之百正确;但他接下来开的药方——「把左边那三个菜单删了吧」——大概率是错的。你真删了,下周他就骂你阉割版。

所以这些年我慢慢把这句话拆成了三句更具体的:新手要的是「你替我做决定」;老手要的是「你少管我」;而连接这两句话的桥梁,叫默认值。

· · ·

先说第一个误区:很多人以为新手和老手是两拨人。

不是。是同一个人。

你回想第一次用 git。三条命令背下来就能活:add、commit、push。那时候你不关心 rebase 是什么,也不想关心。三年之后,你开始 git rebase -i HEAD~3 把一堆提交揉成一个,开始用 git reflog 从灾难里捞代码,开始 git worktree 同时在两个分支上改东西。

同一个你。中间没有换人。

如果 git 在启动的时候弹一个窗问你「您是新手还是老手?」,然后新手版把 rebase 藏起来——这个工具早就死了。因为没人愿意在自己的能力长出来之后,被一把锁挡在外面。

更麻烦的是,一个人在同一天里会在两个身份之间来回跳。我写业务 CRUD 的时候是彻底的新手,我希望一条命令把模型、迁移、控制器全给我生成好,别问我;我半夜排查线上内存泄漏的时候是彻底的老手,我希望任何一层你做的判断都能被我扒开看,包括你为什么默认开这个 GC 参数。

所以产品不是给「两类人」设计的,是给「同一个人的两副面孔」设计的。你不能选边站,你得同时伺候。

· · ·

第二个误区:「简洁」等于「功能少」。

这是我觉得最普遍、也最贵的一个误解。

简洁的度量单位不是按钮数量,是用户在一次任务里被迫做出的决定数量。

拿手机拍照说。你按下快门,它自动对焦、自动测光、自动白平衡、自动判断要不要开 HDR。整个任务里你只做了一个决定:按不按。至于专业模式、夜景、人像、RAW——功能一个没少,只是不在这条路上挡着你。

反面什么样?某些安卓机的相机,第一屏就摆着「AI 场景识别:开/关」「HDR:自动/开/关」「超广角畸变校正:开/关」。这些功能都有用,但你只是想拍张照,被迫做了四个决定。于是它不好用。

命令行里同样的对比:

composer create-project laravel/laravel blog —— 你做了一个决定(项目叫什么)。它背后替你敲定了框架版本、目录结构、依赖清单、autoload、.env 模板、甚至帮你跑了一次 key 生成。两百个决定被压缩成一个。这叫简洁。

ffmpeg -i in.mp4 ... —— 你得决定编码器、码率、像素格式、profile、GOP、封装格式、音频采样率、关键帧策略。它什么都能干,但它不替你决定任何事。于是全世界的人都在它外面套了一层图形界面。

ffmpeg 是「不限制用户」的极致,同时也是「默认值缺席」的极致。它先赢下了所有工程师,然后输给了所有普通人,最后连工程师自己也去装了个 GUI。

· · ·

第三个,也是最要命的一个:默认值不是「先随便填一个」,默认值是你的观点。

一个产品里有多少个默认值,就有多少次你替用户表态的机会。而这些表态,用户不会读,只会承受。

看几个代价昂贵的例子。

MySQL 早年默认字符集是 latin1,后来改成 utf8——三字节的那个假的 utf8。这不是技术细节,这是 MySQL 在表态:「我觉得你不需要 emoji 和生僻字。」这个表态错了,整个中文互联网替它还了十年债,直到 utf8mb4 普及。

PHP 的 magic_quotes_gpc 默认开启。它表态:「我猜你没做转义,我替你转。」听起来很体贴,结果是全世界的 PHP 代码里长出了 stripslashes() 这种反模式——用户得写代码去撤销你的默认值。这是默认值最坏的形态:不但没省事,还制造了一项永久性的认知税。

Docker 的 :latest 默认标签。它表态:「版本无所谓。」这是最近十年最贵的一个默认值,贵到官方后来自己都出来劝人别用。

这些例子的共同点是:默认值选错了,用户不是「改一下就行」,而是要在接下来几年里持续地绕开它、解释它、给新人培训它。

所以我判断一个默认值好不好,看三条。

一、它得是「大多数人的正确答案」,不是「最安全的小值」。 很多库默认把连接池设成 10、把超时设成 3 秒、把日志级别设成 WARN,理由是「保守」。这不是保守,这是偷懒——把决策成本推给了 90% 的正常用户,只为了保护那 10% 的边缘情况。nginx 默认 worker_connections 512 就是这个味道:它其实在说「我觉得你流量不大」。

二、它得能被一眼看见。 用户看到结果的时候,得知道有个默认值在起作用。Laravel 项目里那个 .env.example 干的就是这件事——它不是配置文件,它是一张「我替你决定了这些」的清单。

三、它得能被干净地覆盖,而且覆盖不留副作用。 这条最难。很多系统的默认值像胶水,你以为改掉了,其实某个下游模块还缓着旧值。

· · ·

那么老手到底要什么?

不是「更多按钮」,是「让我绕过你的判断」。

我特别喜欢 VSCode 的一个设计:设置界面做了一百多项可视化开关,但右上角永远留着一个 {} 图标,点开就是 settings.json,你能手写任何 UI 里没有暴露的配置。UI 是给新手的,JSON 是给老手的,两套入口指向同一个配置源,谁也不挡谁。

Laravel 的配置链是另一个漂亮样本。默认值写在 config/*.php 里(跟着版本走),覆盖写在 .env 里(跟着部署走),中间的桥是 env('KEY', 默认值)。新手完全不管 .env 也能跑起来;老手改 .env 能改任何东西;而且这些改动不会污染代码仓库。这就是「设好默认值」和「不限制用户」同时成立的活样本。

反面教材我也见过不少:某些后台管理系统把「高级设置」做成一个开关,打开之后多出二十个输入框,看起来很专业——结果每一个输入框背后都是一个硬编码的枚举,下拉框只有三个选项。用户想填个自定义值,填不了。

这不叫不限制用户,这叫假装不限制用户。它的成本比直接不给还高,因为它骗用户点开了。

· · ·

还有一种更隐蔽的偷懒,得单独拎出来骂一句:把所有东西全摊开。

早年的 Blender 打开就是一脸懵,早年裸奔的 vim 连行号都没有,Jira 的后台配置有一千多项,管理员自己都不敢动。它们的行为逻辑是一致的——「我懒得替你想,你自己想。」

这不是尊重用户,这是把设计工作外包给用户,还不给说明书。

真正的不限制是另一种东西:在默认值正确的这条路上,每往深处走一步,都有一个新的、依旧被认真设计过的默认值。

你从「一键发布」走到「自定义构建流程」,不该是从「有人管你」跳到「没人管你」,而该是从「我替你选好了」走到「我替你选了另一套,你也可以改」。

· · ·

落到能动手的地方,我给自己攒了三条。

第一,默认值要跟着场景走,不要全局一个。 同一个「自动保存」功能,在「新建文档」场景下默认开,在「批量导入」场景下必须默认关——因为后者的用户正在精确控制字节。全局统一的默认值,本质上是不承认场景存在。

第二,每往深处加一层,都要给一个「退回去」的按钮。 一个「恢复默认」比一百个选项有用。用户敢乱点的前提是知道自己能回来。

第三,配置要建成优先级链,别做成开关。 开关只有两个状态,链子有 N 个状态:内置默认 → 项目配置 → 用户配置 → 环境变量 → 命令行参数。这条链竖起来,新手和老手才能住在同一套系统里而互不干扰。开关竖不起来这个,开关只会把系统劈成两半。

· · ·

回到开头那句话。

「用户永远是对的」,它真正的含义是:用户的感受永远是对的。他觉得复杂,那就是复杂;他觉得不够用,那就是不够用;他觉得你这个默认值坑人,这个默认值就是坑人。这部分不容辩驳,也不该辩驳。

但用户的归因和方案经常是错的。他说「太复杂」,归因到「功能太多」,方案是「删功能」。真实原因很可能是「你没替我决定」。你按他的方案改,就是治错了病,还搭进去一个功能。

所以做产品——写库、写框架、写后台、写 CLI 都算——八成的功夫不在「加什么」和「减什么」上,而在「猜用户在这个场景下,愿意做几个决定」上。

新手要的是:你替我做决定,做完告诉我怎么改。

老手要的是:你先替我做决定,但我随时能掀桌子。

这两句话不矛盾。它们只是同一套系统的两层。