最近在评审某跨平台系统架构时,又看到一张让我头疼的部署图:十几个服务方块堆在一起,箭头纵横交错,颜色至少用了八种,图例却只字未提。提问环节大家确认的不是业务逻辑,而是“这个箭头到底是调用还是依赖”。画图的人一脸委屈:我明明画得很认真。问题恰恰出在这里——他把时间花在了“画”上,而没有花在“设计”上。
diagram-design 这件事,往小了说是画图排版,往大了说是信息结构设计。同样一张架构图、流程图或 ER 图,设计过的版本和随手涂的版本,信息传达效率能差一个数量级。这篇文章不聊某个具体工具的高级技巧,而是想把我这些年做技术方案配图、写设计文档、搞团队评审时沉淀下来的方法整理出来:怎么选图种、怎么定版式、怎么用颜色和文字、怎么把手绘图变成可维护的工程资产。适合做架构、写文档、带项目的开发者,也包括被“画图”这件事折磨过的产品和测试同学。
1. 为什么“画图”这件小事,值得认真当作一门设计来做
很多人觉得,画图是技术方案里最不值一提的环节,“思路对了就行”。但我在实际评审中反复遇到的情况是:思路对不等于别人能看懂,而看不懂的思路,在讨论中就会被默认成“不对”。一张合格的图,本质是一份契约,契约写不清楚,后续所有基于它的讨论都会失真。
1.1 一幅好图等于省掉十场会议
我统计过自己在两个项目里的沟通成本。某系统早期没有系统性地设计架构图,每次有新成员加入,就要拉一次散会,逐块口头解释模块关系,一次半小时起。后来花了半天时间把分层架构图、核心链路时序图、数据模型 ER 图各整理一版,新成员上手速度明显加快,很多问题自己看图就能解决。
这不是玄学。人类处理视觉信息的速度远快于处理文字,一张结构正确的图天然具备“并行解释”的能力。它能同时展示整体结构和局部细节,让读者按自己的节奏消化。而文字说明是线性的,读的人必须从头到尾看完才知道全貌。所以,一幅好图省掉的其实是“重复解释”的时间,这在跨团队协作时尤其值钱。
1.2 大多数人画图的通病:只“画”不“设计”
什么叫只画不设计?我总结为三个典型症状。
第一,没有明确“这张图给谁看”。给老板看的架构图和给开发看的部署图,信息密度与抽象层级应该是完全不同的。给老板的核心是“系统边界、投入规模、风险点”;给开发的核心是“模块划分、接口关系、部署依赖”。很多人一张图走天下,结果谁都看不懂。
第二,没有视觉层级。所有方块等宽等高,所有箭头同色同粗细,信息没有主次。读者不知道先看哪里,也不知道哪些是主干、哪些是支线。
第三,图种混用。把部署拓扑、调用关系和业务流程塞进同一张图里。读者满脑子都是问号:这个方块是物理机还是逻辑服务?这条线表示数据流还是控制流?
真正把 diagram-design 当设计做的人,会先想清楚以上三件事,再动手选工具。这一步做扎实了,后面的一切都是执行问题。
2. 先选对图种,再看怎么画:常见技术图的适用边界
设计一张图的第一步不是打开画布,而是回答一个问题:我要表达的对象,本质上属于哪种信息结构?不同结构天然适合不同的图种,强行互相代用,往往事倍功半。
2.1 架构图:让系统结构一目了然的第一个工具
架构图用于表达“系统由哪些部分组成,彼此什么关系”,是技术方案里出现频率最高的图。但很多人把架构图画成了“所有东西的叠加态”。我个人的习惯是,架构图至少拆成三个视角。
- 系统上下文图:只画本系统、外部依赖和关键交互,用于对齐边界。
- 容器/模块图:画系统内部的主要模块或服务,以及它们之间的调用关系。
- 部署图:画各组件与实际运行环境(物理机、容器、云资源)的对应关系。
这三者对应不同读者、不同阶段的决策。把三者混在一张图里,是让我最头疼的坏习惯。比如有人把“Nginx 网关”和“用户认证模块”并排画在一起,前者是部署层面的组件,后者是逻辑层面的模块,根本没有可比性。
提示:一个简单判断标准——如果图中存在“逻辑模块”和“物理节点”两类元素,且没有通过不同容器或分层明确隔开,这张图大概率会被误读。
2.2 流程图与状态图:逻辑梳理才是重点
流程图画的是“一件事的处理路径”,重点在于分支条件和异常路径。我用流程图时最关注三个检查点。
一是单入口单出口。除了并行分支,理想的主流程应当有一个明确的起点和一个明确的终点。出现多个散乱出口,往往意味着处理逻辑本身没有收敛。
二是分支条件必须完整。在某个判断节点上,只画了“是”的分支而漏掉“否”,是流程图最常见的残缺。漏掉的否分支通常就是线上出 bug 的地方。
三是异常路径和主路径分开绘制,或者用完全不同的线型标识。我见过不少流程图把异常处理直接画在主链路旁边,结果看的人以为那也是正常路径。
状态图则用于表达一个对象在其生命周期里的状态变化,比如订单从创建、支付、发货到完成。画状态图的关键是先定义状态机的要素:状态、事件、动作和迁移条件。这里经常出现的问题是状态与动作混淆——把“调用支付接口”当成一个状态,实际上它是一个触发状态迁移的事件动作,不是状态本身。
2.3 时序图与 ER 图:精确表达交互和数据关系
时序图回答的是“在某个用例下,各参与方按什么顺序做了什么”。它的核心约束是时间维度——垂直方向代表时间推进,消息按从上到下的顺序排列。画时序图最容易犯的错误是顺序错乱:消息在图上乱飞,读者无法按阅读顺序还原调用链。解决方法是先写出参与方清单,再列出消息序列,最后才落笔排版。
ER 图画的是数据实体及其关系,重点是基数关系。一对多、多对多是数据建模的核心信息,也是评审时最容易产生分歧的地方。我见过太多 ER 图只在两个实体之间画一条线,不标基数,等于啥也没说。必须用 1、* 或者 crows foot 符号明确标注。
表格式的图种选择参考如下:
| 图种 | 核心表达 | 典型读者 | 常见错误 |
|---|---|---|---|
| 系统上下文图 | 系统边界与外部交互 | 产品、管理层 | 把内部模块画进来 |
| 容器/模块图 | 内部模块与依赖 | 开发、架构 | 与非软件元素混排 |
| 部署图 | 组件与环境对应 | 运维、开发 | 缺少环境边界 |
| 流程图 | 处理路径与分支 | 业务、开发 | 漏画异常分支 |
| 状态图 | 生命周期变化 | 开发、测试 | 状态与动作混淆 |
| 时序图 | 消息顺序 | 开发、测试 | 消息排列顺序错乱 |
| ER 图 | 实体与基数关系 | 数据库、后端 | 不标基数、不标主外键 |
注意:以上是按“主表达”划分的维度,不反对图种嵌套,比如在模块图里嵌入一张带时序的交互子图。但嵌套必须明确主次,不能所有信息平铺、互相争抢注意力。
3. 让读者一眼看懂的核心设计原则
选对图种之后,真正“设计”的部分才刚开始。设计不等于美化,而是一系列有目的的取舍,目的是让读者在最短时间内提取到最关键的信息。
3.1 版式分层:先有骨骼,再有血肉
我画图的固定顺序是:先画骨骼——确定主体结构怎么排列,再填充细节。具体来说,分四步。
第一步,确定主方向。绝大多数人习惯从左到右阅读,所以主流程尽量从左往右走;分层结构则优先从上往下。方向定了,整图的阅读顺序就有了。
第二步,做分组和留白。逻辑上属于同一层或者同一个域的元素,用容器包起来或者通过留白隔开。物理上相近的元素,读者会下意识认为是逻辑上相关的元素。分组不清的图,阅读时会产生很多错误联想。
第三步,突出主干。对于核心链路,通过更粗的线条、更大的区块或更深的颜色让它从背景中跳出来。边缘内容退到底层。没有主次,就相当于什么都没说。
第四步,控制交叉。连线避免跨过大块图元,尽量走外围,或者用总线方式把多条线汇聚。这个目标是让视觉不被打断,保持阅读的连续感。
3.2 配色与一致性:不是美术问题,是信息问题
配色在技术图里不是为了好看,是为了编码信息。我使用的策略是:颜色承载“层”或“类别”的语义,并保持全局一致。
比如在部署图里,约定“绿色系=外部依赖,蓝色系=自研服务,灰色系=基础设施”。这个约定在整个项目的所有文档里必须保持一致,包括 PPT、接口文档、wiki 配图。这才叫颜色编码。如果每个文档各用各的配色,颜色反而会变成噪音。
另外,不能只依赖颜色区分信息。总有人是色弱或打黑白打印版。所以重要信息必须有第二编码维度:加边框、换线型、加文字标注。我在带团队时立过一条规矩:如果去掉颜色后图的信息不完整,说明这张图不合格。
3.3 文字规范:命名、缩写和注释的统一约定
图里的文字虽然少,却是信息传达的最关键载体。我有几条强制规范。
一是节点用名词短语,连线用动词短语。比如节点叫“订单服务”,连线叫“创建订单”。反过来,节点上写“创建订单”,连线上写“订单服务”,读者需要反复翻译。
二是同一个对象在全项目中只有一个名字。不能在这个文档里叫“订单模块”,在那个文档里叫“Order Service”,在代码里又变成order-service。推荐在图里统一使用团队代码仓库中的模块名,保证图与代码可互相索引。
三是缩写必须给出图例或注释。首屏不解释,用了“MB”表示消息总线,就要在旁边图例注明。这是我在评审中反复强调的一点,缩写不解释,等于强迫读者记住一套无意义的符号体系。
提示:一条实用经验——把颜色图例、线型图例和缩写表固定放在图的右下角或左上角,形成模板习惯。这样读图的人一旦遇到不确定的符号,目光会自然去找图例,而不是靠猜。
4. 工具选型:代码驱动还是拖拽画布
聊完方法论,说说工具。市面上的图工具大致分两类:一类是拖拽画布型,比如 Draw.io、某设计协作工具;另一类是代码驱动型,用文本描述图结构,比如 PlantUML、Mermaid 这类标记语言。我自己两类都在用,但使用场景完全不同。
4.1 两派工具的适用场景对比
拖拽画布的优势是即时、自由、视觉顺手,适合头脑风暴、现场评审、快速白板。它的劣势是版本管理弱、难以 diff、多人协作容易互相覆盖。我见过团队用画布工具维护架构图,改着改着出现了三份内容不同的 copy,没人说得清哪份是权威版本。
代码驱动的优势是图即代码,天然支持文本 diff、版本仓库协作和自动化生成。劣势是有学习成本,排版调整比较费劲,复杂图调布局需要反复编译预览。
| 维度 | 拖拽画布型 | 代码驱动型 |
|---|---|---|
| 上手速度 | 快,即改即得 | 需要学习标记语法 |
| 版本管理 | 差,二进制文件难 diff | 好,文本可直接 diff |
| 团队协作 | 实时协作方便,但易冲突 | 基于仓库评审,流程严谨 |
| 自动化 | 弱,难联动文档 | 强,可嵌入 CI / 文档流水线 |
| 复杂排版 | 灵活,手动可调 | 自动化布局,精细调整费劲 |
| 典型场景 | 方案讨论、临时沟通 | 长期维护的权威架构文档 |
4.2 我用 diagram-as-code 的实践
我们现在把长期维护的架构图、ER 图全部迁移到代码驱动方案。对应的文件放在文档仓库里,和项目代码一起走评审流程。改动一个模块依赖,就是一次普通的 MR,reviewer 能在 diff 里看到具体是哪个依赖关系变了,而不是在一张图片里“好像哪里变了”。
一个简单的例子,用类 PlantUML 语法描述模块关系可能长这样:
@startuml package "业务层" { [订单服务] [支付服务] } package "基础层" { [用户中心] [消息队列] } 订单服务 --> 消息队列 : 发送事件 支付服务 --> 用户中心 : 校验身份 支付服务 --> 订单服务 : 更新状态 @enduml这段文本表达的内容和一张手绘架构图完全等价,但好处是:可 diff、可搜索、可生成,而且源文件就是注释文档,新成员看代码时能直接看到系统结构。
4.3 团队协作中的版本管理问题
代码驱动不等于万事大吉,实践中最容易踩的坑是“一个人改了图,没有同步说明”。因为图的源文件本质是代码,必须遵循代码协作的规范:每个图文件顶部写清楚“本图对应的系统版本/负责人”,每次改动在 MR 描述里说明变更原因。我把这些和代码评审合在一起,减少“图文档游离在体系之外”的情况。
另一条经验是“一图一文件,一文件一主题”。很多人习惯把系统的全套图塞进一个文本文件,几屏几屏往下拉,结果 diff 时根本分不清改了什么。我现在要求每个图独立文件,命名时带上主题,例如order-service-module.puml,让文件名首先承担索引功能。
5. 从需求到成品:一张合格图的完整产出流程
很多人的画图流程是“打开工具、凭感觉摆放、导出图片”。我的流程是工程化的,分五步走。这套流程我用了很长时间,稳定可靠。
5.1 澄清读者和目的
动手之前,先回答两个问题:这张图给谁看?看完之后要支持什么判断?如果是给管理层看系统建设进度,那部署细节全部隐藏,只留系统边界和关键依赖;如果是给新入职的研发看,那要保留服务列表和核心调用关系,但可以让颜色和细节更丰富。
我把这个阶段叫做“定义图的契约”。契约没定义清楚,后面画得再漂亮也是自我满足。
5.2 信息收集与结构化
在画布之外,先把图上需要的所有元素列出来。我会用一个简单的清单文本整理,比如这样:
参与方:用户端、网关、订单服务、支付服务、消息队列、外部支付平台 主链路:用户端 -> 网关 -> 订单服务 -> 支付服务 -> 外部支付平台 依赖方:订单服务依赖消息队列,支付服务依赖用户中心 分组:业务层(订单、支付),基础层(用户中心、消息队列),外部(支付平台)把这个清单整理清楚,图就已经完成了一大半。大多数人画图慢,不是排版慢,而是没有先做结构化。结构化完成之后,图上画什么、不画什么、边界在哪里,一清二楚。
5.3 绘制、评审、迭代
结构化完成后再落到画布或代码里,这时候我才会考虑布局、配色和文字细节。第一版草稿出来后,我会做一件事:桌面测试。找一位对项目不了解的同事,只给图不给说明,看他在 30 秒内能不能复述出图的核心信息。
如果他能说出“订单服务调用支付服务,中间经过网关,并且依赖消息队列”,这张图就合格了。如果他说的是“这里有很多方块和箭头,我没太看懂”,那就得回去改版式或减信息。迭代两三轮之后,图才真正具备“可发布”的状态。
提示:30 秒原则是我自己定的一把尺子。技术图是给别人看的,不是给自己看的。自己看每一张都清晰,只有陌生的读者能在 30 秒内抓到重点,才算画对了。
6. 那些年踩过的坑:常见错误与修正方法
方法讲了不少,但真正让人成长的是踩坑经历。我挑几个最有代表性的坑,连同修正方法一起整理出来。
6.1 信息过载与“一张图走天下”
早期给某项目画架构图时,为了显得全面,我把服务列表、数据库表、消息队列、定时任务、甚至是缓存 key 的设计都放进了同一张图。结果评审时没人讨论架构,全在问“这几个参数为什么画上去”。这就是典型的信息过载。
修正方法是拆分视图:总览图只留系统边界、主要模块和核心链路;每个模块的细节用单独的图表达,或者直接指向代码文档。我后来给自己定了一条规则:如果一张图里超过 15 个主节点,就必须拆分。“一张图走天下”不是能力,是偷懒。
6.2 时序不对与边界不清
画时序图时,我发现团队里很容易出现消息顺序凭“感觉”乱排的情况。后来我把消息序列先以纯文本形式写出来,按序号排列好,再翻译成时序图。这个步骤有效避免了“画图时才想流程”带来的顺序错乱。
边界不清则是另一种高频问题。比如系统上下文图把内部模块画了出来,导致外部读者误认为这是系统全景。修正手段是在图上用显眼的分区容器标明内外,容器边界就是信任边界,这条线画得越明确,误读越少。
6.3 图与代码脱节
最后一个大坑是图和代码脱节。画完架构图、写完代码之后,图就成了挂在 wiki 上的“展品”,三个月后代码改了七八轮,图还停留在最初版。废弃的图比没有图更有害,因为它会引导新人走到错误的方向上。
我目前的处理方式是:
- 把图形源文件放进文档仓库,与代码一起走 MR 流程,任何代码变更影响结构,必须在同一次变更里更新图。
- 在 CI 里加一步构建任务,把代码驱动图形生成并发布到内部文档站点,图形一旦不能编译就会告警。
- 对无法自动检查的拖拽图,至少在文档里标注维护人和“最后更新日期”,超过半年未更新的图默认视为废弃。
这个机制不能保证图永远新鲜,但至少让“图过期”这件事从不会在代码评审时被主动提起,变为每次变更都会被顺带确认的例行动作。
我在实际使用中形成的个人体会是:diagram-design 的功夫有大约一半花在动手之前。搞清楚读者、目的和信息边界,比调三个像素重要得多。另一个很实用的小技巧是给自己建一个图例模板库——统一的颜色语义、线型规则和缩写表直接复用,时间长了整个团队的图都会长成一个风格,沟通成本会肉眼可见地降下来。画图这件事不追求惊艳,追求的是不让读者产生误解;能把复杂讲清楚,本身就是一种体面。