乐于分享
好东西不私藏

好交互不是好看:软件工程师应理解的设计底层逻辑

好交互不是好看:软件工程师应理解的设计底层逻辑

引言

作为一名软件工程师,我一直不太知道如何做好交互设计。

我当然知道一些具体的经验,比如界面要简洁,按钮要清楚,状态要有反馈。但这些经验总让我觉得不够完整。它们更像是一些零散的技巧,而不是一套可以指导我判断和取舍的方法。

我始终认为,交互设计应该是一门严肃的学科。它不是单纯依赖审美,也不是靠灵感把页面摆得更好看。它背后一定遵循某些稳定的原则、方法和认知规律。通过这些方法的指导,我们才有可能做出像 Figma、VS Code、Stripe Dashboard、GitHub、Linear、Apple Shortcuts、Zapier 这样交互优秀的软件:复杂但不混乱,强大但不压迫用户,能力很多但用户仍然知道下一步该做什么。

这个问题困扰了我很久。直到有一天,我和聪明的 Codex 进行了一次比较深入的对话,才逐渐意识到:优秀交互的底层逻辑,并不是“把界面做漂亮”,而是让用户操控一个可理解的对象系统。

也就是说,一个好的软件界面,应该能持续回答这几个问题:用户现在想做什么?他正在操作什么对象?这个对象处于什么状态?当前最合理的动作是什么?动作之后系统给了什么反馈?反馈之后用户应该进入哪一步?

这篇文章,就是我对这次对话的整理。它不是一篇设计教程,更像是一个软件工程师试图理解交互设计这门学科时,梳理出来的一套基本思考框架。

为什么工程师容易把界面做复杂

工程师做产品时,很容易从系统能力出发:

  • 我们支持创建项目。
  • 我们支持编辑节点。
  • 我们支持自动保存。
  • 我们支持校验。
  • 我们支持加载预设。
  • 我们支持右键菜单。

这些能力本身都没错。问题在于,如果它们被同时摊在同一个界面上,用户看到的不是能力,而是噪音。

用户第一次打开软件时,脑子里并没有系统架构图。他只是在问几个非常朴素的问题:

  • 我现在在哪?
  • 我正在操作什么东西?
  • 这个东西现在是什么状态?
  • 我下一步应该做什么?
  • 我做完以后,系统有没有理解我?

如果界面不能回答这些问题,再漂亮的卡片、按钮和图标都只是装饰。

这就是很多“功能很强但不好用”的软件的根源:它们把系统内部结构暴露给用户,却没有把用户的行动路径设计出来。

优秀软件的共同点:不是少,而是有秩序

Google Search、Figma、VS Code、Stripe Dashboard、GitHub、Linear、Apple Shortcuts、Zapier 这些产品形态完全不同,但它们有一个共同点:

它们不是把功能直接堆给用户,而是先让用户理解一个可操作的对象系统。

Google Search 的对象是“搜索意图”,所以首页几乎只保留一个输入框。

Figma 的对象是“画布上的设计元素”,所以默认让画布成为主角;只有选中对象后,右侧属性才变得重要。

VS Code 的对象是“工作区、文件、编辑器、终端、问题列表”,复杂能力被组织进侧栏、命令面板和状态栏。

Stripe Dashboard 的对象是“客户、付款、订阅、发票、事件日志”,每个对象都有清晰状态和下一步操作。

GitHub 的对象是“仓库、Issue、PR、Commit、Branch”,几乎所有操作都围绕对象状态展开。

Zapier 和 Apple Shortcuts 的对象是“一条自动化流程”,所以它们用一步一步的构建方式控制复杂度,而不是一上来展示完整系统能力。

所以优秀交互不是“界面元素少”,而是用户始终知道自己在操控什么、它处于什么状态、下一步能做什么。

这背后的学科基础

优秀交互设计不是审美玄学,它背后至少有五个重要学科。

1HCI:人机交互

HCI 研究的是人如何向系统表达意图,以及系统如何把状态反馈给人。

Don Norman 在《The Design of Everyday Things》中提出过一个非常重要的思路:用户使用系统时,会经历从目标到动作,再从系统反馈到理解结果的循环。

这个循环里有两个常见断点:

  • 执行鸿沟:用户知道自己想做什么,但不知道该怎么操作。
  • 评估鸿沟:用户做了操作,但不知道系统发生了什么。

很多糟糕界面的问题,正是这两个鸿沟太宽。

例如一个画布工具里,用户想“创建第一步”,但界面同时显示创建、载入、保存、校验、配置、项目 ID、节点列表,他就不知道该点哪里。这是执行鸿沟。

用户拖出一条线后,没有明确告诉他能接什么、不能接什么、为什么不能接,这是评估鸿沟。

2认知心理学:人不是无限内存机器

工程师习惯同时持有很多上下文:数据结构、调用链、状态机、异常分支。

但普通用户,甚至专业用户,在使用界面时的工作记忆也很有限。界面一次给太多选择,会让用户停止行动。

这不是用户“不聪明”,而是人类认知系统的正常限制。

所以好交互的关键不是“把所有能力都展示出来”,而是控制用户当下需要处理的信息量。

这就是渐进披露的价值:先让用户完成当前一步,再在新状态下展示下一步。

3信息架构:先组织对象,再组织页面

很多界面混乱,是因为设计时先想页面,而不是先想对象。

优秀软件通常有非常清楚的对象模型:

  • GitHub:Repository、Issue、Pull Request、Commit、Branch。
  • Stripe:Customer、Payment、Subscription、Invoice、Event。
  • Figma:File、Page、Frame、Layer、Component。
  • Linear:Workspace、Project、Issue、Cycle、Status。

对象清楚以后,页面才会清楚。因为页面只是对象、状态和动作的组织方式。

如果对象不清楚,界面就会变成功能按钮的集合。

4控制论:界面是系统的控制面板

软件不是静态海报,而是一个可以被用户控制的系统。

一个好的控制面板必须表达四件事:

  • 当前状态是什么?
  • 用户能控制什么?
  • 控制后系统发生了什么?
  • 系统进入了什么新状态?

如果用户创建了节点,界面要显示“已选中这个节点”。如果用户连接失败,界面要显示“连接被阻止,因为输出和输入类型不匹配”。如果系统正在保存,界面要显示“自动保存中”。如果保存完成,界面要显示“已自动保存”。

反馈不是锦上添花,反馈是功能的一部分。

5视觉感知:结构比装饰重要

视觉设计不是把界面做漂亮,而是帮助用户理解结构。

距离、对齐、分组、对比、层级,都会影响用户如何理解信息。

如果所有东西都有边框,用户就不知道哪个最重要。

如果所有按钮都是高亮按钮,用户就不知道哪个是主动作。

如果空状态、编辑器、校验结果、保存状态同时抢注意力,用户就不知道当前任务是什么。

好视觉不是“加设计感”,而是让交互结构变得可见。

一个更底层的公式

可以把优秀软件的交互逻辑压缩成一个公式:

意图 -> 对象 -> 状态 -> 动作 -> 反馈 -> 下一步

这不是一个文案公式,而是一个产品结构公式。

用户先有意图。例如:我想搭建一条内容生产管线。

系统需要让用户找到对象。例如:当前编辑的是某个项目里的某张画布。

对象必须有状态。例如:空画布、已有节点、选中节点、连接中、校验失败、已保存。

每个状态只应该突出当前最合理的动作。例如:空画布时只需要创建第一个原始节点;选中节点后才需要显示可连接的下一步。

动作之后必须有反馈。例如:节点已创建、连接已建立、连接被阻止、自动保存完成。

反馈之后进入下一状态。例如:从空画布进入“已有第一个节点”的状态。

如果这个链路断了,用户就会迷路。

工程师可以怎么落地

对软件工程师来说,最有用的不是“审美建议”,而是一套可以进入开发流程的方法。

第一步:先写对象模型

不要先画页面。先回答:

  • 用户在操作什么对象?
  • 对象之间是什么关系?
  • 哪些对象是持久化的?
  • 哪些对象只是临时状态?

例如管线工具里,至少有这些对象:

  • Project:项目,负责存储作用域。
  • Canvas:画布,负责承载一张图。
  • Node:节点,代表一个处理步骤。
  • Port:端口,代表输入输出类型。
  • Edge:连接,代表数据流向。
  • Validation:校验结果,代表系统判断。

对象模型清楚以后,页面不容易乱。

第二步:列出对象状态

每个核心对象都应该有状态。

以画布为例:

  • empty:空画布。
  • editing:已有节点,正在编辑。
  • node-selected:选中了某个节点。
  • connecting:正在连接。
  • blocked:操作被阻止。
  • dirty:有未保存更改。
  • saving:保存中。
  • saved:已保存。
  • invalid:校验失败。

很多 UI 混乱,是因为没有状态模型。设计时只是在想“这里放什么组件”,而不是“当前是什么状态”。

第三步:每个状态只保留一个主动作

这是最重要的一条规则。

空画布状态下,主动作就是:创建第一个原始节点。

不要同时突出保存、校验、节点配置、项目 ID、历史记录、全部节点类型。

已有节点但未选中时,主动作可以是:选择节点或继续创建。

选中节点后,主动作可以是:从当前节点继续。

出现校验错误时,主动作应该变成:修复错误。

一个状态只有一个主动作,用户才知道下一步。

第四步:把复杂能力延后出现

不是所有信息都必须隐藏,但所有信息都应该有出现时机。

空画布时,不需要显示节点配置面板。

没有错误时,不需要展开校验详情。

没有选中对象时,不需要显示属性编辑器。

没有保存过时,不需要显示复杂的版本历史。

这不是阉割功能,而是控制复杂度的进入节奏。

第五步:把反馈当成协议设计

工程师设计 API 时会认真定义返回值,却常常忽视 UI 反馈。

但对用户来说,反馈就是系统协议。

建议每个重要动作都定义:

  • pending:系统正在处理什么?
  • success:成功后如何表达?
  • warning:有哪些非阻塞风险?
  • error:失败原因是什么?
  • blocked:哪些动作在提交前就应该被阻止?
  • recovery:用户下一步怎么恢复?

例如连接两个节点时:

  • 拖动输出端口:预览可连接目标。
  • 悬停不兼容输入:显示阻止状态。
  • 松手连接失败:不创建边,并说明原因。
  • 连接成功:创建边,标记 dirty,触发自动保存。

这才是完整交互。

用这套逻辑重看几个优秀产品

Figma:选择之后才有属性

Figma 的强大不在于右侧属性面板有多少功能,而在于它遵守了一个基本秩序:

先有画布对象,再有选择状态,再有属性编辑。

没有选中对象时,很多属性没有意义。选中 Frame、Text、Component 后,右侧面板才进入对应上下文。

这就是对象状态驱动界面。

VS Code:复杂能力不挤在一个页面里

VS Code 的功能非常多,但它没有把所有能力塞进编辑区。

文件树在侧边栏,命令在命令面板,问题在 Problems,终端在底部,状态在状态栏。

它的核心原则是:主工作区保持稳定,复杂能力进入不同的控制区域。

这对专业工具非常重要。

Stripe:业务对象有清晰状态

Stripe Dashboard 处理的是非常复杂的业务系统,但它把复杂性落在对象和状态上:

客户、付款、订阅、发票、事件日志,每个对象都有身份、状态、时间线和可用动作。

用户不需要理解 Stripe 内部系统,只需要理解“这个付款现在成功了吗?失败了吗?为什么?下一步能做什么?”

Zapier:流程不是一次性铺开

Zapier 不是让用户一上来面对完整流程图,而是引导用户一步步添加 Trigger 和 Action。

这对自动化产品特别关键。因为流程本身很复杂,必须通过构建顺序降低理解成本。

用户每完成一步,系统进入一个新状态,然后展示下一步。

对工程团队最有价值的检查表

如果一个页面看起来乱,不要先问“怎么美化”,先问这些问题:

  • 这个页面的核心对象是什么?
  • 用户第一次进入时,最可能的意图是什么?
  • 当前对象有哪些状态?
  • 每个状态下唯一的主动作是什么?
  • 哪些信息可以延后到状态变化后再出现?
  • 用户执行动作前,系统有没有预览可行性?
  • 用户执行动作后,系统有没有明确反馈?
  • 错误发生时,用户知道怎么恢复吗?
  • 页面是在展示系统能力,还是在支持用户完成任务?

这套问题比“按钮放左边还是右边”更重要。

最后:好交互是工程能力的一部分

很多工程师会把交互设计看成设计师的事情。但实际开发中,交互质量很大程度取决于工程团队如何理解系统。

因为交互设计并不只是颜色、间距和动效。

它要求你定义对象模型、状态机、反馈协议、错误恢复、信息层级和复杂度进入节奏。

这些恰恰都是工程师最擅长的事情。

所以,一款交互优秀的软件,底层逻辑并不是“更漂亮”,而是:

让用户操控一个可理解的对象系统,在每个状态下只面对当前最重要的动作,并在每次动作之后获得清晰反馈。

当我们用这个标准去设计软件时,界面自然会变得简洁。不是因为功能少了,而是因为复杂性被放回了正确的位置。

参考阅读

  • Nielsen Norman Group:10 Usability Heuristics for User Interface Design
  • W3C:Web Content Accessibility Guidelines 2.2
  • W3C:WAI-ARIA Authoring Practices Guide
  • Material Design:Interaction states
  • Don Norman:《The Design of Everyday Things》