1. 从一张草图到一套系统:diagram-design 到底在解决什么问题
第一次看到 “diagram-design” 这个标题,很多人会以为它只是教你怎么把流程图做得好看一点。但真正在项目里画过架构图、部署图、数据流图的人都知道,图的美观只是最表层的事。真正让人头疼的,是图画完之后没人看得懂、改一个节点要动五张图、不同人画出来的风格完全不统一、评审会上大家对着同一张图理解出三种不同的系统结构。
diagram-design 这个项目,核心就是要把“画图”这件事从个人审美行为,变成一套可复用、可协作、可验证的设计系统。它关注的不是某一张图好不好看,而是一组图能不能在团队里稳定传递信息、能不能随着系统演进而低成本维护、能不能让新加入的人在不问人的情况下看懂关键路径。
我过去几年参与过几个中大型系统的文档和架构可视化工作,踩过的坑基本都集中在三个地方:一是图例不统一,同一个方框在不同图里代表不同含义;二是抽象层级混乱,一张图里既有业务概念又有数据库表名;三是图与代码脱节,系统改了图没改,最后图变成历史文物。diagram-design 要解决的正是这三类问题,它适合系统架构师、技术文档工程师、产品经理,以及任何需要长期维护一套可视化文档的人。
这篇文章不会只讲“怎么画得漂亮”,而是把 diagram-design 拆成设计原则、工具选型、实操流程、常见故障排查四个层面,结合我自己的项目经验,把每一步背后的判断逻辑讲清楚。你可以把它当成一份可以直接抄作业的落地指南,也可以当成一次对“可视化设计系统”的完整复盘。
2. 整体设计思路:为什么不是“找个好用的画图工具”就完事
2.1 核心矛盾:图的寿命比系统短
大部分团队画图的流程是这样的:系统设计阶段,某个人用自己顺手的工具画了几张图,放进文档里;系统上线后,图没人维护;半年后新同事问“这个模块怎么调用的”,老同事说“你看那张图”,新同事看完更糊涂了。问题的根源不是工具不好,而是图被当成了一次性产物,而不是系统的一部分。
diagram-design 的第一个设计原则就是:图是系统的视图,不是系统的装饰。这意味着每一张图都必须有明确的读者、明确的抽象层级、明确的更新触发条件。我在一个项目里做过统计,一张没有标注更新责任的架构图,平均有效寿命只有三到四周。超过这个时间,图里的信息就开始和实际系统产生偏差。
所以 diagram-design 的思路不是先选工具,而是先定义“图的生产线”:谁负责画、画给谁看、什么时候更新、用什么标准判断一张图是否合格。这条生产线定下来之后,工具只是执行环节。
2.2 分层设计:一张图只讲一件事
很多图之所以难懂,是因为它试图在一张图里同时表达业务流、数据流、部署结构和调用关系。读者的眼睛在图上找不到焦点,大脑要同时处理多个维度的信息,最后只能放弃。
diagram-design 采用分层设计,把常见的系统视图拆成四层:
| 层级 | 视图类型 | 主要读者 | 核心问题 |
|---|---|---|---|
| L1 | 上下文图 | 产品、业务方 | 系统边界在哪里,和谁交互 |
| L2 | 容器图 | 开发、运维 | 系统由哪些可部署单元组成 |
| L3 | 组件图 | 开发 | 每个单元内部有哪些模块 |
| L4 | 代码/数据图 | 开发 | 关键流程和数据结构 |
这个分层不是我拍脑袋定的,它参考了业界常见的 C4 模型思路,但做了简化。关键不在于层级名字,而在于每一层只回答一个问题。L1 不出现数据库,L2 不出现类名,L3 不出现部署细节。一旦越层,图就开始变得难懂。
我在实际项目里会强制要求:任何一张图在标题下方用一句话写清楚“这张图回答什么问题”。如果写不出来,说明这张图不该存在,或者应该拆成两张。
2.3 图例系统:让方框和箭头有固定含义
图例不统一是团队协作里最隐蔽的杀手。A 画的方框代表服务,B 画的方框代表模块,C 画的方框代表团队。三个人把图放在一起评审,讨论十分钟才发现大家说的不是一回事。
diagram-design 要求每个项目维护一份图例字典,至少包含以下内容:
- 形状与含义的对应关系:矩形代表什么、圆角矩形代表什么、圆柱代表什么
- 颜色与状态的对应关系:蓝色代表正常、橙色代表待迁移、灰色代表废弃
- 箭头类型:实线代表同步调用、虚线代表异步消息、粗线代表数据流
- 标注规范:节点上写什么、不写什么,版本号放哪里
这份字典不需要很复杂,但必须存在,并且所有图都引用它。我见过一个团队用颜色区分环境,结果红色在架构图里代表生产环境,在部署图里代表告警状态,评审时直接吵起来。后来他们把颜色含义写进图例字典,这类问题就再没出现过。
2.4 工具选型:文本化优先,可视化辅助
工具选型上,diagram-design 的倾向很明确:优先选择文本化、可版本控制的工具。原因很简单,图要跟着系统演进,就必须能进代码仓库、能 diff、能 review。纯拖拽工具生成的二进制文件,在协作和版本管理上天然吃亏。
常见的选型组合是:
- 用文本描述语言定义图的结构,比如 Mermaid、PlantUML、Graphviz DOT
- 用版本控制管理图源文件,和代码放在同一个仓库
- 用 CI 流程自动渲染成图片,嵌入文档
- 需要精细调整时,再用可视化工具做局部美化
这套组合的好处是,图的变更可以被 review,可以被追溯,可以和代码变更关联起来。我在一个项目里把架构图和代码放在同一个仓库,每次合并请求如果改了服务依赖,CI 会提示“架构图可能过期”,虽然不能完全自动化,但至少给了维护者一个提醒。
当然,文本化工具在复杂布局上不如拖拽工具灵活。我的经验是:结构用文本,布局用工具。先用文本定义节点和关系,保证逻辑正确,再导出到可视化工具里调整位置和样式。这样既保留了可维护性,又不牺牲最终呈现效果。
3. 核心细节解析:从图例字典到渲染流水线
3.1 图例字典的具体写法与维护方式
图例字典不是一份写完就锁死的文档,它需要随着项目演进更新。我的做法是把它放在仓库根目录的docs/diagram-guide.md,和架构图放在一起。每次新增一种图形或颜色,都要在合并请求里说明用途,并更新这份字典。
一个最小可用的图例字典包含以下字段:
## 形状 - 矩形:可部署单元(服务、应用、数据库) - 圆角矩形:外部系统或第三方依赖 - 圆柱:数据存储 - 菱形:决策点或网关 ## 颜色 - 蓝色填充:当前系统负责的组件 - 灰色填充:外部系统或已废弃组件 - 橙色边框:待迁移或待重构组件 ## 箭头 - 实线箭头:同步调用 - 虚线箭头:异步消息或事件 - 空心箭头:数据流向这份字典的关键在于可执行。什么叫可执行?就是任何人拿到一张图,都能对照字典判断每个元素的含义,不需要问作者。我在评审会上经常做一件事:随机指一个节点,让作者对照字典解释。如果解释和字典不一致,要么改图,要么改字典。
维护图例字典的另一个技巧是:用真实图做示例。字典里不要只写抽象定义,要附上一张标注过的示例图,把每个形状和颜色圈出来。这样新成员一看就懂,比读十行文字有效。
3.2 抽象层级的判断标准:什么时候该拆图
抽象层级混乱是图难懂的另一个主要原因。一张图里既有“用户下单”这样的业务动作,又有“order_table”这样的数据库表名,读者的思维要在业务和技术之间反复切换,认知负担很重。
diagram-design 给出的判断标准是:一张图里所有节点的抽象层级应该一致。具体怎么判断?我通常用“读者视角”来测试:如果这张图的读者是产品经理,那节点应该是业务能力或用户动作;如果读者是开发,节点应该是服务或模块。两者不能混。
当一张图同时需要表达业务和技术时,正确做法是拆成两张图,用编号或链接关联。比如:
- 图 1:订单履约上下文图(业务视角)
- 图 2:订单服务组件图(技术视角)
- 图 1 中的“订单服务”节点链接到图 2
这样读者可以按需深入,而不是被一张图的信息量压垮。
我在实际项目里还遇到一种情况:图越画越大,一张图有上百个节点。这时候不是拆不拆的问题,而是必须拆。我的经验阈值是:一张图的节点数超过 15 个,就要考虑拆分。超过 25 个,基本必须拆。拆分的维度可以是按子系统、按业务流程、按部署环境,具体看读者需求。
3.3 文本化图源的组织结构
如果采用文本化工具,图源文件的组织方式直接影响维护效率。我推荐按视图层级和系统模块两个维度组织:
docs/ diagrams/ l1-context/ order-system.md payment-system.md l2-container/ order-service.md payment-service.md l3-component/ order-core.md legend.md每个文件只包含一张图的定义,文件名用英文短横线连接,和系统模块名对应。这样做的好处是:
- 找图快:按层级和模块定位
- diff 清晰:一次变更只影响一个文件
- 权限可控:不同模块的图可以由不同人维护
图源文件里除了图定义,还应该包含元信息:作者、最后更新日期、对应系统版本、更新触发条件。这些信息可以用注释写在文件头部,渲染时不会显示,但维护时非常有用。
3.4 渲染流水线的搭建要点
文本化图源要变成可阅读的图片,需要一条渲染流水线。最简单的做法是在本地用命令行工具渲染,复杂一点可以接入 CI 自动生成。
本地渲染的典型命令:
# 以 Mermaid 为例 mmdc -i docs/diagrams/l2-container/order-service.md -o output/order-service.svg # 以 PlantUML 为例 plantuml -tsvg docs/diagrams/l2-container/order-service.pumlCI 集成的思路是:每次合并请求如果修改了图源文件,自动渲染并检查是否成功。如果渲染失败,阻止合并。这样可以避免图源语法错误导致文档构建失败。
我在项目里还会加一步:渲染后检查图片尺寸。如果一张图渲染出来超过 4000 像素宽,说明节点太多,需要拆分。这个检查可以用脚本自动完成,比人工发现更及时。
提示:渲染流水线不要追求一步到位。先保证本地能渲染,再接入 CI。很多团队一上来就搞自动化,结果图源格式还没稳定,流水线天天报错,最后没人维护。
4. 实操过程:从零搭建一套 diagram-design 工作流
4.1 第一步:盘点现有图,建立图例字典
不要一上来就画新图。先把项目里现有的图收集起来,看看它们都在表达什么、读者是谁、有没有重复和冲突。我通常会做一个简单的表格:
| 图名 | 位置 | 读者 | 抽象层级 | 最后更新 | 是否准确 |
|---|---|---|---|---|---|
| 系统架构图 | 文档首页 | 开发 | 混合 | 半年前 | 部分过期 |
| 部署图 | 运维手册 | 运维 | 容器 | 三个月前 | 准确 |
| 流程图 | 产品文档 | 产品 | 业务 | 一年前 | 过期 |
盘点完成后,把仍然有效的图按层级分类,把过期的图标记出来。然后根据现有图的共同元素,起草第一版图例字典。图例字典不需要覆盖所有情况,先覆盖 80% 的常见元素即可。
4.2 第二步:选定工具链,搭建最小可用环境
工具链的选择要考虑团队的技术栈和维护成本。如果团队已经在用 Markdown 写文档,Mermaid 是最自然的选择,因为它可以直接嵌入 Markdown,渲染门槛低。如果团队需要更复杂的布局和样式控制,PlantUML 或 Graphviz 更合适。
最小可用环境包括:
- 一个图源目录,按层级和模块组织
- 一个渲染命令或脚本,能把图源转成图片
- 一份图例字典,放在图源目录旁边
- 一个简单的检查脚本,验证图源语法和图片尺寸
我建议先用一个真实的小系统练手,不要拿最复杂的系统开刀。选一个三到五个服务的模块,画一张 L2 容器图和一张 L3 组件图,走完整个流程。这样可以在低成本下暴露问题,比如图例不够用、渲染工具不顺手、抽象层级不好判断。
4.3 第三步:定义图的元信息模板
每张图都应该有一个元信息头部,方便维护和追溯。我用的模板如下:
--- title: 订单服务容器图 level: L2 reader: 开发、运维 system: 订单系统 version: 1.2.0 last_updated: 2025-01-15 update_trigger: 服务拆分或依赖变更 ---这些字段看起来简单,但作用很大。level强制作者思考抽象层级,reader明确读者对象,update_trigger定义什么时候需要更新。我在项目里要求所有图都必须有这个头部,否则评审不通过。
4.4 第四步:画第一张图并走评审
第一张图不要追求完美,先画出来,然后走一次评审。评审的重点不是图好不好看,而是:
- 图例字典是否覆盖了所有元素
- 抽象层级是否一致
- 读者能否在不问作者的情况下看懂
- 元信息是否完整
我在评审时会让一个没参与画图的同事看图,然后复述图的内容。如果他复述的内容和作者意图一致,说明图合格;如果不一致,找出歧义点,更新图例字典或调整图。
4.5 第五步:接入版本控制和 CI
图源文件进入版本控制后,就可以接入 CI 做自动检查。一个简单的 CI 脚本可以完成以下任务:
#!/bin/bash # 检查所有图源文件是否能成功渲染 for file in docs/diagrams/**/*.md; do mmdc -i "$file" -o /tmp/test.svg || exit 1 done # 检查渲染后的图片尺寸 for svg in /tmp/*.svg; do width=$(grep -o 'width="[0-9]*"' "$svg" | head -1 | grep -o '[0-9]*') if [ "$width" -gt 4000 ]; then echo "图片过宽: $svg" exit 1 fi done这个脚本不复杂,但能挡住大部分低级错误。我在项目里还会加一条规则:如果代码变更涉及服务依赖,合并请求必须同时更新对应的 L2 图。这条规则靠人工检查,但 CI 可以提示“本次变更可能影响架构图”。
4.6 第六步:建立更新机制和责任人
图的生命力在于更新。没有更新机制的图,画得再好也会过期。diagram-design 要求每张图都有明确的责任人,责任人可以是个人,也可以是团队。
更新机制包括:
- 定期检查:每季度盘点一次所有图,标记过期和待更新
- 事件触发:系统架构变更时,同步更新对应图
- 评审关联:合并请求如果涉及架构变更,必须包含图更新
我在项目里把图的责任人写进元信息,并且和代码仓库的 CODEOWNERS 关联。这样当图源文件被修改时,系统会自动通知责任人 review。
5. 常见问题与排查技巧实录
5.1 图例冲突:同一个形状在不同图里含义不同
这是最常见的问题,通常发生在多个团队各自画图、没有统一图例字典的情况下。表现是评审时大家对同一个符号的理解不一致,讨论效率极低。
排查方法:收集所有图,提取每个形状和颜色的使用场景,做成对照表。如果发现冲突,要么统一含义,要么在冲突的图上加局部图例说明。
解决技巧:图例字典要版本化。每次修改图例,都要记录修改原因和影响范围。我在项目里用 Git 管理图例字典,每次修改都有 commit message,方便追溯。
5.2 抽象层级混乱:业务和技术混在一张图里
表现是图里既有“用户下单”又有“order_table”,读者不知道从哪个角度理解。这种图通常是作者想省事,把多个视角塞进一张图。
排查方法:给每个节点打标签,标记它属于业务层还是技术层。如果两层节点数量都超过三个,基本可以确定需要拆图。
解决技巧:拆图后,用超链接关联。L1 图里的“订单服务”节点链接到 L2 图,L2 图里的“订单核心模块”链接到 L3 图。这样读者可以按需深入,不会被信息量压垮。
5.3 图与代码脱节:系统改了图没改
表现是图里的服务依赖和实际代码不一致,新同事按图理解系统,结果踩坑。这种问题的根源是图没有和代码变更关联起来。
排查方法:定期用代码分析工具提取实际依赖关系,和图里的依赖做对比。如果差异超过一定比例,说明图需要更新。
解决技巧:把图源文件和代码放在同一个仓库,合并请求如果涉及架构变更,必须包含图更新。我在项目里还加了一条:如果合并请求改了服务配置文件,CI 会提示“请检查架构图是否需要更新”。
5.4 渲染失败:图源语法错误或工具版本不兼容
表现是本地能渲染,CI 上失败,或者换了工具版本后渲染结果不一致。这种问题通常是因为图源语法不规范,或者工具版本没有锁定。
排查方法:在 CI 里固定工具版本,本地和 CI 使用相同的渲染命令。图源文件提交前先在本地渲染一次,确保没有语法错误。
解决技巧:把渲染命令写进 Makefile 或脚本,本地和 CI 都调用同一个脚本。这样避免“本地能跑,CI 跑不了”的问题。
5.5 图片过大:一张图节点太多,无法阅读
表现是图渲染出来几千像素宽,嵌入文档后需要放大才能看清,读者体验很差。这种问题通常是作者舍不得拆图,把所有内容塞进一张。
排查方法:统计每张图的节点数,超过 15 个就标记为待拆分。渲染后检查图片宽度,超过 4000 像素就强制拆分。
解决技巧:拆分维度可以按子系统、按业务流程、按环境。拆完后用一张索引图关联所有子图,读者可以从索引图跳转到具体子图。
5.6 常见问题速查表
| 问题 | 表现 | 排查方法 | 解决技巧 |
|---|---|---|---|
| 图例冲突 | 同一形状含义不同 | 提取形状使用场景做对照 | 图例字典版本化 |
| 层级混乱 | 业务和技术混在一起 | 给节点打层级标签 | 拆图并用链接关联 |
| 图码脱节 | 图和代码不一致 | 代码分析对比依赖 | 图源和代码同仓库 |
| 渲染失败 | 本地能跑 CI 失败 | 固定工具版本 | 统一渲染脚本 |
| 图片过大 | 节点太多无法阅读 | 统计节点数和图片宽度 | 按维度拆分并建索引 |
5.7 独家避坑技巧:从项目里总结的三条经验
第一条:不要追求一次画对。图是迭代出来的,第一版丑一点没关系,关键是走通流程。我在项目里见过太多人花一周画一张“完美”的图,结果系统一变,图全废。先画出来,再迭代,比追求完美更重要。
第二条:图例字典要有人负责。没有责任人的字典会很快过期。我在项目里指定一个人负责图例字典的维护,每次新增图例都要经过他确认。这个人不需要是架构师,但需要细心和坚持。
第三条:图的更新要绑定到流程里。靠自觉更新图是不靠谱的,必须绑定到合并请求、评审、发布等流程里。我在项目里把图更新作为合并请求的检查项,虽然不能完全自动化,但至少让开发者意识到图是需要维护的。
6. 工具链选型对比与落地建议
6.1 文本化工具对比
| 工具 | 语法友好度 | 布局能力 | 版本控制 | 学习成本 | 适用场景 |
|---|---|---|---|---|---|
| Mermaid | 高 | 中 | 好 | 低 | Markdown 文档嵌入 |
| PlantUML | 中 | 高 | 好 | 中 | 复杂架构图 |
| Graphviz | 低 | 高 | 好 | 高 | 自动化生成图 |
| 拖拽工具 | 高 | 高 | 差 | 低 | 最终美化 |
选型的核心判断是:图需要频繁更新吗?如果需要,优先文本化工具;如果是一次性展示,拖拽工具也可以。我在项目里的组合是:Mermaid 做日常架构图,PlantUML 做复杂部署图,拖拽工具做对外汇报的最终版。
6.2 渲染与发布流程建议
渲染流程建议分三步:
- 本地渲染:开发者提交前本地验证
- CI 渲染:合并请求时自动渲染并检查
- 发布渲染:文档构建时生成最终图片
发布流程建议把图片和文档一起构建,避免图片和文档版本不一致。我在项目里用静态站点生成器把 Markdown 和图源一起构建,输出 HTML 和 SVG,部署到内部文档站点。
6.3 团队协作规范建议
团队协作规范包括:
- 图源文件命名规范:层级-模块名,如
l2-order-service.md - 元信息必填字段:title、level、reader、version、last_updated
- 图例字典更新流程:新增图例需要评审
- 图更新触发条件:架构变更、服务拆分、依赖调整
- 评审检查项:图例一致性、抽象层级、读者可理解性
这些规范不需要一次全部落地,可以先从命名规范和元信息开始,逐步增加。我在项目里的经验是:规范越少越容易执行,但关键规范不能少。
7. 我个人在实际操作中的体会
diagram-design 这个项目让我最大的体会是:画图不是艺术创作,而是信息设计。一张好的图,不是看起来漂亮,而是让读者在最短时间内理解系统结构。为了达到这个目标,图例要统一、层级要清晰、更新要及时,这些都比美观重要。
我在项目里踩过最大的坑,是早期太关注工具和样式,忽略了图例字典和更新机制。结果画了一堆图,半年后全部过期,没人知道哪张还能用。后来我们把图例字典和更新流程建起来,图的寿命明显延长,评审效率也提高了。
另一个体会是:图的数量不是越多越好。我见过一个项目有上百张图,但大部分没人看。后来我们做了一次清理,只保留每个层级最核心的几张图,反而更受欢迎。图的价值在于被使用,不被使用的图就是维护负担。
最后分享一个小技巧:每次画完图,找一个没参与的人看图并复述。如果他复述的内容和你的意图一致,说明图合格;如果不一致,找出歧义点并修正。这个技巧简单但有效,比任何评审流程都直接。