☰
diagram-design实战:用代码画出生动有用的架构图
2026/10/17 12:11:04 网站建设 项目流程

很多人以为画架构图是天赋活,其实它更像翻译活。把系统拆成一块块积木,再把积木之间的依赖、流程、时序讲清楚,整个过程就是 diagram-design。我最初把它当一个小工具链来折腾,结果越做越发现,真正难的不是画图软件,而是怎么让图在团队里持续存活、持续有用。这篇文章不讲虚的,直接把我做 diagram-design 的心法、实操套路、踩坑记录全部摊开,适合那些被“图越画越乱、没两天就过期”困扰的开发者、产品经理和技术文档维护者。全程用大白话,看完能直接用。

1. 先想明白:为什么画不出好图,往往不是手的问题

1.1 图是给人看的,不是给工具看的

我见过太多人一头扎进画图工具,先调半天配色,再拽一堆框框,最后画出来一张“自我感动”图:信息密度极大,五颜六色,自己看得懂,别人一脸懵。这种问题的根源是顺序反了。diagram-design 的第一步不是打开工具,而是先回答一个问题:这张图要帮谁,在哪个场景下,解决什么困惑。

举个例子。同样画一个系统架构,给新入职的同事看,你要突出的是“服务边界”和“调用关系”,中间件细节先收起来;给老板做汇报看,你要突出的是“模块价值”和“演进路线”,底层技术栈反而要淡化;给 SRE 团队排查故障用,你要突出的是“链路节点”和“监控埋点”,画得越朴素越好。同一套系统,至少三种画法。先想清楚受众,图才有存在的意义。

1.2 图文匹配:先定用途再定工具

画图前先给图分类。diagram-design 里最常见的几类用途,我做了个简单的对照表,方便你直接判断该画哪种:

图种解决什么问题典型场景推荐的表现方式
架构图系统里有哪些模块,模块之间怎么连接服务治理、方案评审分层布局 + 实线连线
流程图一件事从开始到结束经历了哪些步骤业务流程、发布流程、审批流程自上而下或自左而右的步骤串联
时序图多个角色之间来回调用消息的先后顺序接口设计、跨系统交互纵向时间轴 + 横向参与者
状态图一个对象有哪些状态、怎么迁移订单状态、设备状态状态节点 + 迁移箭头
数据模型图表、字段、主外键关系数据库设计、数仓建模实体关系 + 连线标注
思维导图把一个主题拆分归类头脑风暴、需求梳理树状发散

我见过不少同学把流程图画成思维导图,把架构图画成脑图,本质都是没想清楚“这张图到底要表达什么过程”。定好图种,后面所有工作都会顺很多。

1.3 一张图上信息负债的边界

“信息负债”是我的一个土说法。任何一张图都有能力上限,超过之后,看图人得花更多力气去解读,图的边际收益就变成负数。我给自己定的规则是:一张图只回答一个核心问题。如果这个问题需要超过 7 个关键节点才能讲清,我就拆成主图和子图。

以前我在某跨平台系统的架构图里,硬塞了监控、日志、缓存、消息队列、网关、权限、数据库、客户端 SDK 一共八个维度,结果每次评审都要讲解十五分钟,图成了“装饰墙”。后来拆成“运行时链路图”和“部署拓扑图”两张,问题一下子清楚了。这个思路也适合流程图:主流程超过十步,就把其中的子流程单独画一张,用子图编号引用,而不是把所有分支都堆在同一张里。

2. 拆开一张合格架构图的核心零件

2.1 角色与关系:节点的分类原则

如果把一张图拆成最小单元,无非是“节点”和“连线”。节点代表一个存在的事物,连线代表它们之间的关系。diagram-design 最关键的纪律,就是节点与连线的语义必须统一。

节点我习惯分三类:实体(比如用户、订单、数据库)、容器(比如服务、应用、中间件)、边界(比如整个系统的外部依赖或者子系统边界)。在图上用形状区分是最简单可靠的,我建议固定一种习惯,比如:

  • 数据库 / 存储类节点:圆柱体或带底线的容器;
  • 服务 / 应用类节点:矩形,圆角幅度小一点更像服务;
  • 外部系统类节点:矩形加虚线边框,强调“不归我们管”;
  • 决策点:菱形或其他形状,只在流程图里出现。

连线则分为实线依赖、虚线弱依赖、箭头单向调用、双向通信这几种。最怕的是整张图全是实线箭头,没有粗细和虚实之分,看起来就像一团麻绳。每一条连线都要能回答两个问题:谁发起,依赖的方向是什么。

这里有个我经常强调的细节:很多图把“依赖方向”画成“创建顺序”,比如 A 创建 B 就把 A 指向 B。这是两种完全不同的语义。依赖方向表示 A 背后需要 B;创建顺序表示时间前后。混在一起,排查问题的人会误判。画图时要把这两类边分开,要么用不同颜色,要么加文字标注,千万不要让读者靠猜。

2.2 布局与视线:不要让人绕圈

好的布局应该让读者的视线顺着一条主路径走,而不是来回跳跃。常见的布局有两种:自上而下适合业务流程和层级结构,自左而右适合时间序列和依赖链。diagram-design 里没有绝对标准,但有个负面清单可以参考:

  • 连线尽量少交叉。交叉不可避免时,优先保证主干链路不交叉。
  • 同一层的节点尽量对齐。节点不齐的图会让人误以为有层级关系。
  • 回环和反向箭头要显式画出来,不要隐藏在网络里。如果反向依赖过多,可能说明模块划分有问题。
  • 不要把核心链路放在图的角落里。视线经过最多的地方,应该放最重要的节点。

我见过最有问题的一类图,是“节点处处相等”——所有框一样大、一样颜色、一样边框,读者根本看不出主次。要避免这个,可以在核心节点上加粗边框,或者用深浅色区分层级。但要克制,核心节点不要超过三个,不然到处都很重要等于都不重要。

2.3 色彩与阴影的克制

这里必须泼一盆冷水:很多人画图难看,不是因为不懂配色,而是因为太想“画好看”。diagram-design 的配色逻辑是语义优先,其次才考虑美观。我通常采用一套 4-5 色的固定调色板:主色给核心服务,次级色给支撑服务,中性灰给基础设施,高亮色只用于“异常路径”或“本次改造涉及节点”。全图高亮色绝对不多于 10%。

阴影、渐变、圆角、立体感这些装饰,能不用就不用。阴影尤其坑人,在导出 PNG 或嵌入文档时经常压缩后变得一坨黑。而且很多团队用灰度打印机,彩色图打印出来直接废掉。我给团队的建议是:先做一个黑白可读的版本,再叠加颜色信息,这样在任何介质上都有底线。

2.4 标签与注释:图中文字的两种宿命

图中的文字只有两种宿命:要么是“查询用的索引”,要么是“人读的细节”。索引必须精简,比如“用户服务”“订单库”,控制在 2-6 个字,能识别即可;人读的细节,比如接口路径、超时时间、状态码,放到注释、图例或者侧边栏里,不要全塞在框内。

框内文字过多是画图大忌。我给每行模拟实践时用的原则:每个节点框内最多三行文字,超过三行就说明这个节点承担了太多职责。比如“订单服务”这个节点,你不应该在框里写“下单、支付回调、库存扣减、退款”,而应该只写“订单服务”,把职责清单放到旁边备注或文档里。读者先看清结构,想了解细节再找注释,这才是健康的阅读顺序。

3. 代码画图:把设计沉淀到仓库里

3.1 为什么要用“图即代码”

手工拖拽画图最大的问题是“不可审查”。你永远不知道这张图最后是谁改的、改了哪里、为什么改。代码画图把这个痛点解决得很彻底:图的源文件是普通文本,进入 Git 仓库,可以做 diff、可以走 Code Review、可以在 CI 里自动导出图片。这才是 diagram-design 能长期保鲜的核心机制。

刚开始团队会觉得多此一举,但坚持一段时间后收益非常明显。第一,改图必留痕,谁动过图、动了什么一目了然;第二,图和代码同库存放,版本天然对齐,不会再出现“代码已经重构,架构图还是三个月前”的尴尬;第三,新人可以通过读图的 diff 理解系统演进过程,比翻文档高效很多。

3.2 从手绘到代码的工作流

我整理了一套自己的 diagram-design 工作流,基本上一直这么用:

  1. 用白板、纸笔或在线白板先画出草稿,这一步只画结构,不纠结细节。
  2. 把草稿的结构翻译成文本描述,明确列出节点清单、边界清单、连线清单。
  3. 用某种“图即代码”工具生成初版图形,比如绘图语法描述类工具。
  4. 对照草稿检查布局和语义,调整分组、连线和样式。
  5. 提交到仓库,写清楚变更说明,和代码一起走评审。
  6. 在文档站点或 README 里引入渲染结果,同时保留源文件链接。

这套流程里最关键的是第二步。很多人直接从脑子想跳到工具写代码,结果边写边改,一团乱。先列清单,哪怕用一张表格列出节点、层级、依赖,也比闭上眼睛直接画要稳。

3.3 文本化描述的要点:节点、连线和分组

这里我不依赖某个具体工具,而是把通用的核心语法思路讲清楚。一般来说,一个文本化图的描述由三部分构成:节点定义、连线定义、分组定义。

节点定义要确定唯一 ID 和显示标签,ID 用于连线引用,标签用于展示。连线定义要写清楚起点、终点和方向,有时还要带标签说明关系类型。分组定义是为了把相关节点折叠到一个边界里,比如“前端层”“服务层”“数据层”。我常用的一种描述结构如下:

{ "title": "某跨平台系统架构图", "groups": [ { "id": "client", "label": "客户端层" }, { "id": "server", "label": "服务层" }, { "id": "data", "label": "数据层" } ], "nodes": [ { "id": "app", "label": "移动端", "group": "client" }, { "id": "web", "label": "Web端", "group": "client" }, { "id": "gateway", "label": "接入网关", "group": "server" }, { "id": "order", "label": "订单服务", "group": "server" }, { "id": "db", "label": "订单库", "group": "data" }, { "id": "cache", "label": "缓存", "group": "data" } ], "edges": [ { "from": "app", "to": "gateway", "label": "HTTPS 调用" }, { "from": "web", "to": "gateway", "label": "HTTPS 调用" }, { "from": "gateway", "to": "order", "label": "RPC" }, { "from": "order", "to": "db", "label": "读写" }, { "from": "order", "to": "cache", "label": "读写" } ] }

这种结构的好处是和代码审查思维一致,改哪个节点、加哪条边,一眼就能看出来。无论你用哪种工具,背后都是同一套抽象。在实际项目里,我通常会额外维护一份“图例说明”,把每种线型、每种颜色代表什么写清楚,放在图的旁边或文档下方,这样看图的人不需要猜。

3.4 导出、嵌入和自动化

图即代码的最终价值是能被持续集成和分发。现在多数绘图工具都支持命令行导出,常见的有 PNG、SVG、PDF 等格式。我最常用的是 SVG,原因是矢量图在文档里放大缩小不糊,而且可以直接被网页引用。如果公司内部文档系统支持外链图片,我更建议把图渲染成图片后放到对象存储里,再把图片地址嵌到文档中,这样既能独立更新,又不会让源文件暴露给不了解的人。

在自动化方面,我习惯在提交代码时触发一次文档构建任务,源文件变更后自动重新出图。这样永远保证 README 或设计文档里的图是最新版本。如果构建失败,就把任务标红,让作者先修图再合代码。这一招看起来严格,其实帮团队省了大量“图过期”的沟通成本。

4. 一个真实场景的完整实操:给某跨平台系统画架构图

4.1 业务假设与边界确认

与其空讲理论,不如走一遍实操。假设我们要给“某跨平台系统”画一张架构图,系统包含移动端、Web 端、接入网关、几个微服务、数据库和缓存。第一步不是画图,而是确认边界:这张图到底覆盖到什么范围。我通常会列一个边界清单:

  • 属于系统内部的:客户端 SDK、接入网关、业务服务、数据存储、缓存。
  • 属于外部依赖的:短信服务、推送服务、第三方支付。
  • 本次图不体现的:监控系统细节、日志链路细节、内部部署拓扑。

边界确认后,画图时对外部依赖统一用“外框+虚线”表示,内部模块用实线框。这个动作能避免读者误把外部依赖当成自家服务。

4.2 先列清单,再造结构

我会先把节点和连线写成一张关系表,这张表就是画图草稿的地基。比如:

节点所属层依赖方向依赖对象说明
移动端客户端层调用接入网关需要登录态
Web端客户端层调用接入网关需要登录态
接入网关接入层调用订单服务负责鉴权、路由
订单服务服务层读写订单库存储订单主数据
订单服务服务层读写缓存缓存热点订单查询
订单服务服务层调用支付服务第三方支付封装
支付服务服务层调用第三方支付外部依赖

这张表写完之后,结构其实已经出来了:客户端层在最顶,接入网关在第二层,服务层在第三层,数据层在最下边,外部依赖放在一侧。不要小看这个笨办法,它能帮你把隐藏依赖全部浮出水面,而不是画到一半才发现少了条链路。

4.3 用代码实现并调整布局

接着把上面的关系表翻译成绘图脚本。我以一个通用的 JSON 结构做示意,重点看它的分组和样式定义:

{ "title": "某跨平台系统架构图", "layout": "top-down", "nodeDefaults": { "fontSize": 14, "shape": "box", "strokeWidth": 1 }, "groups": [ { "id": "client-layer", "label": "客户端层", "stroke": "#666666", "fontSize": 16 }, { "id": "access-layer", "label": "接入层", "stroke": "#CCCCCC" }, { "id": "service-layer", "label": "服务层", "stroke": "#CCCCCC" }, { "id": "data-layer", "label": "数据层", "stroke": "#CCCCCC" } ], "nodes": [], "edges": [] }

这个配置文件里最重要的是“layout: top-down”,它决定整体次序。如果系统的主链路是横向的,比如在多个站点之间跳转,我会改成 left-right。画完后,第一轮渲染出来我通常会做三件事:看主干链路是否顺眼,看分组边框是否包裹住对应节点,看文字是否有重叠。哪不顺眼就在脚本里调整,而不是在成品图上用鼠标硬拖。用代码改图的好处是每次调整可回溯,改坏了可以回到上一版。

4.4 导出、评审和迭代

初版图生成后,别急着发出去。我的习惯是先邀请两个同事做“五分钟盲测”:把图发给他们,五分钟内回答三个问题——“系统分了几层”“流量怎么走的”“哪里容易挂”。如果三个问题答不上来,说明图的信息组织有问题。

盲测通过后,再把图导出成 PNG 或 SVG,嵌到设计文档里,并且把源文件的路径一并写进文档的“更新说明”,避免后人找不到原图。等系统上线新服务、调整了调用关系时,改源文件重新出图,再走一轮评审。长期下来,这张图就成为活的架构记录,而不是一次性装饰。

5. 常见坑位与排查实录

5.1 布局不听话:节点乱跑怎么办

画图时最常遇到的问题就是布局引擎给出的结果和预期不一致。你希望 A 在 B 上方,结果它在侧边;你希望一组节点靠在一起,结果被打散。遇到这类问题,我的排查顺序是:

  • 先检查节点是否被正确放进同一个分组里。分组是布局的默认约束,很多乱跑是因为节点没归组。
  • 再看是否显式指定了层与层之间的依赖顺序。依赖关系越强,布局才会越倾向于把相关节点排近。
  • 如果还是乱,就在脚本里给关键节点加位置偏好或层级权重。几乎所有文本画图工具都支持某种“可调优先级”或者手工固定坐标的机制,优先给核心链路节点固定好位置,其他节点再让引擎自由排。

我自己踩过的坑是给所有节点都加固定坐标,结果加完以后想调一个节点位置,牵一发动全身。正确做法是只锁定少量骨架节点,自由度留给布局引擎,这样整体依然可控,维护成本也低。

5.2 渲染报错与显示黑屏速查

代码画图偶尔会碰到渲染失败,常见原因就那几类:

表现可能原因处理办法
整张图没出来语法错误、括号没闭合用官方校验工具定位,看定位行附近
某个节点消失节点 ID 重复给 ID 加前缀,比如 service-order、db-order
中文显示为方块字体或编码问题确保导出环境装了中文字体,源文件用 UTF-8
线条方向反了连线的起点终点写反对照关系表逐条检查,尤其注意“依赖方向”的定义
图和预期位置差别大分组边界缺失或循环依赖太多抽象出单向的主链路,必要时拆图

中文显示问题最容易被人忽视。很多同学在本地预览正常,一放到 CI 服务器导出就变成方块,根本原因是服务器上没装合适的中文字体。解决方法是把字体文件一并放进构建环境,或者在脚本里指定一个部署环境中确定存在的字体。这个坑不踩一次很难注意,我建议在写自动化之前就把它纳入检查。

5.3 协作冲突:多人改一张图如何不打架

只要把图放进 Git 仓库,多人协作就可能出现冲突。文本画图工具的好处是冲突可以像代码一样合并,但前提是大家遵守同样的修改习惯。我的一些约定:

  • 一个文件一个模块,尽量不把多个系统的图塞在同一个文件里。图太大时拆文件,通过引用方式组合。
  • 修改节点或者连线时,保持每个节点写一行,不要一行塞多个定义,方便 diff。
  • 每次提交只改一个意图,比如“新增支付服务节点”和“调整下单链路方向”不要混在一次提交里。
  • 冲突出现时,不要凭记忆硬解。先和对方确认这个改动的业务意图,再决定保留哪个方向。

如果团队里很多人不熟悉文本画图,还有一个过渡方案:图仍然用可视化工具维护,但定期把导出的源文件快照提交到仓库作为存档。这样至少保留了历史版本,只是失去了精细 diff 的能力。等团队上手后再彻底切换。

5.4 图太大了,拆成多张的时机

什么时候该拆图,我有个经验阈值:当一张图上的节点超过 20 个,或者有读者反馈“太挤”的时候。拆图不是简单分两张,而是要顺着读者的心智拆。

比如一个订单系统,你可以拆成“下单主流程”“支付回调流程”“售后流程”三张,三张图共享同一个“订单服务”的图例与配色。然后做一张总览图,上面只有“客户端/网关/订单服务/支付/售后”五个节点,每张子图在总览图上有对应的链接。读图的人先看总览,再按需点进子图,负担瞬间下降。

拆图之后最难的反而变成了“如何保证多张图信息一致”。我会定期用脚本检查不同图之间是否有 依赖矛盾。比如子图 A 说订单服务依赖缓存,子图 B 却没画缓存,这就是不一致。现在不少团队会直接用文本方式维护,每张图指向同一个共享配置,从源头避免不一致。

6. 最后再分享几个压箱底的习惯

做 diagram-design 越久,越觉得图的价值不在画得炫,而在画得“省脑”。我最后分享几个长期好用的小习惯。

第一个习惯是维护“术语表”。每个团队都有自己的内部叫法,比如“某某服务”“某某中台”,如果没有统一术语,三张图里会出现三个名字都指同一个东西。我的做法是在仓库里放一个术语映射表,画图时必须用术语表里的规范名,别名一律不出现。刚开始会有点麻烦,但图一旦被文档、PPT、故障排查反复引用,术语统一带来的收益非常大。

第二个习惯是给图加“修改日期”和“负责人”。图一旦多人维护,一张图的原点责任必须明确。不是说要卡住修改权限,而是要有一个默认的 owner,当图出现争议时知道找谁问。很多团队图烂掉,就是因为“人人能改,人人不负责任”。

第三个习惯是定期做“图体检”。我每隔一个季度会挑一天,把所有架构图和当前线上实际情况对一遍,把已经下线、改名、重构的节点全部标出来,然后一次性更新。这个体检不要缩减成“没有 bug 就不动”,架构图是需要主动养护的资料,等它彻底过期再更新,成本会高得多。

最后一个只做不说的小技巧:画完图后关闭编辑器,打开成品图,从左上角到右下角快速扫一遍,想象自己是一个完全不了解系统的新人。如果你在哪停顿了超过三秒,那里就加注释;如果你觉得“这里为什么要这样画”,说明图形语言没表达清楚。这个“陌生人视角”的自检,每次都能帮我发现几个隐藏盲点。diagram-design 说到底不是画图技术,而是把复杂系统翻译给另一颗大脑的艺术。多画、多拆、多回看,时间会给你答案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询