# 引导用户使用你的产品

LLMS 索引： [llms.txt](/llms.txt)

---

<a id="ch02_naming"></a>

> 机器及其设计者有义务理解人。理解机器那些武断且毫无意义的指令，并不是我们的义务。
>
> — Don Norman，见《设计心理学》

你的产品用户就是你的英雄。就像在小说里一样，你希望这位英雄走到终点并达成目标。

与小说作者不同，产品设计师的目标是让用户旅程尽可能轻松。在场景的指导下，你会在恰当的位置与恰当的时机提供<a id="id352"></a>路标，帮助用户沿路前进。

在<a id="id353"></a><a id="id354"></a>经典著作《设计心理学》中，Don Norman 将这种路标称为*意符*（signifiers）。意符<a id="id355"></a>是界面中提示某个功能作用的线索。意符的例子<a id="id356"></a><a id="id357"></a>不胜枚举：

- 用名称或<a id="id358"></a>图标来指示用途
- 按钮外的边框，提示你可以点击
- 向下的尖角符号，提示这是下拉菜单
- 单选按钮，表示最多只能选择一个选项
- 一组复选框，表示可以同时选择多个选项
- 带下划线的蓝色超链接，表明目标是网页，链接文字则说明该页面的相关性或名称
- 错误消息，提示用户接下来该做什么

等等。

本章将探讨意符这一概念及其在用户旅程中的作用。通过它们，你将学会如何打磨出更精致的界面。

首先，我会介绍一个关于分层菜单的案例研究，里面有很多可以深入讨论意符的机会。

这会是我们观察本章建议的两个视角之一。第二个视角会以如下侧栏形式出现：

### 编码实践 {#id359}

在<a id="id360"></a>这些模块里，你会看到如何把产品思维应用到日常编码中的建议。毕竟，函数、类等构造本身就是微型产品，而它们的用户正是你的队友和协作者。好的命名和注释会让代码评审与维护高效得多，同时也能帮助你练习产品思维能力。

通过这两个视角，我将审视用户旅程的三个主要阶段：发现、理解与使用。

过程中，我还会展开一些主题，例如本体（ontology）、层级化设计，以及一致性与特异性之间的权衡。

最后，我会拉远视角，整体审视用户旅程，讨论当不同阶段发生冲突时该如何权衡，以及何时需要从更宏观的层面思考设计。

### 案例研究导入 {#id21}

在<a id="CSuj02"></a> 2000 年代中期，微软<a id="id361"></a>在 Office 套件（Word、Excel、PowerPoint）上遇到巨大问题：系统过于复杂，用户难以理解并定位自己需要的功能。到 2003 年，Microsoft Word 除了下拉菜单外，还有 31 个工具栏和 19 个任务窗格。根据产品经理 Jensen Harris 的相关演讲，[图 2-1](/ch2/#office_2003) 展示了典型会话截图。

通过对数百名用户的访谈，团队发现用户很难发现功能，也难以建立对软件的掌控感。许多呼声很高的功能虽然被做出来了，但用户找不到它们。

到了 Office 2007，微软将<a id="id362"></a><a id="id363"></a>“Ribbon（功能区）”菜单系统引入其 Office 桌面应用，如 [图 2-2](/ch2/#ribbon_overview) 所示；同时还有<a id="id364"></a><a id="id365"></a>在选中文本时弹出的上下文菜单，如 [图 2-3](/ch2/#context_popup) 所示。

**图 2-1.** 2003 版 Microsoft Word 的凌乱截图

![2003 版 Microsoft Word 的凌乱截图](/fig/tpme_0201.png)

**图 2-2.** 2025 年 Windows 版 Microsoft Word 的功能区

![2025 年 Windows 版 Microsoft Word 的功能区](/fig/tpme_0202.png)

**图 2-3.** 2025 年 Windows 版 Microsoft Word 的上下文弹窗

![2025 年 Windows 版 Microsoft Word 的上下文弹窗](/fig/tpme_0203.png)

在<a id="id366"></a><a id="id367"></a>设计这次改版时，团队收集了数千项功能的使用频率，并将它们重组为新的层级化菜单设计，同时引入了新的功能呈现方式。这些变化让高度复杂的应用对许多用户更容易上手，并且迄今经受住了时间考验。理解其“怎么做、为什么做”，能为我们在自己的应用中引导用户旅程提供线索。

### 用户旅程中的场景 {#id187}

用户旅程<a id="id368"></a><a id="id369"></a>通常会经过三类主要场景：发现（Discovery）、理解（Understanding）和使用（Usage）。

- **发现**：用户有一个想解决的问题，但还不知道怎么解决。
- **理解**：用户遇到一个功能，想弄清它做什么、怎么工作。
- **使用**：用户希望按预期目的使用该功能，同时避免以不安全方式使用。

如果<a id="id370"></a>你负责交付某项功能，就必须对这三个方面都负责。让功能可见、帮助用户理解、引导用户安全使用，都是你团队的责任。漏掉其中任何一个，你的功能都无法产生应有影响。

如 [第 1 章](/ch1/#ch01_foundations_prod) 所述，你应该能讲出一个可信的故事，让用户贯穿三个阶段且没有情节漏洞。确保覆盖这三类主要场景，是消除漏洞的好方法。并且在针对每个场景时，还要继续补全更多覆盖细节的故事。

### 发现场景 {#id188}

读者将如何<a id="Sdiscov02"></a><a id="id371"></a><a id="UJdiscov02"></a>发现你的抽象？工程师常忽略用户旅程这一段，但它往往最关键：不知道产品存在的人，根本无法使用它。这就是线上广告会形成巨大产业的原因之一：它解决的是发现问题。

考虑发现时，要记住用户可能从不同方向进入。以 Microsoft Word 的上标功能为例，用户可能通过阅读功能区按钮、使用搜索栏、查阅在线文档、查看快捷键列表，或与聊天机器人交互来得知它。当你认为用户会需要你的功能时，他们所有发现路径都应能导向它，所以请尽可能多做心智模拟：用户会怎么接近它？哪里有情节漏洞或不合理的逻辑跳跃？

这一节里，我会先讨论用户带来的既有知识，再探讨帮助他们找到功能的工具，最后说明如何把功能做成层级结构，避免一上来就压垮用户，或者把东西藏得找不到。为引导你理解，我会介绍一种实用设计方法：产品发现地图（Product Discovery Mapping），它也能把这些讨论可视化。

#### 产品发现地图 {#id22}

产品发现图（Product Discovery Map, PDM）<a id="pdm02"></a><a id="pdismap02"></a><a id="DSpdm02"></a>可以帮助你绘制用户学习产品时会走过的路径。假设你在复查产品实现，想确认用户能找到在 Word 中制作简报所需的关键排版功能。这类用户会想做多栏布局、使用横向页面、在正文中嵌入图片，而且很可能会把这些技巧组合使用。

我们聚焦“连续分节符（Continuous Section Break）”的发现过程，这个概念你很确定用户一开始并不理解。[图 2-4](/ch2/#layout_menu) 展示了 Microsoft Word 的“布局”菜单，并展开了“分隔符”下拉菜单。

**图 2-4.** Microsoft Word 的布局菜单

![Microsoft Word 的布局菜单](/fig/tpme_0204.png)

下面是 Yves 的一个用户场景。Yves 所代表的是一类轻度使用者人物画像：偶尔用 Office 做社区活动宣传的人。

> Yves 正在为本地教会做月度简报。他想做成双栏排版，同时让简报标题居中显示在顶部。他先输入标题并在“开始”选项卡中把它居中。然后呢？他看到了“布局”选项卡，并注意到一个“分栏”控件。他点击并选择两栏，但标题跑到了左栏，这不是他想要的。他撤销后看到了醒目的“分隔符”按钮，并根据下拉菜单中的说明，学会了如何使用连续分节符。

当你要交付很多功能，或要测试大型产品可用性时，这类场景很快会变得冗长。产品发现图是一种简写表达，包含三个关键要素：

- 每张图都针对一个特定客户画像，表示其如何在产品中“走迷宫”。
- 每个节点代表产品中的一个元素。
- 每条边表示该画像如何发现相关知识，如 [图 2-5](/ch2/#layout_scenario_pdm) 所示的 Yves 旅程。

**图 2-5.** 连续分节符的 PDM

![连续分节符的 PDM](/fig/tpme_0205.png)

这张图假设 Yves 知道什么是“分栏”，但不知道什么是“分节符”；同时它展示了他成功走到正确功能的一条路径。

你可以把多条流程放进同一张图里，更快速、更直观地构思产品的某个部分。

构建和评审地图时最重要的是思考与协作过程。命名是否足够有帮助？你希望用户完成的认知跳跃是否合理？到达某功能所需步数，是否和它的使用频率匹配？常一起使用的功能是否也被<a id="id372"></a><a id="id373"></a><a id="id374"></a>放在一起？

#### 利用用户已有知识 {#id23}

在 [第 1 章](/ch1/#ch01_foundations_prod) 中，我<a id="id375"></a><a id="id376"></a>说过，人物画像有其“手段”——他们带来了哪些技能与知识？

你的产品呈现的一组名称与概念，以及这些概念之间的关系，称为<a id="id377"></a>*本体*（ontology）。本体本质上是一张图。用户对产品本体越熟悉，理解越深入，使用也越有效。

让东西更容易被发现的最简单方式，就是使用用户本体里已经有的名称和概念。也就是，不要重复造轮子。

在“布局”菜单这个例子里，Yves 知道“分栏（Column）”，但面对“连续分节符（Continuous Section Break）”时，他只带着“分节（section）”这个概念进入。观察 [图 2-4](/ch2/#layout_menu) 就能看到，微软在“分隔符”上的设计要比“分栏”谨慎得多：他们必须提供更详细的说明和图示，来弥补命名上的陌生感。

##### 编码实践 {#id378}

要警惕<a id="id379"></a>晦涩的项目代号。它们不在用户本体里。比如别人要在公司代码库或文档里找一个分布式缓存框架，可能会搜“cache”，但他们不会提前知道你的项目叫“Phoenix”（因为它从上一个分布式缓存的灰烬中“重生”）。如果你确实想做品牌化命名，可以考虑像“SuperCache”这种方案：既有一点品牌感，也保留一点可发现性。

晦涩代号在某些场景也有价值，例如保密需求，或对象足够独特、很难直白描述，导致直接命名反而误导。但要清楚你牺牲了多少可发现性，并确保留下其他<a id="id380"></a>面包屑（例如注释）让人能找到它。

有时，本体还取决于<a id="id381"></a>人物画像。

我在 Stripe 工作时，我父亲（一位会计，几乎不会编程）读了 Stripe 在线支付和账单 API 的文档。他抱怨我，因为他一直在找“应收款（receivables）”之类严谨的会计术语。

我告诉他，这其实是有意为之。这些 API 面向的是通用型程序员，努力使用那些即便没上过会计课的人也能接受的术语。

但这也说明：如果 Stripe 希望会计人群也能顺畅使用，仍有不少工作要做。

#### 提供多条发现路径 {#id24}

当然，语言里<a id="DSmult02"></a><a id="id382"></a><a id="id383"></a>有同义词和相关概念，但你的本体里通常只能选一个主叫法。你需要给那些带着不同猜测进入的用户提供通向它的路径。<a id="id384"></a>

举个<a id="id385"></a><a id="id386"></a>例子，功能区提供了好用的命令搜索框，并支持一定的模糊匹配。如果我在框里输入“Margin”，它会返回含有“Indent”的命令，如 [图 2-6](/ch2/#indent_margin) 所示。我甚至可以直接在该菜单中操作这些命令。

**图 2-6.** 在 Word 中搜索 margin

![在 Word 中搜索 \'margin\'](/fig/tpme_0206.png)

##### 编码实践 {#id387}

通过<a id="id388"></a>注释为你的抽象增加更多发现路径。比如你的基类叫“Plugin”，可以在注释里用“Extension”这样的同义词来描述它。

这个原则可以推广为：

> [!TIP]
> 给用户提供多种发现某个功能的方式，以适配他们不同的“手段”。

以 Word 这类复杂应用为例。大改版之前，发现体验一团糟。用户得在海量菜单、工具栏、任务窗格中判断哪一个适合当前任务。若他们需要工具栏，只有<a id="id389"></a>图标给提示；若看菜单，则要在长长的词语列表里寻找，而且排序几乎没有规律可循。功能分类看起来也很随意：为什么某些功能在 *Tools* 菜单而不是 *Format* 菜单？为什么 *Find* 在 *Edit* 菜单里，即便它本质是读取操作？

一个平庸的 Help 组件，是这团乱麻之外几乎唯一的替代路径。

而自从引入<a id="id390"></a><a id="id391"></a>功能区后，菜单的意符明显更好了。

- 命令被分组到更少的选项卡中，并分配了更多屏幕空间，让 Yves 更容易在“布局”选项卡里扫到与其用例相关的内容。
- 作为常用且推荐命令，“分隔符（Break）”被放大并放在相关命令附近。
- “Break”和“Continuous Section Break”同时用图标与文本表达。不同用户可能更容易通过其中一种形式找到它。
- 提供搜索框；如果 Yves 知道“section”这个词却不知道在哪个菜单，这就很有帮助。它修复了传统菜单的一个根本问题。

[图 2-7](/ch2/#bushy_layout_pdm) 展示了 Yves 可能发现连续分节符的多种有效路径。

**图 2-7.** 发现连续分节符的多条路径

![发现连续分节符的多条路径](/fig/tpme_0207.png)

以下是用户发现你产品的几条<a id="id392"></a>常见路径：

- 搜索
- 询问聊天机器人
- 视觉扫描你的应用，或边点边看，寻找名称和其他意符
- 使用屏幕阅读器（面向视障人群）或收听语音菜单
- 查阅操作指南或其他文档

尽量不要只依赖文档。在消费级应用里，你很可能根本不会依赖它。<a id="id393"></a>

#### 优雅揭示复杂性 {#id25}

几乎<a id="DSgrace02"></a><a id="Crev02"></a>所有成功应用都会不断增加功能。即便是以简洁搜索框著称的 Google，如今也有许多配套搜索结果的选项卡和工具。

为了驾驭这种功能扩张，设计师会向用户*优雅地揭示复杂性*。

看看功能区。即使已经改进，它仍有太多命令和目标画像，难以让菜单始终易用，因此微软引入了<a id="id394"></a><a id="id395"></a>“上下文菜单”的概念。它只在合适时机出现。[图 2-8](/ch2/#shape_context) 展示了“形状格式”选项卡：除非你点击了形状，否则它不会挤占界面空间。

**图 2-8.** 功能区中的上下文菜单

![功能区中的上下文菜单](/fig/tpme_0208.png)

功能区默认有九个选项卡，已经接近令人不堪重负，这在产品发现图里很容易看出来。通过把“形状格式”节点下移到图的更深层级（见 [图 2-9](/ch2/#shape_format_pdm)），这种优雅揭示避免了问题恶化。

Word 还通过上下文菜单实现优雅揭示：当你高亮文本时，鼠标旁会弹出包含加粗、斜体等常用命令的菜单。这类菜单会主动出现，可发现性很强；同时选项很少，用户也更容易快速扫描到所需工具。

**图 2-9.** 用于发现“形状格式”选项卡的 PDM

![用于发现“形状格式”选项卡的 PDM](/fig/tpme_0209.png)

#### 多人物画像设计 {#id26}

应用变复杂的一个<a id="id396"></a><a id="id397"></a><a id="id398"></a><a id="id399"></a>主要原因是：随着增长，它会吸引不同人物画像的人出于不同目的来使用。

Microsoft Word 服务的人群非常广，从需要章节导航的小说作者，到需要图表的营销人员，再到需要屏幕阅读器与语音输入的视障用户。

因此，大多数成功应用至少会有两套不同界面。理想情况下，每套界面都应在用户需要时被优雅揭示出来。

大多数应用都有<a id="id400"></a>重度用户，通常占受众的 5%-20%，他们活跃且深度投入。这些用户希望拥有更强大的原语来定制体验。原因往往在于，他们有更强激励去使用并深入掌握你的产品。一个靠 Word 工作谋生的重度用户，比一个写高中英语诗歌作业的学生，更可能用宏和文档修订追踪。

环顾四周，你会发现这种<a id="id401"></a>“双界面、双画像”模式无处不在：

- Wikipedia 页面有一个不显眼的小“编辑”按钮，读者可忽略，但对贡献者很有用。
- 网站面向新手给出极其醒目的“创建账号”，而对老用户只放一个小小的“登录”按钮。
- 现代操作系统既有面向大众的图形界面，也有面向 IT 专业人员的命令行外壳。
- TypeScript 这类渐进类型语言中，类型标注对简单脚本可选，但在大型组织里可被要求用于构建更健壮的代码库。
- 无代码或低代码框架对非程序员友好，同时允许程序员编写自定义代码。
- 面向轻度用户提供按钮点击，同时给习惯型重度用户提供键盘快捷键。

作为设计师，一个非常重要且令人解放的认知是：当你意识到产品已到应提供多界面的阶段，就可以开始为各人物画像分别优化。这会打开更大的设计空间，也给你更多解决问题的机会。

#### 把“未知的未知”变成“已知的未知” {#id27}

到目前为止，<a id="id402"></a><a id="id403"></a><a id="id404"></a><a id="id405"></a><a id="id406"></a><a id="id407"></a>我大多假设用户知道你的应用具备某些功能，只是多久能找到它的问题。但如果他们压根不知道这个功能存在呢？这在新颖或创新功能中很常见，也常见于对领域本体理解尚浅的新用户。某个用于“神奇记账”的 Excel 函数，资深会计都熟知，但会计专业本科生可能闻所未闻。

所以，把你的产品想象成一片神秘森林，用户正在其中穿行。请给他们留下一条<a id="id408"></a>面包屑轨迹，帮助导航。

举个例子，最近我在多个应用里都看到小型聊天机器人控件弹出。Microsoft Word 在我的光标旁有一个“Draft with Copilot”上下文菜单<a id="id409"></a><a id="id410"></a>。此前我并不知道 Word 有这个能力。我也不完全确定这个聊天机器人能为我做什么，但至少我可以去探索。一个“未知的未知”就这样变成了“已知的未知”。

你还要认真思考菜单里省略了什么。用户可能并不知道缺失命令可以在别处找到。Word 在“布局”选项卡里展示了全部 Break 类型，但在“插入”选项卡只放了“分页符（Page Break）”，如 [图 2-10](/ch2/#page_break_alone) 所示。这样设计的风险是：没看到“布局”选项卡的用户可能误以为分页符是唯一选项。权衡点在于，相比埋在更模糊的“Break”命令下，单独露出的“Page Break”确实更易被发现。<a id="id411"></a><a id="id412"></a>

**图 2-10.** “插入”选项卡中的分页符命令

![“插入”选项卡中的分页符命令](/fig/tpme_0210.png)

### 理解场景 {#id189}

当用户<a id="Sunder02"></a><a id="UFunder02"></a>发现了所需功能后，下一步必须更深入地理解它到底做什么。

我先从最常见、也是促进用户吸收关键概念的方式讲起：起好名字。随后我会介绍其他意符，当名称不够，或你想兼顾不同学习风格时，它们会很有用。

#### 选择易懂的名称 {#id28}

名称<a id="id413"></a><a id="id414"></a>是你向读者脑海植入一两个关键想法的机会。好名字除了提升可发现性，也应促进理解。用户不一定想知道“这个功能是什么”，他们更想知道“它如何帮到我？”这两个问题很多时候答案一致，但并不总是。具备产品思维，意味着你要思考自己对读者产生的影响，而不只是描述系统本身。

命名最重要的规则之一也许是：避免“知识诅咒（Curse of Knowledge）”。

> [!NOTE]
> <a id="id415"></a>知识诅咒是一种<a id="id416"></a><a id="id417"></a>认知偏差，会让系统设计者难以预判理解较少的用户在与系统交互时会遇到哪些困难。

“换鞋思考（shoe-shifting）”<a id="id418"></a>是避免这一诅咒的最佳方式。考虑目标画像已有的本体；更好的做法是，直接找这个人群中的人，问他们会如何解读你的命名。

#### 重新审视经典命名建议 {#id29}

大多数人<a id="NNclassic02"></a><a id="USclassic02"></a>都听过命名建议——那些适合印在项目符号列表里、或者贴在笔记本上的短句。我想用更以用户为中心的<a id="id419"></a><a id="id420"></a>视角，重写其中几条。

##### 避免歧义（而不是“要能自解释”） {#id30}

如果<a id="id421"></a>你只能在便签上写下一条命名箴言贴在显示器边上，那就写“避免歧义”。

在没被知识诅咒“感染”的读者看来，功能命名含糊不清的情况其实非常常见。要防止这一点，就做一次换鞋思考，进入用户视角，头脑风暴你的命名可能被如何误解。（或者，再说一遍，直接去问他们。）

当我写“布局”案例并扮演做教会简报的 Yves 时，就被一个歧义惹恼了。我看到“布局”选项卡里有这个 Align 按钮，如 [图 2-11](/ch2/#layout_menu_align) 右侧所示。

**图 2-11.** Word“布局”选项卡的一部分

![Word“布局”选项卡的一部分](/fig/tpme_0211.png)

你猜它是做什么的？

我本以为它能用来让简报标题居中。然而我展开它后（如 [图 2-12](/ch2/#align_ambiguity) 所示），所有看起来有用的选项都灰掉了。我试了几种办法让它生效，但都没成功。

**图 2-12.** 一个令人困惑的下拉菜单

![一个令人困惑的下拉菜单](/fig/tpme_0212.png)

如果 *Align* 指的并不是通用文本、图片或图表的对齐，而是更具体的东西，那它就应该明确写出来！

###### 编码实践 {#id422}

以下是代码库里经常需要澄清的一些<a id="id423"></a>歧义：

- **角色与关系**：当存在两个职责不同的对象（如 parent/child、sender/recipient）时，澄清“到底是哪一个？”至关重要。
- **变量的类型**：这能在每一行使用该变量的代码上说明“它是什么”。
- **度量单位**：你不会希望读者把磅当牛顿，或把毫希沃特当希沃特，最后导致火星探测器事故或辐射灼伤。
- **词性**：`persist` 是一个表示“是否写盘”的布尔值，还是一个真正执行写盘的函数？如果是前者，`should_persist` 会更好。

##### 在一致性与特异性之间权衡（而不是“保持一致”） {#id31}

一致的<a id="id424"></a><a id="id425"></a>命名有助于用户：他们在本体里学会一个概念后，就能反复复用。给新功能命名时，务必先在现有产品里找“前例”来对齐。

但一致性经常要和特异性做权衡，而特异性往往是减少歧义的最佳手段。在前面的例子里，“Align”术语非常一致，却不够具体。

更具体有时也更符合习惯表达。比如你在音乐流媒体应用中设计一个“随播放显示歌词”的功能，即使产品里一贯用“Track”表示音频文件，你可能仍更愿意写“Song Lyrics”，而不是生硬的“Track Lyrics”。

另一个常见困境是：某个相关概念在很久以前命名得很差，你现在要决定是沿用它保持一致，还是回到第一性原理选一个正确的新名字。

对一些工程师而言，一致性可能变成教条，因为他们无需考虑用户视角也能围绕一致性展开推理。你团队里的技术负责人可能并未聚焦你的功能，没有思考当前场景或共情用户，但始终能对“一致性”发表意见。

对抗这种偏差的一种方法是：同时头脑风暴一个“最易懂、最具体”的命名，以及一个“与现有产品最一致”的命名。如果最终得到两个不同答案，再讨论权衡。这样能确保你既在共情用户，也在用现有本体检验命名。衡量时请继续做换鞋思考，贯穿思考可发现性、可理解性与可用性。下面是一些启发式规则：

- 没有充分理由时，优先保持一致。
- 记住你的知识诅咒。作为系统设计者，你通常比典型用户掌握更全面的信息。只使用产品子集的新用户，可能根本看不到足够内容，自然也不太在意一致性。
- 问清不同受众规模：也许旧命名只被少数重度用户使用，或者只是遗留功能；而你这个新产品有更广泛目标。为了做对未来，牺牲一点一致性也许值得。
- 在多画像应用中，更重要的是每个画像各自体验到什么。比如 Microsoft Excel 完全可以给同一公式提供两个名称，一个面向会计，一个面向软件工程师。又如你的应用同时支持 iOS 和 Android，平台内一致性很重要，但跨平台一致性优先级较低，因为用户并不常在两个平台间切换。

##### 只在目标画像普遍使用时才用缩写（而不是“避免缩写”） {#id32}

缩写词<a id="id426"></a>对理解场景很糟，因为没人会凭空猜出字母含义；它们对发现也不友好。

但你的目标画像可能已经习惯某些术语。你显然不该把“HTML”到处拼成全称。“CPU”更适合软件工程师受众；若面向更大众的人群，试试“处理器（processor）”这样的词更合适。

如果不确定，就调研目标用户，看看他们如何解读你提出的术语。

##### 只传达用户需要知道的内容（而不是“简洁”） {#id33}

严格说，简洁（concise）<a id="id427"></a>意为“简短但完整”。这听起来有道理。用户学习时会被词藻堆砌搞得吃力，但也不希望你漏掉关键细节。

但“完整”是对什么完整？我会改写这条建议，把重点更多放在读者和“什么能帮到他们”上。

我曾玩过一个线上游戏“PHP Diplomacy”——它改编自一款经典桌游，名字取自该移植版本所用的编程语言。非程序员很可能看不懂这个名字。回头想想，我当时一起玩的好像也全是软件工程师。

通过换鞋思考，你能识别哪些只是实现细节。名称可承载的信息位有限，每删掉一个无关信息，就多出一个位置传达真正有意义的内容。

假设你在构建一个长时任务 API：用户会把请求入队，系统从队列取出并执行。用户可以阻塞等待结果，也可以稍后拿句柄去查询。

把异步请求 API 命名为 `enqueue_request` 看起来很自然。乍看不错，但那“既入队又等待结果”的函数该叫什么？`enqueue_and_wait_for_request`？当你觉得这个名字别扭时，就该问：读者为了正确使用它，究竟需要知道名字里的哪些信息？

最相关的事实是它是异步的——响应可能延迟。“enqueue”这个词比需要表达的更具体。

这看似细枝末节，但当你去掉“queue”这个实现细节后，就能得到 `start_request` 与 `execute_request` 这样简洁又传神的命名，分别对应异步版和<a id="id428"></a><a id="id429"></a>同步版。简洁，也更易发现。

###### 编码实践 {#id430}

如果<a id="id431"></a>你想把非常短促的函数名和变量名提交进代码库，请记住 Python 之父 Guido van Rossum 的那句话：“Code is read much more often than it’s written.”（代码被阅读的次数远高于被编写的次数。）

函数名被“阅读并理解”的场景——代码评审、调试、学习代码库、搜索修改点——远比它被“敲出来”的场景——加新功能、写测试——要多得多。而输入阶段还有自动补全帮你。

在代码库生命周期里，读者会不断出现。如果你在构建可能长期存在价值的系统，请用清晰命名对它负责。

#### 提供冗余解释 {#id34}

选一个既便于发现、又能帮助无歧义理解的名字非常难，有时甚至不可能完美传达用户所需的一切信息。<a id="USredun02"></a><a id="redun02"></a>因此，值得用冗余机制来传达你的意图。

看看 Word<a id="id432"></a><a id="id433"></a> 功能区，它有效利用了<a id="id434"></a>图标来表达功能。在 [图 2-13](/ch2/#home_formatting) 中，你可以看到“开始”选项卡里的这些格式命令；它们经过精心设计，在帮助理解方面优于“multilevel list”“justify”这类纯文字标签。

**图 2-13.** 格式命令图标

![格式命令图标](/fig/tpme_0213.png)

下面是一些不只依赖命名、同样能提升理解的方法：

- **图文结合**：图像<a id="id435"></a>可以把文字试图传达的意思可视化，前面的分节符描述就是例子（[图 2-4](/ch2/#layout_menu)）。也别忘了，并非所有用户都在使用其母语版本产品。对英语母语者来说，“Column”也许很基础，但对其他用户未必属于<a id="id436"></a>常用词汇。
- **WYSIWYG（所见即所得）**：用户<a id="id437"></a>甚至不需要先理解，因为操作效果会立刻显示出来。Windows 版 Microsoft Office 除了是 WYSIWYG 编辑器外，还提供“实时预览（Live previews）”，可直接看到切换样式对当前文档的影响。
- **换个说法**：在<a id="id438"></a>工具提示和文档里使用同义表达或改写。用不同措辞复述同一概念，能帮助用户交叉定位其本意并建立信心。
- **始终提醒上下文**：人的<a id="id439"></a>短时记忆容量有限，容易忘记自己在哪。比如用户正处于电商购买流程中，若他们中途离开再回来，每个页面都应提醒“当前正在购买什么”。

##### 编码实践 {#id440}

冗余<a id="id441"></a>在代码、文档和其他信息密集场景中都很有价值。一旦你从用户场景和“人类工作记忆有限”出发，这一点就非常清楚。

例如，看下面这段代码，你发现什么了吗？

```python
def email_lunch_invitation(sending_user, recipient_user, message):
  recipient_organization = recipient_user.get_organization()
  title = f"{sending_user.name} from {recipient_organization} " \
    "has sent you an invitation to lunch!"
  send_email(
    sending_user.email_address, recipient_user.email_address, title, message)
```

你在计算 `title` 时看出 bug 了吗？如果我用的是 `organization` 而不是 `recipient_organization`，你觉得你还能这么容易看出来吗？

下面这些场景里，一点额外冗余会很有帮助：

- 读者在做代码评审，抽样检查质量。
- 函数作者可能像上面那样误用变量。
- 程序员通过断点、引用跳转、查找所有引用、调试调用栈或 lint 规则来到这一行，还没读完整个函数。

如果我们需要知道的关于某行代码的信息都<a id="id442"></a><a id="id443"></a>写在这一行上，生活就会轻松很多。

### 使用场景 {#id35}

当读者旅程进入尾声，<a id="id444"></a><a id="id445"></a><a id="id446"></a><a id="id447"></a><a id="id448"></a>开始动手操作时，危险就更大了。安全问题无处不在。我们会在 [第 8 章](/ch8/#ch08_interaction_design) 引入“可供性”概念时更系统地讨论用户安全；先在这里浅尝一下：看看意符如何在缺乏其他保护措施时，仍能引导人做对的事。好的命名能把用户“导上轨道”，让他们凭直觉做出正确操作，甚至意识不到自己本可能偏离。

请认真推演使用场景，并提醒用户可能的<a id="id449"></a>安全风险。

这里有个极端例子。2010 年代初，某家互联网公司内部有一个特殊配置变量，会被复制到所有提供 Web 流量的机器上。这个变量里放的是一个正则表达式，会在每个对外 HTML 页面发送到用户浏览器前，执行一次查找/替换！

如果要紧急打补丁，比如移除一个有问题的 HTML 标签，工程师就可以把正则写进这个变量，瞬间发布到整个平台。

这听起来危险吗？确实非常危险，而且本意只用于紧急情况。若你用正则改坏了 HTML 导致解析错误，整个站点都可能瘫痪。因此它被命名为 `TAKE_DOWN_THE_SITE`，名字不是描述“它做什么”，而是强调“误用会发生什么”。

尽管极端，这个命名体现出的<a id="id450"></a><a id="id451"></a>安全意识还是让我不情愿地生出几分敬意。

一个更常见的危险信号做法，是把内容塞进“高级”菜单。这既拉长了发现路径、减少会找到它的用户数量，也在暗示使用它可能带来意料之外的影响。Microsoft Office 就是这样做的：用户必须先显式启用，功能区才显示“开发工具”选项卡，把宏和 VB 脚本等工具限制在更可能受益、也更不容易把设置搞乱的人群中。

要判断是否需要显式标注危险，请构造用户“走错路”的模拟，并观察怎样的命名能让这些错误故事变得不再合理。

与“隐藏危险功能”相反的做法，是主动强调概念，比如强制用户做选择。在“布局”菜单选择 Break 时，Word 不会默认最常用的“分页符（Page Break）”，而是强制我先选具体类型。这样既教育了我有哪些选项，也避免我把<a id="id452"></a>文档弄乱。

#### 编码实践 {#id453}

在用户需要做出选择、但可能没意识到这点时，要求<a id="id454"></a>其显式传入参数。

在抽象上通过注释留下<a id="id455"></a><a id="id456"></a>面包屑。比如有两种实现路径，用户找到其中一种时，也要提示另一种他们可能不知道的方式，尤其当后者对大多数用户更推荐时。

### 优化整段用户旅程 {#id190}

正如 `TAKE_DOWN_THE_SITE` 案例所示，<a id="id457"></a>有时为了提升可用性，你会牺牲可发现性和可理解性；换言之，在优化发现、理解、使用时，常常存在<a id="id458"></a>取舍。

对用户旅程做清晰模拟，<a id="id459"></a>有助于做出这些取舍。

例如，如果“不要发明晦涩缩写”这条建议这么好，为什么 Unix / Linux 里还有 `ls`、`cd` 这种流行却晦涩的命令名？它们只是老旧和糟糕吗？

也不完全是。可以说这些名字仍然合理，因为常用命令被“输入”的频次远高于“阅读”的频次。你学一次（或者忘了再学一次）就行，但你不希望每天敲六遍 `list_files` 而不是 `ls`。而且它们面向的是习惯型、技术型用户。在这种情况下，使用场景压过了发现与理解，规则自然会被打破。

但如果你是在做市场传播设计，就可能不得不优先可发现性和搜索引擎优化。比如气泡水品牌“Liquid Death（液体死亡）”这个名字，谢天谢地只有 50% 准确、也不精确，但确实很会营销。在这条用户旅程中，名字先抓住购物者注意力，他们再看副文案确认它到底是什么。

> [!TIP]
> 为你的功能明确发现、理解、使用三者的相对重要性。

### 意符的边界 {#id36}

并非<a id="id460"></a><a id="id461"></a>每个问题都能靠意符解决，尽管你事先未必知道。你可能一开始想找一个完美名称，但随着你推演用户旅程，才发现根本没有一个名字能传达目标画像需要知道的全部内容。也许它太复杂，或者包含某种<a id="id462"></a>安全陷阱。

看看<a id="id463"></a><a id="id464"></a><a id="id465"></a><a id="id466"></a>浏览器 Cookie，以及网页上那些提醒你“页面可能在记录你的数据”的弹窗。要向非技术用户准确传达“到底发生了什么、侵入性到底多强”非常困难。换言之，立法者要求网站沟通 Cookie，网站也在努力合规；他们试图用意符去解决的问题，其实本质上是更深层的设计挑战。

人们做过不少努力，比如区分“必要 Cookie”和营销 Cookie；但用户仍缺乏对 Cookie 本体的清晰理解。结果是大多数人会一刀切关掉所有 Cookie，即使其中一些本可带来好处，或能在几乎无个人成本下支撑互联网经济。

更深的问题是，Cookie 本来就是低层编程抽象，而不是面向用户的功能。人们也提出过更好的抽象，比如提供更细粒度、用户更易理解的分类。可惜，可能由于集体行动困境，截至 2025 年，这个问题仍基本无解。

在产品周期较早阶段就尝试命名与组织产品，是一件非常值得做的事。它会迫使你关注完整用户旅程，并常带来有价值的设计洞察。敬请关注第 [7](/ch7/#ch07_product_discovery_sim) 章和第 [8](/ch8/#ch08_interaction_design) 章，我们会更深入地探讨产品设计。

#### 编码实践 {#id467}

如果<a id="id468"></a>某个抽象既别扭又不安全，而你暂时修不好，就别试图掩盖它。让名字丑一点，吸引注意与审视。也许你的代码评审会有更好想法，也许后来有人会把它改好。我曾见过一个私有类变量，半开玩笑地命名为 `_do_not_use_or_you_​will_be_fired`，对此我也只能不情愿地肃然起敬。

### 本章小结 {#id191}

我们<a id="id469"></a>一路跟随用户，观察他们如何发现、理解并使用产品。此外，在侧栏中我们聚焦了日常编码中的命名决策和编码接口设计，把它们作为练习产品能力的方法。

我最终给出的建议如下：

- 对于发现：优先选择用户本体中已有的名字，或与其他命名保持一致。提供能帮助用户在产品中导航的意符，并以优雅方式逐步揭示更复杂功能。若有帮助，可花时间绘制产品发现图来可视化用户旅程。
- 对于理解：首先避免歧义，并为每类用户提供一致体验。通过提供冗余的“进入路径”来拥抱受众及其视角的多样性，帮助他们理解你的产品。
- 对于使用：利用命名突出潜在陷阱与“危险区”，引导人们正确使用你的函数。

对于多画像应用，不要害怕为不同画像提供不同本体和发现路径。

最后，虽然本章位于 Develop 阶段，请记住：为产品本体中的关键概念命名和建模应更早发生，因为它能为整体设计过程带来重要洞察。

### 练习 {#id192}

在<a id="Euj02"></a>以下练习中，我会把某个东西命名为 *foo* 或 *bar*，你的任务是给它起更好的名字。（由于 *foo* 完全不含信息，可以把你的任务理解为尽可能拉大 *foo-distance*！）当然，这些题并不存在“标准答案”，但请从发现、理解、使用场景去思考。我希望它们接近真实世界练习，这意味着你可能需要做一点轻量设计思考，或上网查资料。

1. 你的数据库允许用户在某张表数据变化时订阅变更事件。如果他们继承 `DataChangeSubscription` 并向数据库注册，那么每次数据库事务提交后都会触发其回调。
   ```python
   class DataChangeSubscription(ABC):
     # subscriptions apply to a certain database table or collection.
     @abstractmethod
     def foo() -> TableType:

     @abstractmethod
     def bar(old_record, changes: Dict[str, Any]) -> None:
   ```
   你会把这两个由子类实现的方法分别命名为什么？
2. 你在做一款面向啤酒爱好者的评分应用。用户点击某款啤酒后，会看到一个名为 *Foo* 的自由文本输入框来写感受。它该叫什么？
3. 新用户会完成一段问卷和注册流程，你正在优化这条流程，因此想追踪一个聚合指标 `foo`，用于衡量他们花了多久。你会怎么命名？
4. 你在维护一个小型社交网络，最近提交的一次改动导致性能回退。你调用了这个函数：
   ```python
     mutual_friends = await current_user.getMutualFriends(friend_user)
   ```
   实际上你并行调用了很多次（每个好友一次），结果比预期昂贵，包含了一些你没预料到的计算与隐私校验。你暂时没时间优化。有没有可能通过重命名来避免其他人将来重蹈覆辙？

### 参考答案 {#id37}

1. 对于 foo，`table_type` 可以，但可考虑 `table_type_filter` 以增强清晰度，让人明确“表类型是用于过滤条件”。对于 bar，可用 `on_record_changed`。`on_` 前缀符合常见事件处理器命名；`record`（单数）明确变化对象；`changed`（过去式）明确提交已发生。若以后新增“提交前”回调，可命名为 `on_record_changing`。另外，我选择 `changed` 也是为了与类名中的 `Changed` 保持一致。
2. 做一点用户研究（或问问聊天机器人）后，你会发现啤酒爱好者常用 “tasting notes” 来描述啤酒。你也可以用 `description` 或 `review` 这类通用词，但 “Tasting Notes” 更可能让目标画像写出“香气”“口感（mouthfeel）”这类细致内容，这正是你想要的。
3. 设想队友在团队看板上读图时会问什么。第一问通常是单位：毫秒、秒还是分钟？第二问可能是统计口径：平均值、中位数还是 p90？因此你可以命名为 `user_signup_seconds_to_complete.p50` 之类。
4. `getMutualFriends` 应该以某种方式提示它“很贵”。比如直接改成 `computeMutualFriends`，也许就足以让你意识到成本。若后续易于重命名，也可叫 `computeMutualFriends_​Expen⁠sive`。也许未来正是这种“丑得显眼”的名字，会促使后来者去做缓存或把它优化得更便宜。<a id="id470"></a>
