☰
diagram-design:用代码维护架构图,让图表成为可版本管理的工程资产
2026/10/21 9:41:27 网站建设 项目流程

“把图写进代码仓库”这句话,我第一次听到时嗤之以鼻。画图不就是要拖鼠标吗?直到有一次,某后台项目在做架构评审,产品当场提出要把三个模块合并,会后我改架构图改到凌晨一点——改完一个箭头,另一条线又歪了,节点稍微挪一下,整个布局全部打乱。从那次之后,我彻底换了一套工作方式:所有架构图、时序图、部署图全部用文本编码,丢进仓库,随代码一起维护。这套实践在行业里有个统称:diagram-design,直白翻译就是“图解设计”,也叫代码化画图。它解决的问题非常具体:图表不再是一次性交付物,而是一种可以被版本管理、被 Review、被自动校验的工程资产。

这篇文章适合被“图过期了”“图改不动了”“图不知道存在哪”折磨过的人,也适合想给团队建立一套高质量技术文档体系的技术负责人。我会从为什么必须放弃拖拽画图开始,讲到工具选型、核心语法、组织技巧、连续集成检核,最后给出可以直接落地的目录结构和避坑经验。没有纸上谈兵,全部是我实际踩过坑之后沉淀下来的做法。

1. 为什么我放弃了鼠标拖拽,改用代码维护所有设计图

1.1 传统画布工具的软肋:画出来好看,改起来崩溃

传统绘图工具的优势是自由:你可以随意拖动节点、调整颜色、加各种图标。但这份自由在项目持续演进的过程中会变成巨大的负担。最常见的情况是,一次小的结构调整需要动十几个节点,连线跟着全部错位,手工对齐的时间比重新画一张还长。

我印象很深的一次经历:某项目要移动端和 Web 端共用一套服务端接口,架构图里需要新增一层网关。我花了整整一个下午把所有节点的连线重新理顺,评审会上又提出网关应该拆成两个服务,于是又改了一遍。那张图看上去永远很精致,但没有人关心它是不是最新的,因为每次改完都会有人问:“最新版在哪?” 答案永远是“我重新导出一份发你”。

这就是拖拽工具的致命问题:图是“画”出来的,不是“维护”出来的。一旦需求变化,图的更新成本高于重画成本,人们就会选择重画,旧图很快就没人记得,最终变成一堆互相对不上的历史版本。在技术文档场景里,这种状态基本等于没有图。

1.2 文本即结构:diagram-design 的核心逻辑

diagram-design 的思路从根本上改变了这个问题。它不把图当作图形对象来编辑,而是用文本描述图的结构:有哪些节点,节点之间怎么连接,分组关系是什么,全部通过代码来表达。图形只是这段文本的渲染结果。

例如,一个简单的订单处理流程,在代码画图工具里只需要几行文本:

开始 -> 接收订单 -> 校验库存 -> 冻结库存 -> 创建订单 -> 结束 校验库存--失败--> 返回错误

改流程就是改这一行文本。新增一个“幂等校验”节点,就是在中间插一行;把某个分支改成并行处理,就是多写两行。整个图的修改成本从“拖动调整半小时”降到了“编辑文本一分钟”。

文本化带来的第二个好处是它天然可以放进版本控制系统。Git/SVN 可以对图做 Diff,评审时可以看到这次改动到底加了哪些节点、删了哪条连线,而不是拿两张图片来回比对。图终于可以像代码一样被审计、被回溯、被协作。

第三个好处也很实际:文本格式可以被程序处理。可以做自动校验,检查是否有悬空的连线、是否有命名冲突、是否有超出边界的节点。这一步是传统绘图工具几乎做不到的,但对于承载工程语义的架构图来说,价值极大。

1.3 图也会“腐烂”:代码化之后才好治理

过去我们维护一张图的思路是“画完就完了”。但在项目持续迭代半年、一年之后,图的内容和现实必然产生偏差。这种偏差就是图的腐烂,而且拖拽画图没有有效的治理手段,只能靠人自觉更新。

当图变成代码之后,治理手段就出来了:把图的更新纳入 CI 检查范围,每次提交涉及接口变更或模块调整时强制更新图;在 Pull Request 模板里注明“涉及结构变更必须附上更新后的图”;用脚本扫描图里引用的模块名是否存在于项目目录中,防止画着画着就画飞了。

我会在后面的章节详细展开这些手段。这里想强调的核心观念是:diagram-design 不是把画图工具换成另一种画图工具,而是把图从“展示件”变成了“代码”,把画图从“设计行为”变成了“工程行为”。

2. 工具选型对比:Mermaid、PlantUML、Graphviz、D2 各自适合什么场景

2.1 四种工具的定位与适用边界

diagram-design 领域有四类主流工具:Mermaid、PlantUML、Graphviz 和 D2。它们各有侧重,下面的表格可以帮你快速做初步判断。

工具定位适合场景短板
Mermaid轻量脚本式图表流程图、时序图、类图、甘特图、文档内嵌入复杂布局控制能力弱
PlantUML类 UML 建模脚本软件开发过程建模,团队协作画草图语法偏重,风格略老旧
Graphviz自动布局引擎复杂的树形图、关系图、依赖图学习曲线陡,手动控制繁琐
D2现代声明式图表架构图、网络拓扑,强调可读性和规范性生态较新,资料少

我没有把思维导图类和手绘类工具算进来,因为它们解决的是创意成长问题,不是工程问题。只要是承载技术设计、需要版本管理和自动校验的图,主流选择基本就在这四类里。

2.2 拿真实模块做了一次横向实测

为了选择团队最终的主力工具,我拿同一个业务模块画了三张图:一张调用链流程图、一张模块依赖图、一张部署关系图。分别用 Mermaid、PlantUML、Graphviz、D2 实现,记录从开始写到渲染出图的耗时、可读性和后续改动成本。

结果比较有意思。Graphviz 在模块依赖图这种节点多、关系复杂的场景下,自动布局能力是最强的,几十个节点、上百条边也能排出层次清晰的树状结构。但写 DOT 语言的体验确实不算友好,稍微复杂一点的属性配置就得翻文档,团队其他人的学习成本偏高。PlantUML 则是标准的软件工程风格,时序图和状态图画得很规整,名字也符合开发者的思维习惯,问题是多年来视觉风格进步不大,做对外展示材料时缺乏现代感。D2 是后起之秀,语法简洁得舒服,颜色和下钻能力都很有想法,但生态还小,文档也不是很全,偶尔会遇到边缘情况不知如何处理。Mermaid 最强的是社区穿透力:GitHub 原生渲染、各类 Markdown 编辑器都认识它,学习和迭代成本极低,但布局引擎确实比较“随缘”,不适合对排版有极端要求的场景。

2.3 我最终的选择思路

最终我给团队定的主力方案是 Mermaid,辅助方案是 D2 和 Graphviz。选择逻辑有三条。第一条是生态优先,Mermaid 的渲染支持面太广了,代码仓库、文档站、协同编辑工具都能直接识别,团队不需要额外安装任何软件,提交代码之后在网页上看 Diff 时图就已经渲染好了。第二条是学习成本,新同学掌握基础语法只需要半小时,画出来的第一张图就能用于评审,这种即时反馈对技术文档习惯的养成特别重要。第三条是够用,我真正高频需要的流程图、时序图、状态图、架构分层图,Mermaid 都覆盖了,复杂依赖关系再交给 Graphviz。毕竟选型的首要目标是让团队愿意用,而不是工具本身有多炫酷。

不过有一点需要提前说明:Mermaid 对布局的控制能力确实弱,如果你要画一张高度定制化的对外宣传图,建议还是导出到绘图软件里人工排版。它负责的是工程语义图,不是视觉设计图,把这两个场景分清,工具就不会选错。

3. 一张可维护的架构图是怎么从零长出来的

3.1 先定边界,再画节点

很多人第一次切换为代码画图时,会按过去的思路把想画的节点一股脑写出来,结果渲染出来的图又乱又丑。原因在于少了一个关键步骤:先定义图的分层和边界,再往里填内容。

以一套常规的后端服务架构为例,我习惯先把视图分成接入层、应用层、领域层、基础设施层四个纵向泳道,每一层是一个子区域。然后逐个判断:每个模块属于哪一层?它依赖哪些下层能力?是否跨层依赖?跨层依赖是否合理?这本质上是做一次架构盘点,理清这一步之后,图的骨架就出来了,剩下的就是填节点。

确定边界之后,每个节点的命名必须遵循统一规则。服务名、模块名、数据库名直接用项目里的真实名称,不要起“支付服务”“核心模块”这种没头没脑的别名。图是给人看的,但更是给代码评审和系统追踪用的,节点名和实际代码对不上,这张图就失去了工程价值。

3.2 用子图和泳道隐藏复杂度

一张架构图最怕的是把几十个节点全部摊在一个平面上。既读不出来层次,也看不清关系。正确做法是在图内建立子图分组,用容器表达边界,把内部细节收纳进去。

Mermaid 里的 subgraph 就是干这个的。我经常用下面的写法:

flowchart LR subgraph 网关层 路由网关 限流网关 end subgraph 业务领域 订单域 库存域 end subgraph 基础设施 数据库 消息队列 end 路由网关 --> 订单域 路由网关 --> 库存域 订单域 --> 数据库 库存域 --> 数据库 库存域 --> 消息队列

这样渲染出来的图,读者第一眼看到的是“三个大盒子 + 四条连线”,信息层级很清晰。需要深入了解某个子图内部时,可以单独再维护一张细节图,把大图和小图通过链接关联起来。所谓“自顶向下拆解”,在图上也是这样落地的。

3.3 连线是图的灵魂:方向、命名、跨层依赖都要讲规矩

节点定义得再清晰,连线一乱,图就毁了。我把连线规则固定成了三条铁律。第一条是方向优先,所有依赖线统一从上到下,从主调方指向被调方,禁止出现“A 调用 B,B 又调用 A”的循环线,如果存在就说明架构上要做接口拆分,而不是硬画。第二条是语义明确,连线不能只画一根线,要用标签说明关系类型,比如“HTTP 调用”“事件订阅”“数据流”,让读图的人不需要猜。第三条是跨层依赖要重点标注,正常依赖应该是上层依赖下层,若要跨层依赖,连线要用不同颜色或样式标出来,同时在下方加注释说明原因,因为跨层依赖通常是架构腐化的信号。

这套规则看起来简单,但坚持半年以后效果非常明显:图的可读性大幅提升,评审会上大家讨论的是关系合不合理,而不是“这条线是干什么的”。图从装饰品变成了真正的沟通工具。

4. 图库级组织技巧:当图的数量超过 30 张之后

4.1 单图必须瘦身,超过一个屏幕就拆

我见过不少团队用 Mermaid 画了一张几百行的超级大图,整个系统的全部关系都塞在里面。渲染出来要缩放才能看全,打印出来字比蚂蚁还小,这样的图本质上已经失去了沟通功能。

我给自己定了一条硬性指标:单张图的体量控制在“一屏能看全”的范围。大致折算下来,主流程节点不超过 25 个,连线不超过 40 条。超过了就必须拆。拆分的维度有两种,一个是按领域边界拆,把订单、库存、支付拆成独立图,它们之间的关系通过大图标引用;另一个是按视图拆,把逻辑架构图、部署架构图、时序调用图分开画,不要试图用一张图讲清楚所有事。

拆完之后怎么保证不丢失全局视角?通常在项目根目录维护一张概览图,每个模块只显示一个节点,节点上挂链接指向细节图。这张概览图信息密度低,但能回答“系统里有哪些东西、大概怎么连”这类高频问题。

4.2 目录结构、命名规则和注释约定

我们项目里的 diagram 目录结构是长期迭代固化下来的,基本长这样:

diagram/ README.md architecture/ overview.md gateway.md order-service.md workflow/ order-flow.md refund-flow.md deployment/ staging.md production.md assets/ (需要手工排版时导出的辅助图片)

每个图文件顶部固定写一段元信息注释,内容包括这张图对应的业务域、最近更新人、更新时间、关联代码路径。这样任何人打开图文件,第一眼就知道这张图有没有过期风险,该找谁确认。GitHub 的 blame 也能溯源到具体提交,但这层注释能在不跳出文档的情况下解决信息差。

关于命名:文件一律用 kebab-case,动词开头表示流程,名词开头表示结构。order-flow.md就是流程类,gateway.md就是结构类。这套命名的好处是目录扫一眼就能找到目标,不用挨个打开猜。

4.3 用脚本把散落的图文件变成可发布文档

图写了不发布,价值会大打折扣。我们现在的做法是,用脚本把 diagram 目录下的所有 Markdown 图文件统一拉取,剔除掉源码注释之后,拼接成一份带目录的 HTML,推到内部文档站上。整个流程走 CI,每次提交代码后自动执行,相当于每次提交代码都会重新发布一次最新的架构文档。

这套自动化流程有一个额外的好处:强制图仓库保持干净。如果某个图文件语法报错,CI 会直接失败,提交都合不进去。大家为了不阻塞发布,自然而然会维护图的质量。文档从“靠自觉”变成了“靠机制”,这是 diagra-design 最有价值的工程化收益之一。

5. 再往前一步:把图变成设计的活文档

5.1 图的另一面:辅助接口和数据结构定义

很多人的画图止步于“表达现状”,但我发现在设计中先画图、再写接口,效率会高很多。流程图画清楚之后,接口路径、入参出参几乎自己就会浮现出来:两个节点之间的连线,往往就是一次 RPC 调用;节点内标注的数据项,往往就是一份 DTO 字段。

有一次设计退款流程,一开始直接写接口总是感觉漏了边界情况。后来先静下心画时序图,把“普通退款”“部分退款”“退款失败后重试”“原订单已发货”四个分支全部画出来,再回头对照时序图补接口定义,瞬间就齐了。图这笔账,画在代码之前,是最值的投入。

5.2 把错误分支和降级策略画进去

正路径的图画得再漂亮,也只是系统设计的一半。真正的坑都在异常分支里:依赖服务超时、消息丢失、库存不足、幂等键重复,这些分支在传统架构图里几乎都被省略了,而省略之后团队往往就不会去讨论它们。

我的建议是,每个核心流程图必须画两条分支:主分支和失败分支。失败分支不需要占太多版面,用虚线框出来,标注降级策略即可。例如“支付服务超时 → 本地事务表记录状态 → 定时任务补偿”,这样简单两行,评审时就能立刻触发讨论,到底补偿任务有没有做、有没有告警。把失败分支画进图里,这张图才真正代表系统运行时的行为。

5.3 代码与图的联动:反向检核机制

既然图是代码写出来的,那么代码也可以反过来校验图。我们在 CI 里加了一个简单脚本:扫描图文件里引用的所有服务名、模块名和数据表名,再与项目目录、数据库迁移文件列表比对。凡是图里出现了但代码里不存在的名字,就输出警告。这个检查很粗糙,但它能抓住最常见的问题——服务已经改名或者下线,架构图没有跟着改。

如果你所在的团队暂时不具备写复杂脚本的条件,也有一个低成本替代方案:在每个图文件的元信息注释里写一段“关联代码路径”,例如指向服务模块的根目录。以后改代码时,开发者看到代码变更关联了某张图,顺手就更新了。

6. 我踩过的坑和建议的起步配置

6.1 三个最容易让图“乱而不自知”的坏习惯

先说我踩得最深的三个坑。第一个是过度配色。一开始画图喜欢给每个节点标不同颜色,渲染出来确实花哨,但后来发现当节点超过二十个时,颜色变成了噪音,很难记住哪个颜色代表什么语义。现在我把颜色限制在四种:边界、正向流程、反向分支、基础设施。花哨留给演示文稿,架构图只需要清晰。

第二个坑是节点文案过长。曾经图里有个节点叫“订单状态流转与超时未支付自动取消服务”,渲染之后框比整个子图还大,布局直接崩了。后来做了硬性约定:节点文字不超过八个字,详细信息写进注释。图不适合承载大段解释,它只负责指出“这是什么”,细节交给文字文档。

第三个坑是只顾节点不顾全局,每个模块单独画出来都好看,合到一张图里交叉连线满天飞。解决方式也很简单:先画高层级概览图,细节通过折叠或者链接下钻,不要指望一张图承载所有细节。

6.2 可以直接抄走的起步目录和建议配置

如果你准备带着团队切到代码画图,我建议第一天不要追求完整方案,先做三件事。第一,建一个 diagram 目录,用 README 定好每类图表出口和命名规则,不用多,一页纸足够。第二,只要求新增模块和新增接口时画图,存量图暂时不动,避免起步阶段就背上沉重的迁移负担。第三,先选一个展示场景跑通,比如内部 Wiki 直接嵌入 Mermaid 渲染,或者 CI 发布一份静态 HTML,让成员看到画图的“成品效果”。

我目前固定使用的编辑器配置里,最有用的是本地预览插件,代码仓库里的 Markdown 图文件在编辑状态下可以直接渲染,改一行立刻看到效果,不需要反复推送到远端。这一步对养成画图习惯极其重要,没有即时反馈很容易画两天就放弃了。

6.3 关于“终极完美图”的一点心得

最后想说一个认知层面的体会。长期维护代码画图之后,我越来越不追求一张所谓“终极完美图”,而是追求画图逻辑本身可持续迭代。架构图、流程图这些设计资产的本质,是团队对系统结构共识的外化。只要这个共识在演进,图就天然不可能是静止的。接受这一点之后,反而轻松了:不需要一张图完美,只需要一套任何人都愿意维护图的机制。机制建起来之后,图会自动跟上系统演化的节奏,而不是靠某个“文档专员”在角落里独自追赶。

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

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

立即咨询