把架构图当代码管理:Diagram-as-Code 完整实践指南 先问各位一句你们项目里最后那张架构图是活在某个白板截图里还是活在最新一版代码旁边我在不少团队里见过同一种尴尬——辛辛苦苦画出来的流程图、时序图两个月后就没人敢改了。文档的图是五月份的代码是十月份的新人问起来所有人都含糊其辞“大概、也许、好像是这么走的。”这种事碰多了我慢慢把图表设计从“打开画板随手画”变成了一套“写代码驱动的设计工作流”也就是老外常说的 diagram-as-code。今天这篇就把我实际在用的方案、踩过的坑、整理出来的规范完整写一遍给那些想让图表真正“活”在项目里而不是躺在截图里的人做个参考。diagram-design 这个事表面看是“选个工具画图”实际上背后是一整套关于文档版本化、团队协作、自动化交付的工程决策。我尽量不讲虚的全部是实操层面的东西。1. 为什么我放弃了白板画图开始认真对待图表设计的可维护性1.1 在线白板和画图工具的真正痛点先说实话Miro、Figma、draw.io、Excalidraw 我用得都不少尤其项目早期头脑风暴、快速梳理流程的时候白板类工具的优势非常明显没有学习成本拉个框连条线信息瞬间可视化。但一旦图纸进入“维护期”问题就来了。第一没有 diff。代码有 Git 可以 review图表没有。谁改了某一个流程分支其他人根本不知道改了什么、为什么改。第二位置信息是噪音。画布里每个节点的坐标、间距、连线走向这些信息对“逻辑”本身毫无意义但在你的文件里占了 90% 的体积。第三打开速度越来越慢。一张大图拉到后面节点多了之后画布滚动都不流畅。第四也是最致命的——图和人脑里的逻辑可能是脱节的。你脑海中想的是分支判断、数据流转、状态迁移手底下却在挪框调线久而久之图的逻辑结构会被布局问题掩盖。我遇到过最典型的一个案例团队用在线画布维护支付流程图某次需求调整把“退款先校验订单状态”挪到了“风控审核”之后画图的人改完没通知任何人下游依赖老图做开发最后在灰度环境暴露出一个错账。图的版本不一致在某些场景下是会出事故的。1.2 把图表当作代码管理本质上是把“逻辑”从“布局”中解放出来diagram-design 如果按照代码的方式来做核心思路是逻辑结构用文本描述布局和渲染交给专门的引擎。你写一段 Mermaid 或 PlantUML里面表达的是“A 依赖 B”“C 在条件 X 下走到 D”至于这个图最终呈现成什么形状、连线怎么绕、节点怎么排不由你操心。这份源文件可以进 Git可以走 Code Review可以 diff可以自动校验。改了一行评审的人一眼就能看出你动了哪个依赖关系。这个转变表面上是换工具实际上是换一种思维方式把图表从“一次性设计稿”变成“长期维护的文档资产”。这不是说画布工具就该被抛弃。快速头脑风暴、探索性设计、给客户画概念稿画布依然是效率最高的。但一旦梳理清楚、进入工程实施阶段我会把所有关键图语义化进代码让它们成为可维护的资产。两个阶段各司其职不是谁取代谁。2. 工具选型没有银弹四种主流方案的边界在哪2.1 各工具实测后的能力边界聊 diagram-design 绕不开工具。我实际生产环境里用过四类方案这里给出比较直接的对比和选型建议。工具/方案适用场景版本控制友好度布局干预能力部署/导出方式主要槽点MermaidMarkdown 内嵌的流程图、时序图、甘特图、状态图极好纯文本弱只能通过方向/边约束间接控制GitHub 自动渲染、Mermaid Live、CLI 导出复杂图易布局混乱跨子图连线痛PlantUMLUML 完整语义类图、时序图、用例图、部署图极好纯文本比 Mermaid 强支持隐藏/排序官方服务器渲染、本地 jar 包默认样式比较老气依赖 JavaGraphviz (DOT)节点关系复杂的 DAG、依赖图谱、树形图极好纯文本极强通过 rank、subgraph 精确控制CLI 输出 SVG/PNG/PDF写起来像写代码学习曲线陡Excalidraw / draw.io手绘风快速走查、产品原型演示、交互示意图文件可入 Git但 diff 不友好手动拖拽完全可控导出 PNG/SVG/嵌入文档没有“语义层”就是纯画布从我的经验来看Mermaid 是性价比最高的起步方案。语法直观读起来像伪代码不需要额外起服务写进 Markdown 直接就能渲染。很多技术社区和项目仓库现在都原生支持 Mermaid 渲染这对文档集成非常有利。PlantUML 强在 UML 语义。时序图里可以精确表达激活块、嵌套调用、异步消息Mermaid 在这些场景下表达力还是弱了一截。如果你的项目需要大量严格意义的 UML 图PlantUML 值得作为主力。Graphviz 我主要用来处理 Mermaid/PlantUML 搞不定的复杂图。比如团队内部的微服务依赖关系几十上百个节点和边Graphviz 的 dot 布局引擎跑出来的效果是其他工具没法比的。它的布局算法经过了大量场景优化复杂图可视化这块确实是最能打的。缺点是语法简陋可读性差维护起来像阅读正则表达式。Excalidraw 这类我不排斥尤其在快速方案预演、评审时给人看“草图感”场景手绘风反而降低了心理压力。但我会控制它的使用半径只出草图不进主干文档。万一哪天想 GraphQL 化或者做数据驱动画布文件很难被程序化消费。2.2 选定工具前先想清楚三个问题第一这张图的生命周期有多长一次性评审图随便用什么要长期维护的架构图、流程规范图趁早上文本化工具。第二谁会维护这张图如果团队里只有一个人会 Mermaid那工具选型本质上是团队技能的决策。我不会为一个“看起来很酷但没人会写”的方案买单。第三图的消费端在哪是 GitHub 自动渲染、内嵌到公司的 Wiki、还是导成图片贴 PPT先想好终态再反选工具能省掉很多转换的麻烦。我的建议是默认从 Mermaid 起步强度不够再引入 Graphviz 或 PlantUML画布类工具留在方案前期使用。别让工具定义你的架构表达要让表达需求去匹配工具。3. 长期可维护的图表组织规范从一套目录开始3.1 我实际在用的图表目录结构和文件命名工具选好了真正让 diagram-design 从“一次性产出”升级为“工程能力”的是组织规范。很多团队的图之所以后来坏了不是画的不好是没人知道该去哪找这张图、怎么改、改了有什么影响。我现在主持的项目里图表源文件统一放在仓库的docs/diagrams目录下按系统和模块拆分docs/ ├── diagrams/ │ ├── README.md │ ├── order/ │ │ ├── order_flow.mmd │ │ ├── order_state.mmd │ │ └── payment_timeout.mmd │ ├── account/ │ │ ├── login_sequence.puml │ │ └── identity_graph.dot │ └── ops/ │ └── deploy_flow.mmdREADME.md作为索引列清楚每张图的用途、维护负责人、最后修改日期。文件命名统一用“模块_图类型”的格式比如order_flow、payment_timeout一看名字就知道大概是讲什么的避免出现“最终版.v4.mmd”这种鬼东西。3.2 单一来源与最小信息量原则单一来源意味着同一张逻辑图在任何场景下都只保留一份源文件其他所有的展示形式——PNG 截图、PPT 里贴的图、Wiki 内嵌的图片——都是由这一份源文件生成的产物而不是独立存在的副本。实际操作中我见过很多团队在 Git 里同时放着.mmd和.png过段时间.png被手工更新了.mmd却是旧的反过来了也有。为了避免这种混乱我一般只会把源文件提交进代码仓库渲染产物通过 CI 构建生成放进发布物或者文档站点不在仓库里维护图片文件。最小信息量原则也特别重要。一张图只表达一个主题。系统整体架构、核心业务时序、状态流转、异常处理拆成四张图分别维护而不是揉在一张大画布上。单张图如果超过 20 个节点我会停下来想想能不能拆。图越大阅读成本越高维护意愿越低最终必然落得“没人敢动”的下场。3.3 内容上的“代码规范”同样适用于图既然是 diagram-as-code写图就应该像写代码一样讲究。字符串常量抽出来统一维护颜色主题保持一致。Mermaid 里我习惯把颜色定义在%%{init: ...}%%头里面而不是散在节点里。注释也必不可少每个关键分支、难以理解的判断条件在图表代码里写好解释。下面是我在 order_flow.mmd 里注释的典型写法%% 主订单流程下单 - 支付 - 履约 %% 所有节点名使用英文标识展示文本用冒号分隔 flowchart TD A[创建订单] -- B{是否秒杀} B -- 是 -- C[锁定库存] B -- 否 -- D[常规库存扣减] C -- E[发送消息] D -- E %% 消息发送失败不阻断主流程 E -- F[异步处理]这种带注释、带规范的图三个月后翻出来你还能一眼看懂当时的设计意图。反过来一堆没有注释、没有摘要的图和那些没有注释的祖传代码本质上没有区别。4. 从源文件到交付物自动导出、内嵌文档与持续更新4.1 本地方案Mermaid CLI 和 IDE 插件实际写图的过程中即时反馈非常重要。你写的每一段 Mermaid 代码最好在几秒内就能看到渲染结果不然就会像写 CSS 没有浏览器刷新一样盲调。如果你是 VS Code 用户推荐装个 Mermaid 相关的预览插件。文件编辑保存后右侧预览面板直接渲染非常顺手。这些插件底层还是依赖 mermaid.js支持的语法版本可能不太同步如果发现某段新语法不识别先看看插件版本我踩过这个坑。如果要做批量导出我用的是 Mermaid CLI# 安装 mermaid-js/mermaid-cli npm install -g mermaid-js/mermaid-cli # 将 mmd 文件导出为 SVG 或 PNG mmdc -i order_flow.mmd -o order_flow.svg mmdc -i order_flow.mmd -o order_flow.png -t dark -b transparent导出图片时最头疼的是中文字体。Mermaid CLI 默认依赖 Puppeteer 渲染如果系统没装中文字体导出的图片里中文全变成了方块。解决办法是给mmdc配置一个字体文件{ fontFamily: Noto Sans CJK SC, fontSize: 14 }用-c mmdc-config.json指定配置。或者更粗暴一点给系统安装fonts-noto-cjk。这个细节不处理好导出一次踩一次坑。4.2 文档站和 CI 集成让图表自动出现在该出现的地方图表最大的价值在于被阅读。孤立在目录里的.mmd文件是没有价值的要让它融入团队的文档生态。我现在用的方案是 MkDocs Mermaid 插件。在mkdocs.yml里启用插件后Markdown 里直接写 Mermaid 的代码块构建出的静态站点就能渲染图表。VitePress 也有类似支持前端团队通常用它来搭内部文档站。把图表集成进文档站只是第一步CI 自动化校验才是保证图表持续可用的关键。我在 GitHub Actions 里配置了一个 PWA 流程name: Check Diagram Syntax on: [pull_request] jobs: diagram-check: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Setup Node uses: actions/setup-nodev4 with: node-version: 20 - name: Install mermaid-cli run: npm install -g mermaid-js/mermaid-cli - name: Render all mmd files run: | for file in $(find docs/diagrams -name *.mmd); do mmdc -i $file -o /tmp/render-check.svg done这个脚本会遍历所有.mmd文件只要有一个语法错误或渲染失败PR 就直接被拦下来。这比任何 code review 都有效语法层面的错误在合并前就被机器消灭了。4.3 周报和外部协作中的“低技术含量”交付不是所有阅读图表的人都在代码仓库里。客户、业务方、外部合作伙伴他们更习惯收到一张 PDF 或一个指向在线链接的地址。针对这类场景我的做法是准备一个定时构建任务每周把项目核心图表渲染成 SVG再批量转换成 PDF上传到内部知识库或者飞书/钉钉文档空间。替代手工截图的行为格式统一、内容永远是最新版。很多人忽略了一点SVG 是可以内嵌 alt 文本和 title 的。导出 SVG 时配合无障碍信息产品文档的可用性会提升一大截。反正输出 SVG 不用额外花成本这步不该省。5. 实战从零到一完成一张订单流程图的完整链路5.1 画图前先做信息整理拿一个最常见的场景来实操画“订单创建到支付完成”的核心流程图。很多人上来就开画这个毛病要改。我一般会先问自己三个问题读者是谁想让他得到什么信息什么可以不画比如这次图是给后端开发看的核心链路那就不用画前端页面的切换细节UI 的东西留到原型文档里去。信息整理阶段我会先在随便一个文本文件里把关键节点列出来创建订单、校验库存、锁定库存、支付下单、支付回调、超时关单、退款、履约。写下来的时候已经能感受到哪些节点是并列的、哪些是有条件的。5.2 图的起草、迭代与成型先给出第一版粗糙的 Mermaidflowchart TD A[创建订单] -- B[校验库存] B -- C[锁定库存] C -- D[待支付] D -- E[支付回调] E -- F{校验签名} F -- 成功 -- G[通知履约] F -- 失败 -- H[支付失败] D -- I[超时未支付] I -- J[取消订单] J -- K[释放库存]这个版本的问题很明显没有清晰的泳道边界判断节点不突出异常链路和主链路混在一起读者要在脑子里自己区分“正常路径”和“异常路径”。优化时我会注意三件事一是按子系统或角色把节点分组二是主路径尽量垂直向下不要左拐右拐减少视觉跳跃三是给异常分支加醒目的颜色。第二版flowchart TD subgraph Client[客户端] A[提交订单] end subgraph OrderSvc[订单服务] B[校验参数] C[校验库存] D[锁定库存] E[生成待支付单] F[发布支付回调事件] end subgraph PaySvc[支付服务] G{回调验签} H[更新支付状态] end subgraph StockSvc[库存服务] I[预占库存] J[扣减库存] K[释放库存] end A -- B B -- C C -- I I -- D D -- E E -- F F -- G G -- 验签通过 -- H G -- 验签失败 -- E H -- J D -- L[30分钟未支付] L -- K把节点归属到子图后服务间的调用关系从“谁挨着谁画”变成了“谁属于谁的边界”逻辑层级清晰了很多。虽然 Mermaid 的子图布局有时候还不够理想但语义层面已经正确了。5.3 渲染后的自查清单图渲染出来不要急着提交围绕美感和可读性做一轮自查每个子图是否有合理的边界节点是否散布在另一个子图内主路径是否一眼可辨异常路径是否用了不同的视觉权重标签是否统一用“动词对象”格式比如“校验库存”而不是“库存校验”有没有多余的“连接线交叉”交叉可接受但不必要的绕路会增加认知成本。我发现新人画图最容易出的问题是喜欢在一张图里塞太多分支。“失败重试三次”“超时分支”“人工介入”全画进去后图的复杂度瞬间爆炸。建议先把 80% 的常态路径画漂亮剩下 20% 的边界场景要么单独画一张异常流程图要么写成文字说明不要贪多。6. 那些文档不写、但一定会踩的坑6.1 中文字体、编码与跨平台渲染前面提过字体问题这里展开细说。你本地渲染正常换到 CI 或朋友机器上中文全变方块。这个问题在 Mermaid 和 PlantUML 里都经常出现本质是渲染环境的系统字体缺失。我的标准做法是把字体配置写进项目的mmdc-config.json并提交到仓库同时在 CI 的安装步骤里显式装中文字体apt-get update apt-get install -y fonts-noto-cjkPlantUML 的时序图里如果有中文除了字体问题还要注意skinparam里设置默认字体名称否则有些组件渲染出来中文间距会挤成一团。6.2 布局引擎偏执症很多朋友第一次用 Graphviz 画图看到引擎自动生成的布局不是自己理想的样子就开始疯狂加ranksame、constraintfalse、隐藏节点、不可见边最后整个图的脚本变成一坨没人能看懂的“布局补丁代码”。我的建议是不要和布局引擎较劲。如果你发现一张 DOT 图需要大量干预才能“看起来对”说明图本身的结构有问题——大概率是关系层级不够清晰。正确做法是调整子图分组和边的关系而不是疯狂打补丁。真正难以控制布局的场景我试过一个有效办法把图拆成多张小图不同视角展示不同细节而不是强行一张图承载所有信息。6.3 Review 流程里图表变更经常被忽视代码 PR 里如果改了.mmd文件评审人往往只扫一眼渲染出来的 PNG很难发现逻辑上的细微变化。为了降低评审成本我在 PR 描述里会写明“此次图变更新增了支付超时子图删除了误标的三次重试节点”而不是让评审人自己去 diff 图文件。更进一步可以用脚本把图表渲染成两张对比图用图片工具生成可视化的 diff 标注但这套方案成本偏高我目前只在大型架构图改动时用一般场景写清楚变更清单就够了。6.4 “图已过时”的定时清理机制代码库里的图也会像代码一样腐化。业务演进三个月后有些状态节点已经不存在了图却还留着。如果没人主动更新这张图反而成了误导源。我目前采用一个很朴素的办法每张图的源文件顶部放一个“最后确认日期”每季度集中清理一遍目录超过一年未确认并且确认已不用的移入archive/子目录或直接删除。当目录里只剩“活图”时目录本身的可信度才会提升。7. 让图真正“好用”的进阶设计建议7.1 用样式和颜色传递层级而不是装饰图里的颜色如果只是为了好看那它就是噪音。我在设计图表配色时会明确规定每种颜色的语义比如主流程用蓝灰色系异常/高风险用红色系外部系统用橙色系待定/未实现节点用虚线边框。这样读者不看文字光看颜色就能建立“这是主链路还是异常链路”的初步判断。视觉设计服务于信息表达而不是反过来。7.2 一张图只讲一个故事复述第四节的思路单图主题要窄。面对复杂系统我会倾向于提供“四件套”一张总览架构图页面级入口、一张核心业务流图时序/流程、一张状态机图模型边界、一张部署拓扑图物理视角。每张图解决一个特定问题读者才能按需取用。一张所谓“能展示所有信息”的总图本质上等于什么信息都没展示。7.3 可访问性现在就可以做起作为工程师图表的无障碍和可访问性很容易被忽略。给 SVG 添加 title 和 desc 标签、确保配色在色盲用户视角下依然可区分、避免仅仅依靠颜色传达关键信息这些调整成本都不高但对产品文档的体验提升是实打实的。Mermaid 里可以通过配置给图添加 caption生成的 SVG 中会包含相应的描述信息。细节做到位文档的完成度细节一下就拉开了。8. 一套我常用的 Mermaid 主题配置可直接抄作业最后分享一份我平时会复制的初始化配置放在.mermaid文件头部能少写不少重复的样式定义%%{init: {theme: base, themeVariables: { primaryColor: #E8F4FD, primaryBorderColor: #1E88E5, primaryTextColor: #1A1A2E, lineColor: #64B5F6, fontFamily: Noto Sans CJK SC, PingFang SC, Microsoft YaHei }}}%%这套配置解决了三件事中文字体显示正常、主色基调稳定可辨识、文本对比度适合浅色背景。换了新文件可以直接粘贴不用每次从零调样式。深色模式下可以把primaryTextColor调成浅色系themeVariables支持覆盖多数核心视觉变量。提示Mermaid 版本升级后配置字段可能发生变化。如果某段配置不生效先确认渲染版本再看官方配置文档不要盲目堆字段。我在实际使用中发现真正提升图表质量的关键是统一配置 固定规范 强制 CI 校验而不是“画的时候多用点心”。流程制度化之后每个人产出的图风格一致、结构清晰可信度自然就上来了。diagram-design 是一个值得长期投入的基建项。把图当作代码来管理表面上是工程习惯的改变本质上是对文档可信度的重视。如果你团队里的图还经常处于“没人敢改、没人愿意维护”的状态不妨按照我上面的路径慢慢调整先从一张核心流程图开始跑通 Mermaid 入 Git、CI 渲染、定期确认这条链路整体体验会彻底不一样。