做技术久了,你会发现一个很扎心的规律:大部分系统不是写垮的,而是扯皮扯垮的。需求评审的时候你一言我一语,边界模糊、依赖不清、改来改去,最后代码写得跟迷宫一样。
我自己写代码前有个习惯:先画图。不是那种应付汇报的流程示意图,而是真正能表达边界、依赖、调用关系的设计图。这个习惯帮我省了太多不必要的返工。
今天这篇想聊聊我理解的 diagram-design——不是讲某个绘图软件怎么用,而是讲清楚一张能用于评审、能指导开发、能长期维护的图表到底应该怎么设计。核心就一句话:图表设计不是画得好看,而是把事情说清楚。
这篇内容适合谁看?想用图表达系统设计但总画不好的后端工程师,被架构图折磨过的前端同学,需要跟开发对齐需求的产品经理,以及所有觉得“图表只是画图工具”的开发者。我会结合自己踩过的坑,讲图表的思路设计、工具选型、落地实操、排查技巧,还有怎么让图真正融入开发流程,而不是画完就当个文件躺在那吃灰。
1. 图表设计的第一步:动手前先想清楚四件事
很多人画图有个通病,打开工具就开始拖框框、拉箭头。鼠标点得飞快,半小时后画出一张五颜六色的大网,自己看着挺满意,旁边同事凑过来一看,完全不知道重点在哪。
我现在的做法是反过来,先不碰工具,拿张草稿纸把四件事写清楚:给谁看、主链路是什么、选什么图种、边界在哪里。这四件事想清楚了,画图就是水到渠成的事。
1.1 受众决定取舍:图是画给谁看的
同样的系统,给三类人看,画法完全不同。
给决策者看,比如你的技术主管或者产品负责人,他们要的是宏观视图:系统分哪几块、模块之间什么关系、关键链路是否合理。这类图上不需要出现服务名、接口名,更不需要细节到数据库字段。画得太细,反而让他们陷入局部细节,抓不住重点。
给协作者看,比如同一项目组的后端、前端、测试,他们要的是接口边界:调用关系是什么、数据怎么流转、异常分支怎么处理。这种图要具体到能对着写代码,该标接口路径就标接口路径,该标注同步异步就要标注。
给自己看,比如你正在设计一个新模块,图是你的思考草稿,怎么乱都可以,重要的是能把脑子里的想法可视化成结构,帮助自己发现逻辑漏洞。
我见过最典型的反面案例,有人把一张架构图画得跟系统手册一样,每个服务、每张表都清清楚楚,结果汇报的时候主链路反而看不出来。给领导看他们要的是决策信息,你塞给他们的全是噪音。给同事看你要的是什么?是能对齐的协作信息,不是抽象的云概念。
所以第一件事永远不是选工具,而是问一句:这张图画出来,第一个打开它的人是谁。
1.2 信息层级:让视线跟着主链路走
一张合格的图表,应该在三秒钟内让读者抓住主干。这就是信息层级问题。
信息层级的关键是视觉权重。在图表里,视觉权重可以通过位置、大小、颜色深浅来调节。核心链路放在画布正中央,用实线加粗;辅助链路放两侧,用普通实线;基础设施、外部依赖这些背景信息放边缘,用浅色或虚线。
一个很好用的原则:如果把颜色全部去掉,只看线条粗细和位置分布,依然能辨认出主链路,层级就对了。我第一次画架构图的时候,恨不得每个框都填满颜色,结果整张图红一块绿一块,像圣诞树一样,核心链路完全被颜色埋没。后来学乖了,主题色最多用两个,一个标主链路,一个标告警或风险点,其余全部黑白灰。
层次设计还有一个实用技巧:相邻的模块放在一起,用背景色做成一个隐性分组,而不是靠画一条巨大的虚线框。虚线框在屏幕上看着还行,一旦打印出来或者投到大屏上,经常糊成一团。用统一的背景色块做分组,任何场合都不会认错。
1.3 图种认知:别把什么图都画成架构图
图表设计最容易被忽视的一点,是图种选择。不同的关系模型,应该用不同的图种来表达。
架构图表达的是静态结构,核心是分层和依赖。你有一个前端、一个后端、一个数据库,它们之间怎么组织,适合用架构图。流程图表达的是时间顺序,核心是步骤的前后关系。用户下单到支付回调之间经历了什么,适合用流程图,也可以用时序图把参与方之间的消息交互画出来。
时序图表达的是跨对象的交互顺序,重点在“谁在什么时间向谁发了什么消息”。状态图则是表达一个对象在不同条件下如何迁移状态,这跟架构图完全是两回事。
我见过最典型的错误是用架构图去表达业务流程。业务流程的核心是有条件分支、有先后顺序的,而架构图默认表达的是静态依赖。把流程硬塞进架构图里,结果就是一张图又像架构又像流程,谁看谁迷糊。
选图种之前,先问自己一句话:这张图表达的重点到底是结构、顺序还是状态?结构找架构图,顺序找流程图,跨对象交互找时序图,状态迁移找状态图。选对了图种,内容就成功了一大半。
1.4 边界意识:一张图只讲一件事
我早期画图必犯的毛病,是想把所有信息都塞进一张图里。接口、数据表、缓存结构、部署拓扑、监控告警,全堆在一张画布上,觉得这样才叫“全景图”。结果就是,任何一个人打开这张图,都得花十分钟去理解它到底在讲什么。
后来被一位前辈点醒:一张图只说一件事。你要画的是订购流程,就只画订购流程;订购流程里涉及的模块依赖,在图上简单标注即可,不要展开;模块内部细节如果有必要,画第二张图。大构思类的全景图仍然有价值,但那是放在文档首页给人建立整体认知的,不是拿来指导开发评审的。
一张图的信息承载量,以能不费力看明白为上限。图里超过七成的内容是支撑信息,那就说明主链路已经被稀释了。这时要么切分视图,要么简化描述。敢于对细节说“另起一张图”,是图表设计走向成熟的一个标志。
2. 工具选型解析:从白板到文本化,我踩过的工具坑
工具是很多人最纠结的部分。今天这个画图工具加了个 AI 功能,明天那个在线白板又出了新模板,看得人眼花缭乱。以我的经验来说,工具没有那么重要,但工具的思路选错,确实会拖累效率。
2.1 从白板到文本化:我的工具演进路径
我做技术方案的头两年,一直用鼠标在桌面上拖框框。说实话,刚开始觉得挺方便,什么都能画。但用了半年后发现一个致命问题:这种图形化工具画出来的文件,在项目里根本没法维护。
首先是不可 diff。一张架构图,同事把模块 A 挪到模块 B 旁边,然后导出新图片发给你,你不会知道他到底改了什么。你用肉眼对比两张图片,看半天也看不出区别。其次是协作冲突,两个人在线编辑同一个白板,经常互相覆盖。最尴尬的是,图一旦随着项目演进需要更新,绝大多数人的选择不是去改图,而是重新画一张。
这个痛点让我转向了文本化图表工具。核心思路是:用代码写图,然后通过渲染引擎生成图片。图的源代码放在项目仓库里,谁改了内容,提交记录里清清楚楚。两个人改同一张图,合并冲突可以直接处理,就像改普通代码一样。
转向文本化之后,我才真正理解什么叫“图表即代码”。一张架构图的本质,不过是一组节点和连线的结构化描述。既然它是结构化的,那它就应该用文本来描述,应该进版本库,应该接受评审,也应该在项目演进时被持续维护。
2.2 主流工具横向对比与选型逻辑
市面上常见的方案大致分三类:纯图形化工具、文本化工具、混合型工具。各有各的适用场景。
纯图形化工具适合快速画一次性的示意图,比如方案讨论时随手画个大概结构,不考虑长期维护。文本化工具适合表达明确的结构关系,放仓库里长期维护,参与了持续开发流程。混合型工具则试图兼顾两者,比如常见的绘图白板软件,但现在也支持导入一些格式,不过在我看来,越是试图兼顾,越容易两头都平庸。
你看下面这个简单的对比表,基本反映了我的选型逻辑:
| 维度 | 纯图形化工具 | 文本化工具 |
|---|---|---|
| 上手速度 | 快,拖拽即所得 | 稍慢,需要记住少量语法 |
| 绘图的自由度 | 高,可以自由摆放 | 偏低,受布局算法约束 |
| 可视化美观度 | 高,可以精细调整 | 中规中矩,胜在工整 |
| 版本管理 | 差,二进制文件不可 diff | 好,纯文本可 diff 可 review |
| 团队协作 | 容易互相覆盖 | 用版本控制天然支持多人协作 |
| 长期维护 | 差,更新成本高 | 好,改几行文本就能更新 |
| 适合场景 | 一次性示意图、轻松交流 | 仓库中的长期文档、架构评审 |
我自己现在的习惯是:凡是进仓库、跟随代码一起维护的图,一律用文本化工具;凡是临时画个草图跟同事对齐想法,随便用什么白板、画板,画完截图就行,不用纠结。
2.3 文本化图表的边界:什么场景不该用它
文本化工具不是万能药,它也有明显的边界,搞清楚了才不会硬上。
第一类是强调视觉冲击力的图,比如给外部汇报做的漂亮大图,需要精细控制每个元素的位置和配色,文本化工具做起来就很别扭。不是说做不到,而是你会花大量精力在跟布局算法较劲上。
第二类是自由绘制的地图、示意图、手绘图,比如办公室的网络拓扑图,带地理位置信息的设备分布图,这类内容本质上是空间关系,不是结构化关系,用文本化工具只会把自己绕晕。
第三类是需要嵌套复杂图形、非标准形状的图,比如某些特殊的数学结构,在文本化工具里很难表达。
我给自己定了一条原则:如果一张图的核心价值在于结构关系、逻辑关系,那么它可以用文本化;如果核心价值在于视觉表达、空间位置,那还是老老实实用图形化工具。
3. 实操:用文本化描述画出一张可评审的架构图
理论聊了不少,这部分直接上实操。我以一套典型的文本化图表语法为例,带你从零写出一张可以进评审会议的架构图,不是渲染意义上的好看,而是逻辑上次次分明。其他文本化工具思路都类似,学会一种,别的基本能触类旁通。
3.1 环境准备:三分钟跑起渲染环境
写文本化图表最小的环境就是文本编辑器加一个渲染插件。我最早是直接在编辑器里写好语法,然后复制到在线渲染器里看效果。这种方式虽然要来回切换,但胜在零配置,改完立刻能验证。
后来项目里开始大量使用图表,我把它集成进了文档体系,文档里直接写代码块,渲染时自动转成图片。这一步做完之后,图表就真正成了文档的一部分,杂乱无章的截图不会再出现了。
不管用哪种方式,核心环节是一样的:你写的是结构化文本,渲染引擎负责画图。遇到写好的语法渲染不出来,优先检查语法,而不是怀疑工具坏了。
3.2 第一张会话流图:方向、节点与连线
从最简单的开始。假设我要描述一个用户通过网关访问业务服务的会话流程,第一版代码长这样:
graph TB U[用户] -->|HTTP 请求| G[网关] G -->|转发请求| S[业务服务] S -->|查询| DB[(数据库)]逐行解释一下:graph TB 表示这是一张从上到下(top to bottom)布局的图。U、G、S、DB 是节点标识,方括号里是显示的文本。带括号的 [()] 表示数据库风格节点。箭头和连线表示依赖方向,箭头上的文字用管道符写法表示连线说明。
这段代码渲染出来,就是三个节点从上到下依次排列,每个箭头旁边还有一行说明文字。这么一张简单的图,如果你愿意,其实已经超过了很多人画的第一版架构图。因为它的结构是清晰的:请求从哪进、经过哪、最终落到哪,一眼就能看出来。
3.3 加分组加注释:让图从“能看”到“能评审”
单张流程图画好不难,难的是把整张架构图组织得能评审。接下来我把这个例子扩展成一个更加完整的架构片段,加入分组结构和注释。
graph TB subgraph 接入层 G[网关] W[负载均衡] end subgraph 业务层 S1[用户服务] S2[内容服务] end subgraph 存储层 DB1[(用户数据库)] DB2[(内容数据库)] end U[用户] -->|HTTPS| W W --> G G -->|路由| S1 G -->|路由| S2 S1 --> DB1 S2 --> DB2这里用 subgraph 关键词给节点做了分区:接入层、业务层、存储层。渲染出来之后,三块区域各自被一个色块包住,区域之间有清晰的视觉边界。这张图拿去评审,别人第一眼看到的是三个层次和它们的依赖顺序,再细看每个层次里具体有什么服务。
这个版本相对前一版最大的进步,是把图的粒度从节点级别提升到了模块级别。评审的时候,大家讨论的单元是“这一层是否合理”,而不是“这个节点叫什么名”。
3.4 命名规范与统一风格:给别人维护留条活路
代码写多了你会知道,命名规范有多重要。写图也不例外。节点的标识符,也就是那串英文字母,本质上就是图里的变量名。它不需要显示在最终图片上,但不代表你可以乱起。
我的习惯是:节点标识用有业务含义的英文缩写,比如用户服务用 user_svc,数据库用 db_user,而不是 a、b、c 这种纯无意义的标识。原因很简单,图是要进版本库的,两周之后你自己打开这张图,看到 a、b、c,谁还记得哪个是哪个。就算渲染出来显示的是中文,修改代码的时候你还是要面对这些原始标识。
布局方向也值得统一。我一般遵循两个原则:架构图多数用从上到下的方向,表示上层依赖下层;流程类图多数用从左到右的方向,表示时间顺序。整个项目里的图保持同样的方向习惯,看起来就会很统一。不要这张图从上到下、那张图从下到上,读者每次都得重新适应。
注释也很重要。文本化图表大多支持给节点写注释,建议把你的设计意图留在注释里,而不是只画图不留理由。注释里写清楚为什么这个模块放在这一层,为什么这两个服务之间需要这条连线。图的表达能力是有限的,设计意图靠注释和正文补充。
4. 常见问题与排查技巧实录
画图这件事,踩坑是难免的。我自己画了这么多年图,依然会在一些典型问题上翻车。这里挑几个高频问题,分享一下我的排查思路和解决经验。
4.1 信息过载:图越来越乱的应对策略
很多人的图画到第三版就变成一团乱麻,根本原因是想表达的内容超出了图能承载的范围。
排查思路是先数一数图上有多少个节点。如果节点超过二十个、连线超过三十条,这张图大概率已经不适合直接阅读了。应对方案不是把图继续缩小,而是切分视图。
我通常这样切:把原图按功能区拆成三张子图。第一张是总览图,只画模块级结构和依赖;第二张是核心链路图,画主要流程走查;第三张是细节图,给需要展开的模块单独画。三张图分别挂在文档的不同章节,各有各的定位。
这里说的“拆”不是把一张图分成三张碎片,而是每张图都有独立的意义和价值。总览图帮助建立整体认知,核心链路图帮助理解业务逻辑,细节图指导实际开发。这样拆完之后,每张图承担的职责都明确了,反而比一张大而全的图更容易维护。
4.2 连线成蜘蛛网:依赖关系的表达艺术
图画到最后全是连线,满屏都是交叉线,这是最打击信心的事情之一。交叉线的本质是布局不合理,根源则是不加节制的依赖关系表达。
这里有个关键认知:架构图上的线条,不是越详细越好。两条服务之间的实际调用可能有几十个接口,但架构图上只需要一条线,线上标个“RPC 调用”就足够了。你画的是关系,不是每一次调用。把调用细节留给接口文档或代码去表达。
如果连线交叉还是很多,我的经验是调整节点位置。文本化工具的自动布局算法通常已经很智能,但你必须给它清晰的层级结构。最常见的做法是在 subgraph 中定义模块边界,层级分明之后,渲染引擎会自动把线条整理得更加清晰。
还有一种连线交叉的常见原因:把不在同一层级的节点强行连在一起。业务服务直接连数据库,这本身没错,但如果你同时又画了缓存服务连数据库、消息队列连数据库、日志服务连数据库,那数据库节点周围就会变成一个蜘蛛网。这种情况下,考虑加一层统一的数据访问层,把多个依赖收敛成一条线。
4.3 渲染翻车:语法错误与编码问题的排查
文本化图表最常见的翻车是渲染失败。排查是有套路的:
先看有没有明显的语法错误:检查方括号有没有闭合,检查箭头方向符号是否写错,检查 subgraph 的 end 关键字有没有漏掉。语法检查一遍之后,再看看是不是字符编码的问题。你在源码里写中文注释,工具默认解析不了,结果字符显示成乱码或者渲染直接失败。这种情况要设置正确的全局编码声明。
还有一个我常踩的坑:不同渲染器对同一段语法的支持程度不一样。在版本 A 里能正常渲染的语法,放到版本 B 里可能就有兼容问题。这类问题排查比较费时,我的做法是只使用基础特性,稳定可靠比花哨的扩展能力更重要。
遇到渲染异常,千万不要对着完整的大图盲改。把图的内容裁到最小复现单元,先定位是哪行语法出了问题,修好验证之后再放回大图里。写图和调 Bug 的道理是一样的,减小排查范围是最高效的路径。
4.4 图与代码脱节:让图表跟上项目演进
比渲染失败更麻烦的是图与事实脱节。项目重构了,代码改了,图还停在三个月前的样子。我看过太多仓库里有图,但没人敢用的状况,因为大家都知道图已经过时了。
破解这个问题的思路是把图放进开发流程的必经之路。我现在的做法:图作为源代码的一部分参与评审。涉及架构变动的需求,先把图改了,图通过评审再动代码。代码合并的时候对应图也同步合入。这个流程顺了之后,仓库里的图和代码基本能保持同步。
凡是代码仓库里的图,我都倾向于文本化描述并放在对应模块的说明文档里,跟着版本库一起走。二进制图片放入仓库很难 diff,难以审查,而文本化实现要修改只需改几行代码,提交记录一目了然。有一次我重构一个模块的依赖关系,改了十来行图的源码,提交记录里清清楚楚写着改动范围,Review 的人一眼就看懂了意图。
5. 进阶:把“能用的图”变成“好用的图”
画图做到不出错不难,难的是让图真正好用。这一部分我聊几个让我自己受益比较大的进阶习惯。
5.1 细节考究:颜色、注释与图例的统一
一张图好用的前提,是读者能无障碍地解读。很多人默认别人看得懂自己图里的颜色含义,这个假设在绝大多数情况下不成立。
我的做法是给图配一个微型图例。图例不需要多复杂,一行说明就好:蓝色实线是同步调用,绿色虚线是异步通知,红色粗线是本次改动的链路。有了图例,读者就不需要靠猜来理解你的颜色系统。图例本身也是绘制在图上,不随上下文变化。
颜色使用也有门道。我一直提醒自己,颜色是语义的载体,不是装饰。如果你给两个没有语义差别的框填上两种颜色,读者就会开始思考“这两个有什么区别”,然后发现没有区别,白白产生认知负担。
节点尺寸和字号的统一也值得注意。同类节点保持相同尺寸,同类文字保持相同字重。这一点看起来琐碎,实际上是影响整张图专业度的关键指标。一堆大小不一的框排在一起,视觉上先输了,内容的可信度也会跟着打折。
5.2 图表入文档:把图融入日常工作流
图不能是孤岛,要跟文档形成整体。我的文档结构通常是:先有总览图建立全局认知,然后按模块展开详细说明,每个详细说明的章节都配有该模块的子图。读者从上往下读,可以从整体钻到局部,全程不掉线。
图表在文档当中还有一个很实际的作用:它是讨论的锚点。评审会上,与其争论“这个服务应该属于业务层还是基础层”这种抽象问题,不如直接在图上指出来。有图作为讨论锚点,每个人的意见都会落到具体坐标上,会议的效率会明显提升。
这套做法落地之后,你会发现图在团队里不再是“画完就完”的摆设,而是一个持续演化的活文档。有人调整了服务拆分,图同步更新;有人新增了调用链路,图同步补充。
5.3 图驱动开发:画图先行,代码后写
最后一个进阶习惯,是我个人收获最大的:图驱动开发。接到一个复杂需求,千万别急着动手写代码。先把需要涉及的模块、依赖关系、关键链路画成图,拿图去做设计评审,评审通过后再进入编码阶段。
这么做有一个非常实际的好处:很多设计缺陷在画图阶段就暴露了。你画图的时候,把 A 服务和 B 服务连起来,结果发现 B 服务依赖 C,而 C 在系统里根本不存在,或者 C 是 B 的下游,形成了循环依赖,那你可能在十分钟内就能发现并纠正这个问题。要是直接写代码,这种问题可能要等到联调阶段才会炸出来。
我经历过一次印象很深的项目重构:老系统的模块之间纠缠不清,团队讨论了好几次都没定出方案。后来我花了两个晚上,把老系统按当前运行的实际情况把依赖图画出来,画到一半就发现了两处循环依赖和三处重复建设。基于这张图,重构方案很快就达成了共识,没有在会议上翻来覆去地吵。
这也是为什么我现在把图表设计看得这么重。它的本质是低成本试错,画在纸上的错误,改起来成本最低。
最后再分享一个我个人特别受用的习惯:每张图竣工之后,我问自己一句话——如果一个人完全不了解这个项目,拿着这张图能看懂多少?如果答案低于六成,说明图里少了关键的上下文;如果超过九成,大概率是老头乐,信息浪费了。合适的密度,就是让该看懂的人,快速看懂该懂的部分。
画图是一项技能,跟写代码一样,练多了才有感觉。我始终相信一个朴素的原则:先能把图画明白,才可能把代码写明白。希望大家都能让图表真正产生价值,而不是变成仓库里的又一个僵尸文件。