很多团队把架构图当成“画个图”的活儿,但在我这里,diagram-design 一直是和写代码同等重要性的一门工作。做一个技术方案、梳理一个旧系统、给新人讲一遍全局,最后都绕不过一张结构清晰、信息准确的图。可现实是,绝大多数项目的架构图都活不过三个月,要么信息过时,要么没人能看懂,要么打开文件看到一堆形状和线条挤在一起,根本分不清主次。我后来想明白一件事:问题几乎都出在“设计”这两个字上——画图(drawing)只是把已知信息落下来,而设计(design)要从阅读者、使用场景、信息层级出发,先想清楚这张图到底要承担什么任务。这篇文章就把我整个思考过程、实践方法、踩过的坑完整拆开,供你直接参考。
1. diagram-design 和画图完全不同:它是一项信息架构工作
很多人一听到“架构图”就打开工具开始拖方块、拉箭头,觉得把模块摆上去就算完成。这么做最大的问题是,最后产出的只是“视觉表达”,而不是“信息传达”。我见过太多这样的案例:某团队把系统架构图画得五彩斑斓,每个模块都带阴影和渐变,看起来非常专业,但真正需要拿它做技术评审时,评审委员问一句“失败路径在哪里”,全场没有人能指着图回答。这就是典型的把 diagram 当成了绘画作品。
diagram-design 里的 design,我理解成三层含义。第一层是语义设计,要定义图中每个图形元素代表什么,形状和颜色不能想用就用;第二层是结构设计,要决定模块和模块之间的关系如何排布,是分层、是拓扑、还是矩阵;第三层是阅读设计,要控制读者的视线路径,让他在三秒内抓住主线,三分钟内找到细节。这三层都不到位,图就只是涂鸦。
这也解释了一个现象:很多图看起来信息量极大,每个节点都有文字,线条密密麻麻,但读者根本无从下手。问题不在信息量,而在信息架构。我做 diagram-design 的第一课总是让团队成员做一个练习:把一张别人画的系统图盖住图例,让另一个人复述这张图的主流程。十次有八次复述得支离破碎。这说明图形语言没有建立统一规则,图形和图形之间的关系就没有被“设计”过。
从我这几年的经验看,一张图从诞生到真正失去价值,通常要经历四个阶段:创建阶段、消费阶段、变更阶段、回收阶段。绝大多数人只考虑了创建,没考虑后续三个阶段。消费阶段要考虑别人怎么看、怎么检索、怎么引用;变更阶段要考虑这张图是手绘的还是代码化的,能不能快速同步修改;回收阶段要看它能不能沉淀成团队知识资产,而不是永远躺在个人网盘里。diagram-design 的完整闭环是覆盖这四阶段的,文章后面我会逐个展开。
2. 动手之前多看一步:这张图的读者是谁,他要做什么决策
我现在的习惯是,任何图表开始之前,先花十分钟回答一个问题:这张图要被谁,在什么场景下,用来做什么决策。
很多人觉得这个问题多余,但恰恰是这一步决定了图的抽象层级。比如同样一个“跨平台系统”,给老板看的图和给新同事看的图不可能一样。老板关心的是边界、成本和主要依赖,新同事关心的是服务有哪些入口、数据怎么流动、失败会发生在哪一层。你没法用同一张图服务所有目的,硬要塞进一张图,最后就是哪部分都不清楚。
我给自己做了一个图表设计任务卡,在任务卡上固定填几个字段:
- 目标读者:初级工程师 / 技术评审组 / 运维值班同学 / 项目管理层
- 使用场景:方案评审 / 新人 onboarding / 线上故障排查 / 架构演进记录
- 这张图要回答的核心问题(写一句具体的话,比如“用户请求从 App 发出后经过哪些服务到达数据库”)
- 变更频率:低频(季度级)/ 中频(周级)/ 高频(天级)
- 一个禁止事项:比如“不需要画到 Pod 级”,或者“不要把跨地域容灾画进来”
这个任务卡不是形式主义。它逼着我在开画前把意图说清楚。我曾经接手过一个任务,对方只丢下一句话“把系统架构图画一下”,如果我当时直接画,最后一定会被反复打回。但我先反问了三件事:这张图要不要用于故障排查?给后端团队看还是给全栈团队看?部署细节要不要展开?得到答案后,图的画法就完全不一样了。
下面是我常用的读者-信息密度对照表:
| 目标读者 | 主要关注点 | 推荐抽象层级 | 图中必须突出的信息 |
|---|---|---|---|
| 项目管理层 | 模块边界、成本、风险、里程碑 | 粗粒度,只到服务/模块级别 | 主链路、外部依赖、红色风险点 |
| 技术评审组 | 接口、数据流、一致性、异常路径 | 中粒度,可看到接口和存储 | 箭头方向、数据格式、失败处理标注 |
| 开发组成员 | 调用链、配置、依赖关系、代码位置 | 细粒度,可看到类/方法 | 完整路径、端口、协议、配置项 |
| 运维值班同学 | 部署拓扑、依赖第三方、日志入口 | 中粒度,突出节点和依赖 | 服务名称、网络边界、健康检查、告警 |
我还会在任务卡里写“这张图必须避免的情况”。比如给管理层看图,就避免出现超过十个以上的服务框;给运维看的图,就一定要把外部依赖画全,漏掉一个中间件可能影响故障定位。
做完这一步后,我会说服自己:如果用一个自然段能把这张图的内容说清楚,那就不要急着画图,而是先写这段文字。只有当你发现文字很难表达复杂关系时,图表才真正登场。diagram-design 的核心原则是“文字能说清的不画,关系绕不开的才画”。很多人恰恰相反,把图画当成逃避文字思考的捷径,结果图比文字更混乱。
3. 一张图的三个层次:几何骨架、语义主链、信息注脚
解决了“为什么画”之后,再解决“怎么画”。我自己把图表拆成三个层次,任何 diagram-design 都绕不开这三个层次,它们就像写作里的结构、主线和脚注。
3.1 几何骨架是全图的基础
几何骨架指所有元素在画布上的位置、对齐、间距和排布方向。我特别强调“骨架”这个词,是因为很多图的问题是结构松散,元素随意摆放,视觉上没有稳定感。好的几何骨架,应该像建筑脚手架,即使把所有文字都盖住,只看到方框和线条的排布,也能感受到秩序。
操作上我有一套能直接抄的规则。第一,统一在 8 点网格上布局,所有方框的横纵坐标尽量落在 8 的整数倍上,间距要么是 8、要么是 16、32。这样的好处是视觉均匀,而且后续做代码化导出或二次布局时方便计算。第二,确定一个主导方向,全图要么“从左到右”要么“从上到下”。最常见的问题是,图画到一半又回头往反方向拉线,最后箭头横七竖八。除非表达循环依赖,否则禁止反向。
第三,不要把节点平均撒在画布上。应该先找出主链路,主链路放在画布的中轴线附近,次要节点围绕主链路两侧展开。这样读者的视线自然延着主链路扫过去,而不是满画布找起点。第四,留出足够的呼吸空间。两个模块之间的最小间距至少要能放下一句十几字的说明文字,否则没法在图上加注脚。
3.2 语义主链要能被三秒识别
语义主链就是这张图里最核心的流程或依赖主线。技术图最常见的失败是没有主线。正常画法应该把主链路的线条加粗、用高饱和颜色,把次要路径的线条降级为灰色或虚线。
举个例子,在跨平台系统的架构图里,如果主线是“用户请求经过网关进入业务服务,再访问数据库”,那么这条链路的连接线应该比其他线粗两号,颜色用主题色;而服务之间的辅助同步、后台任务,就压制为虚线或不明显颜色。这个做法不是为了让图漂亮,而是为了“扫描效率”。人眼在三秒内如果找不到主链路,就会立即失去阅读信心。
我通常会用箭头粗细来表达链路权重,而不是靠加颜色数量。颜色是区分类别的,不是区分权重的。很多图颜色用得多,红色、黄色、绿色齐上,但它们之间的区别没有意义,结果读者记不住颜色语义,主链路也被淹没。更实用的做法是:整张图的主色调只有一种,主链路用这个色的加粗版本,次要链路用灰色同一种版本。读者不需要分辨很多颜色,只需要识别“谁是粗的、谁是谁灰色”这一组对比。
3.3 信息注脚是图表的元数据层
信息注脚是很多人忽略的第三层。它指的是图例、版本号、更新时间、绘制者、依赖说明等元信息。我见过大量图没有图例,符号全靠猜;也见过很多图没有任何时间戳,图上画的到底是上个月的还是去年的都无从判断。
我的建议是在图的一角固定放一个信息注脚块,包含以下几项:图标题、信息更新日期、适用版本、图例(说明颜色、形状、箭头类型)、开放性问题或 TODO 标记。这个注脚块本身就是一种设计的体现,它让图有了可维护性。更新日期尤其重要,因为它提醒读者这不是一张永不过时的图,也提醒绘制者不要几个月不去维护。
三层结构是顺序关系:没有几何骨架,图就乱;没有语义主链,图就平;没有信息注脚,图就不可信任。一次设完成得很好,缺了最后一层,这个图就会在团队里慢慢失效,遭遇“这图不知道还准不准”的尴尬。
3.4 视觉编码规则要让图形语言一致
有了三个层次,还要有统一的图形编码规则。我踩过配音的代价才明白,图形符号不能随意使用。圆角矩形我固定表示“服务/应用”;直角矩形表示“外部系统/边界”;圆柱体表示“存储”;六边形表示“消息队列”;菱形固定表示“决策或判断”。一开始全员遵守这个规则,后来评论我只能看懂图例,不用去猜这个符号是什么。
颜色也做了限定。我全图最多三种主色:一种表示核心业务域,一种表示基础设施/数据层,一种表示外部依赖,其余全部是灰色系。超过三种颜色,就说明分类维度没想清楚。字体同样要分层:图标题最大,模块名次之,接口细节最小。我保持三层字号梯度,不出现第四层,除非真的是超大画布。
下面给一个简单的 SVG 示例,演示分层布局和主链加粗的效果。在这个图里我没用任何自动绘图工具,直接手写了 SVG,最大程度控制坐标和样式。
<svg width="800" height="400" xmlns="http://www.w3.org/2000/svg"> <!-- 图例信息注脚 --> <text x="20" y="380" font-size="12" fill="#666">更新日期:2025-06-01 v1.2</text> <text x="480" y="380" font-size="12" fill="#666">实线=主链路 虚线=次要链路</text> <!-- 外部系统 --> <rect x="20" y="50" width="120" height="60" rx="0" fill="#999"/><!-- 外部调用方 --> <text x="80" y="80" fill="#fff" text-anchor="middle" font-size="14">客户端</text> <!-- 主链路服务 --> <rect x="240" y="40" width="140" height="70" rx="8" fill="#2b7bba"/> <text x="310" y="75" fill="#fff" text-anchor="middle" font-size="14">网关</text> <rect x="480" y="40" width="140" height="70" rx="8" fill="#2b7bba"/> <text x="550" y="75" fill="#fff" text-anchor="middle" font-size="14">业务服务</text> <!-- 存储 --> <rect x="680" y="30" width="100" height="80" rx="8" fill="#e0b800"/> <text x="730" y="70" fill="#fff" text-anchor="middle" font-size="14">数据库</text> <!-- 主链路加粗 --> <line x1="140" y1="80" x2="240" y2="80" stroke="#2b7bba" stroke-width="5" marker-end="url(#arrow)"/> <line x1="380" y1="80" x2="480" y2="80" stroke="#2b7bba" stroke-width="5" marker-end="url(#arrow)"/> <line x1="620" y1="70" x2="680" y2="70" stroke="#2b7bba" stroke-width="5" marker-end="url(#arrow)"/> <!-- 次要链路 --> <rect x="480" y="220" width="140" height="70" rx="8" fill="#6f6f6f"/> <text x="550" y="255" fill="#fff" text-anchor="middle" font-size="14">异步任务</text> <line x1="310" y1="110" x2="310" y2="220" stroke="#999" stroke-width="2" stroke-dasharray="6,4"/> </svg>这个例子虽然简单,但已经把三层结构都放进去了:矩形排布是骨架,加粗实线是主链,左下角信息块是注脚。实际项目里比这复杂得多,但规则一致。
4. 实战拆解:一张跨平台系统架构图从草稿到定稿的完整过程
方法论说再多,不如看一次完整实战。我以最近的“模拟项目X”为例,它目标是一个跨平台系统,包含移动端、桌面端、网关、用户服务、订单服务、数据库、消息队列和缓存集群。整个过程我走了一天,真正有价值的迭代不在工具里,而在思考方式上。
4.1 第一版草稿:先把模块和依赖关系摆出来
我第一步不用绘图工具,用文本列出所有的模块清单和依赖关系。列文本的好处是强迫你先把逻辑想清楚,而不是被画布上的美观问题带偏。我列出的是:
- 客户端(App / 桌面端)调用网关 REST API
- 网关转发用户请求到用户服务,走 gRPC
- 网关注销订单请求到订单服务,走 gRPC
- 用户服务读取用户表(数据库)
- 订单服务读取订单表(数据库)
- 订单服务发送消息到消息队列,异步更新搜索索引
- 缓存集群服务于用户服务和订单服务
看起来是几条线,但真实系统里还会有失败重试、降级开关、权限校验等路径。我先把“主链”和“次链”分好,决定图上只保留核心路径和一条失败路径,其他路径用文字在图注里补充,不画进主图。这是我最重要的取舍原则:一张图不代表全部信息,它是引导,不是穷举。
4.2 布局推演:为什么选了分层而不是中心辐射
图形摆法上我当时有三个选择:分层布局、中心辐射布局、完全拓扑布局。分层布局的优点是天然反映调用方向,读者能沿链路一层层看下去;中心辐射布局适合一个核心服务周围有很多旁路依赖的场景;完全拓扑布局适合表达复杂网络,但可读性最差。
我最终选了分层布局。因为这张图的核心就是“用户请求从客户端进来,逐层穿透到数据库”,分层布局能让这层语义直接可见。同时我在图的顶部放“接入层”,中间放“网关层”,下面放“业务服务层”,再下面是“基础设施层”,这样的层名本身就是语义提示,读者一看就知道这些区域分别承担什么角色。
4.3 视觉编码落位:每个模块的形状、颜色、箭头类型
接着我给每个实体分配视觉编码。客户端和外部系统用直角矩形,网关和业务服务用圆角矩形,数据库和缓存用圆柱体,消息队列用六边形。颜色上,业务服务统一用核心品牌色,基础设施用另一色,外部系统用灰色。箭头方面,同步调用用实线箭头,异步消息用双线箭头,返回结果用虚线箭头。
我特别留意箭头方向。很多图失败在箭头方向混乱,读起来像一团乱麻。所以在画图时我死盯住一条规则:主链路的箭头方向全部指向“后一个依赖方”,也就是从调用发起方指向被调用方。返回结果不单独画箭头,除非有特殊强调必要,否则会让主线变得杂乱。
4.4 团队评审后的四类修改意见
第一版出来后,我找人评审,收集到的意见非常典型,几乎每个项目都会遇到。第一类意见是“箭头含义不唯一”,同一条线上既有调用又有返回,让人分不清是谁先发起。解决方式是明确主链只有请求方向,用细虚线表示返回。第二类意见是“命名过于内部化”,比如一个服务内部叫“order-server”,但对外文档写的是“订单中心”,图里没做映射。后来我在图注里加了一行“内部服务名 ↔ 对外产品名”对照表。
第三类意见是“缺少失败处理标注”。图上只画了正常流程,但评审人关心的是下单失败后会走哪个分支。我于是在订单服务旁边加了一个小菱形判断节点,用一条红色虚线标出失败路径,并用一句注释说明“失败后进入重试队列,三次后人工介入”。第四类意见是“不同区域背景混乱”。一开始每个层用了不同底色,以为有助于区分,实际效果是视觉噪音太大。最后统一背景,只用边框划分区域。
4.5 图的版本管理:纳入代码仓库让它活下来
改完这轮后,图的初稿进入正式使用阶段。我意识到一个现实:电子图如果不纳入版本管理,任何一次系统变更都会让它迅速过时。所以我把这张图的源文件放进了代码仓库,和代码一起提交、一起评审。每次变更架构,都要求对应更新这张图。这个流程非常有效,图从“一次性交付物”变成了“持续维护的资产”。
我还给这张图做了两个导出版本:一个面向技术评审的高清版,一个面向新成员的简化版。简化版只保留主链路,去掉了失败分支和内部命名,用来做 onboarding 材料。高保真版则用于方案评审和故障复盘。一张源图,两种视口,这个思路也让 diagram-design 的“设计属性”体现得更充分。
5. 把经验固化成团队规范:从“我会画图”到“团队都会画图”
单靠一个人会画图,对团队的价值有限。我把前面这些做法固化成了一个“图表设计规范”文档,任何人接手中都需要遵守。这一步很值得做,因为没了规范,每次画图都是重起炉灶,风格分裂,互相之间没法接力。
5.1 用配置化变量管理图表的视觉风格
我把颜色、字号、间距、线宽这些视觉参数变成变量配置,而不是让每个人手动去选色。下面是配置示例,可以视作图表设计规范的模板,用 JSON 存下来,画图时调用。这套规则让图表之间保持一致性,换人维护也不会跑形。
{ "grid": { "size": 8, "moduleWidth": 140, "moduleHeight": 70, "horizontalGap": 80, "verticalGap": 60 }, "colors": { "coreService": "#2b7bba", "infra": "#e0b800", "external": "#999999", "danger": "#c0392b", "linePrimary": "#2b7bba", "lineSecondary": "#bbbbbb" }, "lineWeights": { "primary": 5, "secondary": 2, "dashed": "6,4" }, "shapeSemantics": { "service": "roundRect", "externalSystem": "rect", "storage": "cylinder", "messageQueue": "hexagon", "decision": "diamond" }, "typography": { "titleSize": 20, "moduleSize": 14, "detailSize": 12, "sourceFont": "monospace" } }这套配置不是为了炫技,它解决的是团队合作时一张图中满目混乱的问题。比如新同事画图时看到“coreService”的颜色是#2b7bba,就知道团队约定核心服务都用这个色;画一个存储,就会想到用圆柱体,而不是自己发挥成圆角方块。视觉规范让图表像代码一样可以被“审计”。
5.2 图表评审与更新流程
图表也需要评审。我把图表评审并入常规技术方案评审,要求每个方案图附带“图表设计任务卡”中填好的目标读者和核心问题。评审时重点看三条:主链路是否清晰?信息层级是否符合目标读者?注脚信息是否完整?只要这些不过,再漂亮也打回。
更新流程上,我提倡“图随代码变”。代码合并触发图源文件检查,如果涉及了架构相关模块,必须更新图。这个流程听起来很重,实际用起来成本很低,因为图通常只在服务模块变化时才需要动,频率远低于代码。把这个习惯养成后,团队里就很少有“过时图”了。
5.3 常见坑和对应解法
我把这几年频发的“图表事故”整理成一个避坑清单,放在团队文档里,遇到症状时直接对照查。
| 症状 | 原因 | 解法 |
|---|---|---|
| 图上一堆节点但看不出主流程 | 没做分层,主链没有加粗 | 先圈定主链路,加粗并用主线颜色 |
| 箭头方向五花八门 | 没统一调用方向规则 | 强制主链从调用方指向被调用方,返回结果不画箭头 |
| 颜色过多,难以区分 | 颜色随心情用,没有语义映射 | 最多三主色,颜色只能表达分类,不表达权重 |
| 图例缺失 | 觉得图形语言“一看就懂” | 每个图固定放图例注脚,注明形状和颜色语义 |
| 没有日期和版本 | 把图当最终交付物,不考虑生命周期 | 信息注脚块必须含更新日期和版本号 |
| 一个图塞了上百个节点 | 想把所有信息都画进去 | 拆分多图:总览图、细节图、部署图,每张只表达单一任务 |
| 图形排列毫无规律 | 手动拖拽不依赖网格 | 统一网格对齐,使用配置文件参数来布局 |
避坑清单有个附加价值:它让普通成员也能说出“这张图哪里不对”,而不是只能说“感觉怪怪的”。当团队能精确描述问题时,图的质量会快速上升。
5.4 工具选择的真实建议
不少读者会关心工具。我自己的经验分三种场景选择。第一是头脑风暴和快速草图,我会用自由绘制工具,随手画,不做对齐,目的是快速捕捉关系和想法。第二是正式技术文档图,我会优先使用代码化绘制方案,把 SVG 写进仓库,保证可维护性。第三是复杂网络依赖图,比如服务间上百个调用的关系,我会先让工具做自动布局,再做人工结构调整,而不是从零手摆。
重要的不是工具的功能多少,而是它是否能满足“版本管理”和“多人协作”这两个基本要求。如果你的图表工具导出的是孤立图片,没法 diff、没法合并、没法自动化,那它就只能服务于个人记录,难以沉淀成团队资产。
我还想强调一点:不要迷信自动布局工具。能自动排布并不代表能自动设计。自动布局只解决几何层次,语义主链和注脚层级依然要靠人脑判断。我见过有人用自动布局工具生成了巨复杂的图,连接线直接穿过模块文字,根本没法出版。我的做法始终是:先确定逻辑结构和主链路,再让工具去算位置,最后手工修正关键区域。
图表设计不是一次性的工作,它是持续演进的过程。我最近在自己团队里推行了一个小习惯:每个人在讲解技术方案时,都要用图来叙述完整链路,而不是用Word文档堆字。一张经过设计的图,比十页文字都更能暴露逻辑缺口。这也是“diagram-design”这个理念对我最大的价值,它让我从“画完交差”变成了“用图表把自己的思维逻辑可视化并接受检验”。希望这篇文章能提供一些可以立刻复用的框架,帮你少走那些我绕过的弯路。