聊到 diagram-design,很多人第一反应是打开某个绘图工具,然后拖几个方框和箭头。老实说,这几年我经手过不少技术方案、产品说明和内部文档,越来越确定一件事:大部分让人看不懂的图,问题根本不在画图技术,而是画图的人没想清楚这张图到底要替谁回答什么问题。这篇文章不准备讲某个工具的某个按钮,而是把图表设计的完整思路、工具选型逻辑、排版原则和实战里踩过的坑一次性说透。不管你是画架构图、流程图、时序图还是数据流向图,只要是一个需要“画图表达关系”的场景,这篇文章都值得你花十分钟读完。
1. 图表设计的第一步:先把要表达的关系写成人话
1.1 图表是陈述句,不是装饰画
我见过很多人在新项目启动时,第一件事就是打开画板,开始拖矩形、画箭头。结果画了两小时,图越来越复杂,自己看着都心虚。原因很简单:他们跳过了“用文字描述信息关系”这个阶段。
图表本质上是一个陈述句。它回答的是“A如何变成B”“C依赖于D”“E在什么条件下跳转到F”这类具体问题。如果在动笔之前,你能用一两句话把这张图要表达的核心问题说出来,那么这张图的基本骨架已经完成了一半。比如:
- “这张图要说明用户从输入手机号到进入首页,中间经历了哪些校验。”
- “这张图要说明订单服务在超时、取消、支付成功三种情况下状态如何流转。”
- “这张图要说明数据从日志采集到数仓层,经过了几个清洗环节。”
如果你发现自己说不清这张图到底要回答什么问题,那大概率是这个问题本身还没被想清楚。这时候画出来的图,要么是堆砌,要么是自嗨。我自己的习惯是:先在文档最顶部写一行“本图目标:……”,如果写不出来,就合上画图工具,先回去做信息梳理。
1.2 从关系清单到图形语言:节点、连线、分组
当文字描述清晰之后,下一步是把句子里的关键元素拆出来。所有图表,不管它多复杂,都可以归纳成三类图形单元:节点(名词)、连线(动词)、分组(形容词或副词)。
节点是图中的实体,比如系统、模块、人员、状态;连线表达实体之间的关系,比如数据流转、控制流、依赖关系;分组则是把一大片内容按边界划分,告诉读者“这些东西属于同一个域”。
你可以先用一个简单的表格列出关系清单:
| 关系类型 | 典型例子 | 图形上如何表达 |
|---|---|---|
| 顺序执行 | A调用B,B调用C | 实线箭头,从上到下或从左到右 |
| 条件分支 | 如果库存不足,走异常流程 | 菱形判断节点后分两条带标签的箭头 |
| 依赖关联 | 订单服务依赖用户服务 | 弱语义用虚线,强绑定用实线,方向指向被依赖方 |
| 层级归属 | 支付服务属于交易域 | 外部容器/泳道包裹内部节点 |
| 消息返回 | 请求后异步回调 | 与主请求方向相反的虚线 |
这一步的价值在于,它逼你把“感觉上应该这样画”变成“这些关系本身决定了图形表达”。一个常见的错误是:把所有关系都画成实线箭头,结果整张图变成一团毛线。其实如果能在动笔前先列清楚每条线的语义,你会发现很多连线根本没资格被画出来。
2. 工具选型的真实逻辑:不同图表语言对应不同工作流
2.1 先分清你是“画图”还是“算图”
很多人在选绘图工具时,只看“能不能画”,不看“画完之后怎么办”。我自己的经验是,工具选型的第一刀,是判断你属于“画图”还是“算图”。
所谓画图,是指你希望完全控制节点位置、连线走向和整体布局。这类场景最典型的是白板讨论、产品原型配图、给客户看的方案示意。工具上适合用 Figma、Excalidraw、draw.io(也就是 diagrams.net)、ProcessOn 这类可视化编辑器。它们的共同特点是自由度高,但代价是:当图变大变复杂时,布局维护全靠手工。
所谓算图,是指你已经知道节点和关系的数据,希望工具自动计算布局。典型场景包括系统依赖图、类继承关系图、状态机图、调用链图。这类场景用代码化工具效率高得多,Graphviz、PlantUML、Mermaid 都可以;只要改了关系,布局是工具算出来的,不需要你手动去拖线。不是越高级的工具越好,而是你的工作流需要哪种“图形生成方式”。
我经常遇到有人用画图工具硬画上百个节点的依赖图,结果挪一次节点要耗掉半天。反过来,也有人用代码化工具画一张给客户的精美概念图,结果光是调布局就调了一个礼拜,这就是典型的人与工具错配。
2.2 团队协作方式决定工具上限
选工具的第二刀,是看这张图之后要和多少人协作、在什么场景下被更新。
如果图只是你个人临时梳理思路,用白板纸笔画都行。但如果是团队文档里的架构图,那么“能不能被回溯修改”就比“当下画得漂不漂亮”重要得多。
- 需要多人同时在线编辑的,优先考虑支持实时协作和评论的工具。
- 需要进入 Git 仓库做版本管理的,优先考虑文本代码化方案,比如 Graphviz 或 Mermaid,它们能随普通代码一起走 code review。
- 需要嵌入生成的静态文档反复使用的,优先考虑能稳定导出 SVG 和透明背景 PNG 的工具。
我见过太多团队,因为选了一个“画图很方便”但没法版本追踪的工具,导致三个月后文档里的图和线上系统完全对不上,最后整篇文档失去信任。图表设计从来不只是一张图的问题,它背后是信息更新的可持续性问题。
2.3 我目前常用的组合
我并不迷信某个单一工具,现在我通常这样搭配:
- 快速探索、和同事白板讨论时,优先用 Excalidraw 或者直接拿笔画,目标是快速表达想法,不求美观。
- 正式技术设计文档里,会用 draw.io 画少量业务流程图和部署图,导出为 SVG 内嵌到文档系统。
- 涉及大量节点关系、需要长期维护的依赖图,直接用 Graphviz DOT 编写,每次改动都提交到 Git。
- 如果是在代码注释或 README 里顺带展示调用流程,就用 Mermaid 写一段短图,让代码托管平台自动渲染。
这套组合的关键不是工具多,而是每个工具都被用在它擅长的生命周期阶段。
3. 节点、连线和文字:图表的基本设计三要素拆解
3.1 节点是图表的“名词”
节点形状不是随便选的。虽然很多绘图工具默认所有节点都长得差不多,但你一旦决定用某种形状表达某类实体,整个文档里所有同类型实体都应该保持同一形状。
我常用的约定如下:
- 圆角矩形:表示系统、模块、服务等抽象组件。
- 直角矩形:表示数据表、文件或者实体定义,视觉上更“硬”一些。
- 菱形:表示判断、分支条件,流程图里最常见。
- 圆柱体:表示数据库、消息队列等存储或中间件。
- 圆角胶囊:表示起点或终点。
形状的本质是给读者提供一种“视觉快速归类”能力。如果一张图里所有节点全用直角矩形,读者只能逐字读标签,认知负担非常大。相反的,如果形状语义不统一,比如同样是“服务”,上一张图用圆角矩形,下一张图用六边形,读者就会困惑:这是不是两种不同的东西?
节点内部文字也要克制。一个节点里塞两三行功能描述,是方案文档里最常见的灾难。节点的作用不是写需求,而是命名。理想情况下,节点文字应该短到能一眼看完。如果信息量太大,可以拆成节点标题加下方备注,或者用数字角标在页面外补充说明。
3.2 连线是图表的“动词”
节点决定“有什么”,连线决定“干什么”。我在审查图表时,最先看的不是布局,而是箭头语义是否混乱。
箭头方向是第一优先级。流程图的箭头应该顺着主流程的方向,不要出现一根箭头往左、下一根往右这种蛇形走位。依赖图的箭头,各团队常常有不同习惯:有人指向调用方,有人指向被调用方。这无所谓对错,但必须让整个项目保持一致,并且在图例里写清楚。否则看图的人,永远不敢确定你的箭头到底代表调用还是被依赖。
实线与虚线的语义差异也应该隔离清楚。我自己的惯例是:实线表示确定的数据或控制流,虚线表示异步、回调、配置依赖或可选的扩展关系。这两个语义绝对不能混着用。如果你发现一张图里虚线有时候是“异步”,有时候是“不重要”,那建议直接删掉一半虚线,换成备注文字。
连线上的标签,能不加就不加。如果两三个节点之间的关系必须靠标签才能看懂,说明主流程没有把关键状态点画出来,可能需要增加中间节点,而不是靠线标签来解释。
3.3 分组与留白是图表的“段落”
段落感是图表设计里最容易被忽略的一层。文字有段落,图表同样有。分组容器(泳道或背景色块)用来告诉读者“这些节点属于同一个边界”。比如在一个跨部门协作流程图里,可以把市场部、产品部、研发部分成三条泳道,那么角色边界一目了然。
但分组不要滥用。我见过一张图,五个节点每一组都套一层背景色块,结果背景色叠在一起,反而看不清哪些节点归属于哪一层。正确做法是:只有当分组信息确实能帮助读者快速定位时才画容器,否则就别画。
留白也是设计的一部分。两排节点之间如果挤得密不透风,连线的标签根本没有地方放。你可以在定稿前,把所有节点之间的距离统一拉大一点,视觉质量会立刻提升一个档次。很多人以为丑是因为颜色不够鲜艳,其实大部分时候是因为挤。
4. 从反面案例到合格图表:一张架构图的完整修改过程
4.1 一张典型问题图的病灶清单
空讲原则不容易落地,我拿一个很常见的场景举例:新来的同学画了一张订单系统架构图,投稿到技术文档里。这张图乍一看内容齐全,但所有人审查时都表示“看不懂从哪里开始读”。
问题主要出在四个方面:
- 所有节点毫无规律地等间距排列,没有主次层级,主流程被淹没在边角料里。
- 有七种颜色,每种颜色都只是装饰,并没有稳定的语义。
- 箭头方向有的朝左有的朝右,有的实线有的虚线,而且没有图例。
- 节点标签非常冗长,比如“处理用户下单请求并校验库存与优惠券信息”这样一个节点就占了大半面积。
先别急着渲染。任何图定稿前,都可以过一遍自检清单。这张问题图在自检时几乎每一条都不达标,所以不是“画得不够认真”,而是从一开始就没有按信息层级来设计。
4.2 修改步骤:先拆信息层级,再定视觉语法
我陪他把这张图改了一遍,前后大概只用了半小时。修改不是微调,而是重构。
第一步,把所有实体按“主链路、支撑链路、外部依赖”分三层。主链路是下单到支付成功这个核心流程,支撑链路是库存扣减、优惠券校验、消息通知这些与主流程靠近但旁路的逻辑,外部依赖是支付网关、用户中心、商品中心这些外部系统。
第二步,统一形状语义。主链路的服务用圆角矩形,数据库用圆柱体,外部依赖用带阴影的直角矩形,这样读者一眼就能通过形状划分出边界。
第三步,确定方向。整张图严格从上向下展开,主链路占中轴最显眼的位置,支撑链路放在左右两侧,外部依赖放在最外层。所有箭头统一朝下或朝右,遇到返回消息或异步回调,用反方向的虚线,并且在图里加一个简短的图例说明。
第四步,简化文字。所有节点改成“名词短语”,比如“订单服务”“库存服务”“支付回调”,复杂描述挪到文档正文或图下方的注脚里。
修改后的效果显而易见:任何读者第一次看到这张图,视线会自然落在中轴的主链路上,5秒内就能说出系统大致做了什么事。这不是画图技巧变好了,而是信息层级清晰了。
4.3 定稿前可以过一遍的自测问题
后来我把那次修改的审查逻辑沉淀成几个自测问题,每张图发布前都让作者自己过一遍:
- 不读任何说明,只看图,能不能在5秒内说出主流程?
- 图里所有同一种颜色、同一种线型,是否有完全一致的语义?
- 去掉装饰性元素(比如背景渐变、阴影、多余的视觉花样)后,信息是否还成立?
- 把所有颜色去掉,打印成黑白稿,还能不能分清主次和边界?
- 节点文字是否存在超过15个字的长句?
其中最后一条大家最容易忽略,但它其实是整张图可读性的关键指标。节点文字越长,读者越难快速扫视,图的价值就越低。
5. 让图表可维护:用代码化图表设计对抗文档腐化
5.1 为什么文档里的图三个月后全是假的
很多团队的技术文档,架构图永远是刚发布那天最准确,三个月后开始失真,半年后基本没人敢信。原因很简单:手工拖拽的图,每次修改都要重新调整布局。改一个节点,往往要连带挪动七八条线,沉重的维护成本让图更新永远落后于代码变更。
要让图表持续保真,最好的做法不是“大家勤快一点”,而是把图变成代码。代码化图表的设计思路是把“图”当作一种领域语言:节点和关系由文本定义,布局交由引擎自动计算。你不需要再去拖文本框,只改一行依赖关系,重新生成一下图,所有连接都会自动更新。
5.2 一个 Graphviz DOT 示例:依赖关系图怎么写
我平时最常用 Graphviz 处理依赖关系。下面是一个极简的 DOT 代码示例:
digraph G { rankdir=TB; "用户服务" -> "订单服务"; "商品服务" -> "订单服务"; "订单服务" -> "支付回调" [style=dashed]; "支付回调" -> "通知服务"; "订单服务" -> "消息队列" -> "通知服务"; }这段文本表达了两张图想要的信息:订单服务依赖用户服务和商品服务,支付成功会产生一个消息,通过消息队列通知到通知服务。你不需要操心布局,Graphviz 会自动把它排成一棵清爽的树状结构。如果以后想加一个新的依赖关系,只需要在文本里多写一行,所有维护动作都在版本控制里被完整记录。
代码化图表最大的隐藏价值是 diff。当团队评审一段 Graphviz 代码时,可以精准看到这次改动到底是“订单服务多依赖了一个用户服务”还是“某个节点改了个名字”。这在手工绘图工具里几乎不可能实现。
5.3 从“画图”到“写图”的日常工作流
代码化图表并不意味着你要抛弃所有 GUI 工具。实际工作中,我建议按下面这个流程把代码化嵌进日常:
- 需求分析阶段,用白板或 Excalidraw 画草图,重点是快,不追求精确。
- 关系稳定后,把草图里的实体和关系转写成 Graphviz DOT 或 PlantUML 源码。
- 把源码放进 Git 仓库,和代码一起走评审流程。
- 通过 CI 流程自动生成 SVG 或 PNG 图片,嵌入到文档系统或发布到内网知识库。
这套流程的最大好处是图随代码变。需求变更改代码的同时,顺手把 DOT 里的依赖关系改掉,文档永远不会烂到没法恢复。这也是我认为 diagram-design 真正值得投入精力的方向:不是画一张好看的图,而是设计一套可持续更新的图形表达系统。
5.4 什么场景不应该强行代码化
当然,代码化不是万能的。我踩过很多次“为了代码化而代码化”的坑,总结出不适合转成代码的几类场景:
- 快速头脑风暴:思维还没成型,频繁拖拽比改代码更符合人的直觉。
- 给客户看的精美示意:需要大量手动微调版式和阴影效果,代码化工具做不到这种像素级控制。
- 复杂网络拓扑图:节点之间连线过于复杂,引擎自动布局容易变成一团乱麻,人工整理效率更高。
工具之间不是替代关系,而是不同生命周期里的不同选择。最怕的是团队只认一种工具,把适合白板的讨论强行变成写代码,或者把所有文档图都拖成手绘图,最后都卡在使用体验和维护成本上。
6. 我在项目里反复踩过的几个图表设计坑
6.1 在一张图里塞了整个系统架构
最容易犯的错,就是想用一张图覆盖全部信息。以为画得越全越专业,结果阅读体验极差,视觉上到处都是信息,等于没有信息。
我现在的原则是:一张图只讲一个核心问题。如果是系统全貌,就画高层的系统上下文图,只画外部角色和系统边界,不画内部细节;如果是内部服务流程,就只画本次需要讨论的那条主路径,无关模块全放到注释里。信息密度不是越高越好,而是越聚焦越好。
6.2 颜色在投影和黑白打印下全部失效
颜色是图表设计里最容易被高估的工具。会议室投影仪偏色,打印出来的文档基本都是黑白,如果一张图的主次完全靠颜色区分,一旦颜色失效,图就变成灰度的一团。
所以我现在设计图表时,一定会事先假设“颜色不可用”。层级关系靠容器、字号、形状和粗体文字来表达,颜色只承担状态强调。比如异常流程用红色、成功链路用绿色,即使去掉这些颜色,图里的结构依然可以通过形状和位置看懂。这条原则能救很多图。
6.3 导出图片发到文档里变成马赛克
辛苦画完的图,导出时选错了格式,最后在团队群里看到的就是马赛克。最常见的原因是导出了低分辨率 PNG,或者文档编辑器对图片做了压缩。
我的建议分两种情况:如果图最后会嵌入到可交互的网页或文档系统里,优先导出 SVG 矢量图,任何缩放都不会失真;如果场景强制要求位图,比如放到 Word 里,就导出至少 2 倍分辨率的 PNG,同时检查长宽比,不要让图片被拉伸变形。图形细节清晰的图,才配得上前面的排版功夫。
6.4 多人协作同一张图,最后变成互相覆盖
手工绘图工具在多人协作上有一个隐形问题:没有可靠的冲突处理机制。两个人同时打开同一张图,各自修改后保存,后保存的人覆盖先保存的人,这种事故我见过太多次。
后来团队里凡是需要长期维护的图,基本都迁移到了代码化方案。哪怕不是代码化,也至少要约定“一个人负责某一类图”的归属权,避免多人同时编辑同一张图。协作的本质是职责边界清晰,不是所有人都能改才叫协作。
图表设计走到最后,拼的已经不是“会不会用工具”,而是“有没有一套稳定的表达纪律”。工具更新换代很快,今天流行的画板,过几年可能就没人用了;但节点、连线、分组、图层关系、信息层级这些基本设计元素,永远都不过时。我在实际项目里发现,真正能让一张图活下来的,不是它画得多好看,而是它是否容易被更新、是否经得起团队反复查看。如果你只记住一个习惯,那就从“动笔前先写一句图的目标”开始,这一句话能帮你省下后面无数次的返工。