画图这件事,看起来人人都能做,但真正做好的没几个。这些年我接过不少“把架构图画一下”“画个流程图”的需求,也见过太多人一上来就打开绘图软件,随手拖几个方块就开始连线,连到最后自己都看不下去,只能整张删掉重来。后来我才慢慢意识到,diagram-design 的核心从来不是“画”,而是“想”——在一张图有限的空间里,把层次、关系和边界讲清楚,这才是设计的真正意义。
所以这篇内容我打算围绕 diagram-design 的完整链路来写:从需求分析、工具选型,到一次完整的实操案例,再到布局细节和常见问题排查。整个过程都用我自己的经验和踩过的坑来说话,不整虚的。适合需要画架构图、流程图、时序图、数据流图的开发者、产品经理、运维、测试,以及所有要把复杂逻辑讲清楚的人。只要你有过一次“画出来的图被人吐槽看不懂”的经历,这篇内容应该能帮到你。
1. 先搞清楚:设计一张图到底在设计什么
我见过太多画图翻车的情况,根源往往不是工具不熟练,而是没想清楚一张图要传递什么。diagram-design 本质上是信息结构设计,不是美术设计。你画出来的每一个框、每一条线,都要对应一个你脑子里已经想清楚的信息关系。如果你自己都没理顺“谁依赖谁”“谁先谁后”,那工具再高级也救不了你。
这个道理就像搭乐高。节点是积木块,连线是积木之间的咬合点,层次则是盒子上的分类标签。积木块本身没有意义,按图纸咬合在一起才有结构。画图也一样,先有结构设计,才有视觉呈现。我见过很多人一开画板就摆框,摆着摆着发现线交叉了、逻辑乱了,然后又删掉重来,白白浪费大量时间。正确顺序应该是先把逻辑结构在白纸上用文字想清楚,再进图形工具。
1.1 图的三要素:节点、连线、层次
任何一张信息图,拆到最底层就三样东西:节点、连线、层次。
节点是“有什么”,比如一个系统、一个模块、一个步骤、一个状态。连线是“什么关系”,比如数据流、调用关系、依赖关系、时间先后。层次是“谁和谁是一组”,比如业务域、部署环境、流程阶段。这三样少了任何一个,图都会出问题:没有节点,图是空的;没有连线,图是散沙;没有层次,图是一团乱麻。
我习惯在动手画图之前,先用一段缩进列表把结构写出来。比如画订单模块的架构图,文字草稿长这样:
订单系统 接入层 页面入口 开放接口 业务层 订单服务 支付服务 数据层 订单库 缓存这段缩进文本本身就是图的结构。第二层是分组,第三层是分组里的节点。等你把这层结构理清楚,再进任何工具都只是“翻译”的问题,而不是“设计”的问题。如果连缩进列表都写不顺,说明你自己还没想清楚,先别急着画图。
1.2 先定场景再定形式:一图一主题
另一个常见误区,是想在一张图里塞进所有信息。我见过有人把部署架构、业务流程、监控链路、容灾方案全画到一张图里,结果就是一张满得密不透风的“蜘蛛网”,谁看谁懵。
图是用来帮人做决策的,不是用来展示你掌握了多少信息。一张图只回答一个核心问题,这是 diagram-design 的铁律。我给自己定过一个判断表:
| 图要回答的问题 | 适合的图类型 | 典型读者 |
|---|---|---|
| 系统由哪些模块组成,模块间怎么依赖 | 系统架构图 | 架构师、开发 |
| 一个请求从发起到返回经历了什么 | 时序图 | 开发、测试 |
| 业务流程有哪些环节和异常分支 | 流程图、泳道图 | 产品、运营 |
| 数据从哪里来、到哪里去、怎么落库 | 数据流图 | 数据工程师 |
读者不一样,他们关心的信息也不一样。老板想看系统边界和核心链路,开发想看模块接口和依赖关系,测试想看流程分支和异常路径。把“给老板看的图”和“给开发看的图”揉在一起,最后两边都会嫌弃。
所以画图前的第一个动作应该是问自己三个问题:这张图给谁看?他看完要做什么决定?哪些信息可以不放?想清楚之后再画,图基本就成功了一半。
2. 工具没选对,后面全是泪:场景化工具选型参考
工具选择这件事,我踩过的坑比画图本身还多。早期我习惯用一个在线画图工具,拖拽方便,样式也好看,但后来项目迭代,需要把图改一版,发现源文件是私有格式,团队成员根本没法协作维护,最后只能重新画一张。从那以后我对工具的要求就变成了:第一看可维护性,第二看协作友好度,第三才看画得快不快。
市面上画图工具看起来一大堆,但本质上就三个流派:拖拽式、代码声明式、数据可视化库。选错流派的后果不只是画得慢,而是你的图会随着项目演进越来越难维护。
2.1 三种工具流派怎么选
拖拽式工具的特点是所见即所得,鼠标拉一个框就是一个模块,拖一条线就是一个关系。代表就是那些开源绘图平台和在线白板。它们最适合快速头脑风暴、会议沟通、一次性草图,因为画起来几乎零学习成本,大家在一个白板上边说边画,效率确实高。但这类工具用在正式文档上有天然缺陷:图的源文件往往和工具绑定,换工具就换不了源文件;而且一旦图复杂起来,手工对齐、整理交叉线会消耗大量时间。
代码声明式工具则是另一条路。你用文本描述节点和连线,工具自动帮你完成布局。代表就是 Mermaid、PlantUML、Graphviz 这类方案。它们的最大好处是“文本即图”,改一个字重新渲染就能出新图,配合版本控制可以做 diff、做 review,这是长期维护的刚需。不足之处是布局自由度低,追求像素级精致的视觉效果时比较吃力。
数据可视化库是第三类,常见的前端图表库,适合把数据变成图表,比如折线图、柱状图、大屏可视化。它们和画架构图、流程图的逻辑完全不一样,我基本不把它们和前两类放在一起比较。
我个人建议的选型逻辑是这样的:正式文档里的架构图、时序图优先选代码声明式;会议快速沟通用白板或在线协作工具;对外宣传稿需要高度定制视觉的,再考虑设计软件手绘。图也分“草稿”和“成稿”,用草稿工具画成稿,或者用成稿工具画草稿,都会很难受。
2.2 为什么正式文档我偏爱代码生成图
我在团队里推行过一个约定:凡是进入项目文档库的图,一律用代码生成图。原因很简单,图不是一次性的,它会跟着项目一直演化,所以它本质上是一份技术资产。
代码生成图走的是纯文本,可以直接放进代码仓库,跟着代码一起做版本管理。任何一次改动,团队都能在提交记录里看到“哪个节点改了、哪条依赖没了”,代码 review 的时候也能把图和代码一起审。拖拽式工具做不到这一点,你很难说清楚上一版和这一版到底哪里变了,也很难多人并行维护同一张图。
另一个好处是批量维护。一个系统有几十个微服务,画架构图的时候如果靠手拖,光对齐框就能崩溃。但代码生成图可以用脚本从配置里批量生成节点,服务增减直接改配置文件就行。有一回我们做架构梳理,我用脚本从注册中心拉服务列表,自动生成了一张依赖图,几分钟搞定。要是手工画,光这一个动作就得画一个下午。
当然我也会保留几个拖拽式工具,用途是画那种只出现一次、用完就丢的沟通草图。但这类草图画完拍个照发群里就行,不需要进文档,进文档的图我一定要它“代码化”。
3. 从需求到成图:一次完整的架构图设计实操
光讲理念容易飘,我拿一个实际案例完整走一遍。假设要给某订单模块画一张架构图,读者是后端开发和运维,要表达的信息是:客户端请求怎么进入系统,订单服务依赖了哪些组件,数据最终落在哪里。
很多人拿到这种需求直接打开绘图工具,顺手就画了。我的第一步恰恰不是打开工具,而是打开文本编辑器。
3.1 动笔之前:用文字把结构写出来
我先写一段结构草稿,把所有要出现的节点列出来,顺便标清分组:
订单系统架构 > 客户端 Web端 App端 > 接入层 统一网关 > 业务层 订单服务 支付服务 库存服务 > 数据层 订单库 库存库 缓存 > 异步依赖 消息队列写这段文字的时候我其实在做三件事:第一,确定节点清单,确认没有漏掉关键组件;第二,确定分组边界,业务层和数据层之间不要混在一起;第三,观察连线关系,这一步能提前暴露哪些跨组依赖是“架构味道”,为后面优化留出空间。
这段草稿我一般会给同事看一眼,确认结构没有大问题再进工具。在文字阶段修改的成本几乎为零,画成图之后再调整分组和布局,代价就高得多了。
3.2 第一版:先让内容跑起来
结构确认后,我用 Mermaid 的 DSL 语法写第一版。为了让演示完整,我先把语法写出来:
graph LR Client --> Gateway Gateway --> OrderService OrderService --> PayService OrderService --> InventoryService OrderService --> DB[(订单库)] OrderService --> Cache[(缓存)] InventoryService --> InventoryDB[(库存库)] OrderService --> MQ[(消息队列)]第一版能把关系表达出来了吗?能。但问题也肉眼可见:所有节点都在一条线上摊开,没有分组的边界,谁属于业务层、谁属于数据层,完全看不出来。而且一条链路从左拉到右,节点一多图就会变得很宽,阅读时要来回扫视,很累。
这个版本只适合自己确认信息有没有漏,不适合直接放进文档。我管这叫“丑小鸭版”,画图迭代的正常起点。
3.3 迭代优化:分组、分层、标记主路径
进入优化阶段,我按三个步骤来。
第一步,确定方向。如果把图的方向从 LR 改成 TD,也就是自上而下,那么接入层、业务层、数据层的分层关系会非常清晰,读者顺着视线从上往下扫就能理解整个调用链。架构图里如果核心逻辑是“请求从上到下走完一条链路”,优先用纵向布局。
第二步,加分组。Mermaid 里用 subgraph 来划组,把客户端、接入层、业务层、数据层、异步依赖分别圈出来。这一步会让“谁和谁是一伙”立刻变得清楚:
graph TD subgraph Client["客户端"] Web App end subgraph Gateway["接入层"] API[统一网关] end subgraph Biz["业务层"] Order[订单服务] Pay[支付服务] Stock[库存服务] end subgraph Data["数据层"] DB[(订单库)] SDB[(库存库)] Cache[(缓存)] end subgraph Async["异步消息"] MQ[(消息队列)] end Web --> API App --> API API --> Order Order --> Pay Order --> Stock Stock --> SDB Pay -. 异步通知 .-> MQ Order -. 消息推送 .-> MQ Order --> DB Order --> Cache第三步,标记主路径。架构图好看不好看,关键在于读者第一眼能不能找到主干。我用 classDef 把客户端到网关再到订单服务这一条主调用链做成红色粗框,其他依赖用普通样式:
classDef main stroke:#d64545,stroke-width:2px; class API,Order main;折腾完这三步,这张图基本就能达到“发到群里没人喷”的水平。读者拿到图的第一反应是看红色主路径:请求从客户端进网关,再进订单服务;然后看分组边界,了解系统分几层;最后看虚线,了解哪些是异步动作。这个阅读顺序就是信息设计的结果。
这里有个小细节:Mermaid 的节点 id 尽量不要用中文,显示文本单独写在引号里,比如Order["订单服务"]。原因是一些版本对中文节点 id 的解析不够稳定,容易出现渲染异常。这个细节我在后面还会再提一次。
4. 好看的图是怎么炼成的:布局、配色与导出细节
内容对了,接下来就是让图看起来舒服。很多人以为“好看”是审美问题,其实大部分是技术问题,有明确的规则可以遵循。
先聊一个容易被忽略的点:自动布局工具画出来的图为什么经常乱?因为工具里的布局算法只做两件事——让连线尽量短、让节点尽量不重叠。它根本不知道你的业务语义,更不知道哪条线是主路径。所以工具永远画不出“有语义”的图,语义必须由你手动注入,方式就是分组、排序、颜色和方向控制。
4.1 布局原则:让眼睛有路可走
构图好不好,就看读者的视线能不能“有路可走”。我常用的布局规则是这几条:
第一,主链路要单向且不回头。画流程图、架构图时,先想清楚图的主线是“请求从上往下走”还是“数据从左往右流”,然后让主链路上的节点严格按照这个方向排列,不要在中间出现往回拐的线。往回拐意味着读者要倒回去看,阅读成本直接翻倍。
第二,控制每一层的节点数量。同一个分组里,节点超过四到六个就会开始拥挤。业务层的节点太多,说明这一层该继续拆分成子模块,而不是硬往一张图里塞。如果一张图超过二十个节点,我大概率会把它拆成两张,总览图只画分组和分组间的关系,细节图再展开每个分组内部的结构。
第三,跨组连线越少越好。subgraph 之间的连线是“架构味道”,线和线勾来勾去说明边界没有想清楚。每画一条跨组线,都值得问自己一句:这两个组件真的要直接依赖吗?能不能通过中间层解耦?这个问题的答案往往能倒逼架构优化。
第四,善用空白。分组和分组之间留出足够的空隙,比加粗边框和深色底更有效。留白是免费的视觉工具,画完图后退一步看,哪里挤成一团,就在哪里补空间。
4.2 配色、字体和留白:干净感的来源
配色这条我只讲一个原则:别让颜色自己“说话”超过图本身。默认文字是黑色,默认背景是白色,然后最多用两种颜色:一种强调主路径,一种标识关注点。其他的模块、分组、连线,全部用中性色。
我常用的最小配色方案是这样:
| 用途 | 样式建议 |
|---|---|
| 页面背景 | 白色或极浅灰 |
| 普通模块 | 浅灰边框、深灰文字 |
| 子图分组 | 浅色背景区块,边框略浅 |
| 关键路径 | 单一高饱和强调色,加粗边框 |
| 异步/非关键路径 | 灰色虚线 |
背景越淡越干净,强调色越少越有力。如果一张图里同时出现五六个颜色,读者的眼睛就不知道该停在哪里,图的重点也就没了。
字体方面,代码生成图要注意 fontFamily 的设置。Mermaid 里可以在配置文件里写:
config: flowchart: fontFamily: "Microsoft YaHei"命令行的渲染参数也可以加:
mmdc -i input.mmd -o output.svg --configFile config.yaml中文字体和西文字体要区分,标题用一个较大的字号,节点文字用常规字号,子图标题介于两者之间。字号层级一旦建立,图的阅读节奏就出来了。
留白方面,节点内部 padding 至少 8 到 12 像素,不要让文字贴着边框;子图内部距离边框至少 16 像素;整张图出口处留至少 30 像素空白,避免导出图片时边缘被裁掉。
4.3 导出与嵌入:清晰度和格式问题
图画完之后还有一个环节经常被忽略:导出格式。很多人直接把图截图塞进文档,结果放大就糊,或者底色和文档背景不对。
我的选择是这样:如果是网页或电子文档,优先用 SVG,一是任意缩放不糊,二是文件小;如果是需要嵌入 Word 或演示文稿,导出一张 2 倍分辨率甚至 3 倍分辨率的 PNG,不然在高分屏上看全是锯齿。命令行渲染时可以用-s 2控制缩放倍率:
mmdc -i input.mmd -o output.png -s 2 -b white-b white是强制白色背景,因为 SVG 默认是透明背景,直接放到浅色以外的底色上会露馅。导出之前先在预览里检查一遍边缘留白,裁掉不该有的部分,再放到文档里。这些细节看似小,但一张模糊、带透明底、边缘被切掉的图,足以让整体文档质感掉一个档次。
5. 常见问题与排查技巧实录
写图遇到问题在所难免,我把这些年最常被问到的几个问题和自己的排查方法整理在下面,都是实操里能直接用的。
5.1 节点太多装不下怎么办
一张图画到后来发现全挤在一起,最直接的办法不是缩小字号,而是拆图。我采用“总览+细节”的两段式:先画一张总览图,把每个大模块缩成一个分组,只展示分组之间的依赖关系;再针对每个分组单独画一张细节图,展示组内节点和连线。比如订单模块,总览图就五六个框,一眼能看懂上下游;细节图再展开订单服务内部到底调了哪些接口。
拆图看起来是“多画了几张”,实际阅读体验提升非常明显。总览图负责定位,让人先知道自己在整张图的哪个位置;细节图负责深入,让人只看某个局部而不被其他信息干扰。哪怕信息量翻倍,图也不会失控。
5.2 中文渲染出问题怎么处理
代码生成图踩得最多的坑就是中文变成方块或者乱码。Mermaid 本身支持中文文本,但字体回退策略在不同环境下差异很大,尤其是浏览器里预览正常,导出 PDF 或 PNG 的时候突然缺字。解决方法就是显式指定字体,而不是依赖系统默认。
Mermaid 里指定字体:
config: flowchart: fontFamily: "Microsoft YaHei"Graphviz 里指定字体:
graph G { node [fontname="Microsoft YaHei"]; }如果团队里有人用 macOS、有人用 Windows,字体名最好统一约定,否则同一份代码会在不同机器上渲染出两个样子。更省心的做法是全团队用同一个容器或者同一套渲染命令,保证导出环境一致。
5.3 多人协作时图被改乱怎么办
图一旦进入多人协作阶段,最大的风险不是没人维护,而是每个人都在改,最后改成了一锅粥。我的应对方式是两条:一是代码生成图必须走版本控制加 review,任何改动都留痕;二是命名规范必须一开始就定好,节点 id 用英文驼峰,节点显示文本用中文,分组命名统一叫“域名+层级”,比如Biz_Order、Data_OrderDB。
另外,每张正式文档里的图,我都会要求在图下面附一行注释,写明这张图画的是什么边界、维护者是谁、更新日期是什么。代码图的好处又一次体现出来,这些注释跟着文本走,不会丢。
5.4 预览和导出不一致怎么排查
同一份 Mermaid 文本,在某个工具的页面上预览是一种样子,命令行导出又变成另一种样子,这个坑我也踩过。原因多数是版本不一致,浏览器上跑的渲染引擎版本和本地命令行工具的版本不一样,布局算法和字体处理有差异。
我的排查顺序是:先固定版本,要么全团队用同一个版本的 CLI 工具,要么全团队都用某一个浏览器的在线预览,不要混用;然后对比预览和导出的输出参数,检查是不是 PNG 缩放倍率、背景色、字体这些配置有差异;最后确认部署环境里有没有安装中文字体,没有就会触发字体回退。这些问题排查完,九成以上的“不一致”都能解决。
我个人在实际操作中的体会是,diagram-design 最大的成本从来不是动手画的那几分钟,而是返工。返工又大多来自画图之前没想清楚、画图之后没定规范。所以后来我给自己立了一个规矩:任何一张图在动手前,先写三行字——这张图要回答什么问题、给谁看、哪些信息必须省略。三行字写完再打开工具,基本一次就能过。这个习惯我推荐你也试试。