代码化图表设计:用Mermaid把架构图变成可维护的软件资产
最近在帮团队梳理技术文档体系我发现一个很有意思的现象大家宁愿写一大段文字来描述模块间的调用关系也不愿意画一张图。问了一圈理由出奇一致——“画图太麻烦了调整对齐就要半天”。但文档里的架构描述一旦超过三句话阅读的人就开始眉头紧锁。后来我在一个内部项目里把图表全部改成了代码化设计统一用文本描述来生成图。这个思路就是项目名里的diagram-design把画图从“拖动鼠标微调”变成“写代码生成”让图表像代码一样可维护、可评审、可版本管理。这篇内容会完整拆解我在这个项目里的设计思路、工具选型、实操过程以及从混乱到规范化过程中踩过的坑。如果你也在为文档里的图表维护头疼或者想给团队搭一套图形资产体系这篇应该能给你一个可直接落地的参考。1. 核心思路为什么图表要用“代码”来设计1.1 从“画图”到“写图”一次思维转变传统的画图工具比如 Visio、ProcessOn、draw.io核心交互逻辑是“拖拽 连线”。你从左侧图元库拖一个矩形出来双击打字再拉一条线到另一个矩形。图好看不好看取决于你手动对齐的耐心以及同事之后维护时有没有同样的耐心。diagram-design 的思路正好相反图表的一切信息都由文本定义包括节点、连线、分组、样式。你面对的不是一块画布而是一段结构化的描述文本。听起来好像把简单事情搞复杂了但实际用下来收益非常大图表可以和代码一起提交到 Git 仓库评审时直接看 diff知道这周架构图里到底是哪个模块改了、哪条链路动了。这在传统画图工具里几乎做不到。传统方式下架构图的维护成本是“每一次变更都等于重画一遍”。而代码化设计之后维护成本被压缩到“改一行文本”。这个差异在项目前期不明显一旦图表数量超过十张、需要跟随架构持续演进的时候就是两种完全不同的体验。1.2 图表资产化把图当作代码来管理我在项目里定义了一个原则图表是软件资产不是一次性文档配图。既然它是资产就必须有版本、有评审、有变更记录。代码化设计天然满足这些要求。具体来说我把所有图表的源文件统一放在项目仓库的diagrams/目录下每个图表的源文件负责生成一张业务图。源文件是普通文本格式无法直接预览所以我配置了一个本地渲染环境改完源文件立即生成新图预览。整个过程和写代码完全同构新增图表就是新增文件修改图表就是修改文本废除图表就是删除文件。这个方式让图表真正进入了软件工程的生命周期。团队里不只一个人能维护图表不再是“这张图是谁画的就只能找谁改”。任何开发人员拿到源文件就能改改完通过同一个渲染流程产出新图。这种资产化能力是传统画图工具给不了的。1.3 diagram-design 要解决的核心问题这个项目的目标很明确解决软件开发团队在文档协作中图表维护成本过高的问题。具体拆解来看有三层诉求。第一层是“改得动”架构演进时图表能快速跟随调整而不是成为一篇永远过期的文档。第二层是“查得到”历史版本的图表能回溯知道某次架构调整是在什么时候、由谁、为了什么而改。第三层是“接得上”图表能融入现有的 CI/CD 流程和技术文档、API 文档一起构建出完整的项目知识库。这三层诉求落到技术选型上就会自然导向代码化方案。因为只有文本形态的源文件才能同时满足“可 Diff”“可版本控制”“可自动渲染”这三个特性。2. 工具选型四款主流代码化图表工具对比2.1 统一建模语言方案与轻量级文本方案的取舍选型之前需要先明确“代码化设计”不是一个单一工具而是一个工具家族。业内成熟的方案有好几类UML 工具链、Mermaid、PlantUML、Graphviz、D2 等。它们都能用文本生成图但设计哲学和应用场景差别很大。UML 工具链更严谨适合软件工程设计阶段Mermaid 与 PlantUML 主打文档内嵌Markdown 生态适配优秀Graphviz 的强项是复杂拓扑图D2 是新生力量语法设计更现代。我当时在 Mermaid 和 PlantUML 之间犹豫最久。两者都能满足代码化设计的需求但对应的使用者体验差异明显。为了不让团队成员产生“我用哪个都行为什么要学新工具”的抵触心理我最后用一张对比表把差异摆出来拉着团队一起做决策。对比项MermaidPlantUMLGraphvizD2语法难度低接近自然语言中等需要记住关键字较高node 与 edge 概念抽象低声明式语法清晰Markdown 原生支持优秀GitHub 直接渲染一般需插件不支持需额外配置尚可有社区插件复杂流程图一般较好强大良好时序图优秀优秀不支持支持类图 / 架构图支持强项一般良好布局算法固定难以手动调整可调但精细控制有限高度可控动画式布局较现代学习曲线最平缓中陡峭平缓2.2 最终选型为什么是 Mermaid 而不是 PlantUML团队最后选了 Mermaid核心原因有三个。第一是语法直觉化。Mermaid 的语法非常贴近英文自然语言比如A -- B就表示从 A 指向 B 的箭头没有多余的关键字。团队成员第一次看到示例代码不需要查阅文档就能猜到大概含义上手成本极低。第二是生态嵌入强。我们团队的技术文档平台原生支持 Mermaid 渲染这就意味着源文件和渲染结果可以共存于同一个 Markdown 文件里。代码评审时评审方看到的既可以是源码 diff也可以是渲染后的效果信息传递闭环在一个平台内完成。第三是渲染生态成熟。Mermaid 在 GitHub、Notion、Obsidian 等主流平台均内置支持本地只需要一个轻量客户端就能预览。选择 Mermaid 也付出了一些代价。最明显的是布局自动化的不可控性复杂图中节点偶尔会重叠。不过这个代价在团队场景里可以接受——我们做的是技术文档配图不是出版级制图。2.3 保留 PlantUML 的适用场景类图补充Mermaid 拿到图表设计的绝大部分工作但有一个场景它确实弱复杂类图的可读性。Mermaid 的 classDiagram 虽然能用但多继承关系、泛化实现的展示效果不如 PlantUML 清晰。所以我在方案里保留了一条补充路径类图、包图这些 UML 强相关的图形允许使用 PlantUML 绘制。对这个特例我在仓库里适配了一个 PlantUML 渲染环境和 Mermaid 主环境并存。这条“默认 Mermaid 特殊场景 PlantUML”的双轨方案既保证了 90% 场景下的统一性又不至于在 10% 的场景里硬凑。毕竟 diagram-design 的目标是让图表工作流顺畅而不是让团队为工具的教条买单。3. 实操过程从零到一落地 diagram-design3.1 准备基础环境这一步我直接基于 Node.js 生态来做因为团队本身就在 Node.js 技术栈上不需要额外引入异构环境。Mermaid 的本地渲染方式有两种CLI 命令行工具和 Puppeteer 渲染方案。我选择的是mermaid-js/mermaid-cli原因是它的核心逻辑是“用无头浏览器渲染 SVG/PNG”能最大程度保证本地渲染结果和线上 Markdown 渲染结果一致。安装过程很简单全局安装 CLI 工具即可npm install -g mermaid-js/mermaid-cli安装完成后还需要一个可视化编辑器辅助预览。这一步有不少选择我用的是 VS Code 的 Markdown Preview Mermaid Support 插件。它能在写 Markdown 的时候直接预览图表支持常见流程图、时序图、状态图等当团队协作时大家都能用同一套工具本地检查图表效果。3.2 设计源文件目录规范环境准备好之后先别急着画图而是定目录规范。这是整个项目里最容易被忽视、又最重要的一步。我在项目仓库里建立了如下目录结构docs/ diagrams/ src/ system-overview.md user-login-flow.md order-process.md images/ system-overview.svg user-login-flow.svg order-process.svgsrc/目录存放 Mermaid 源文件后缀名用.md方便 Markdown 编辑器直接预览images/目录存放渲染后的 SVG 图片文件。SVG 是矢量格式在文档系统里放大缩小不会模糊这是 PNG 做不到的。每个源文件的头部我都写清楚这张图的元信息项目名称、维护人、最后更新日期。这不仅方便追溯历史也让后来接手的人第一眼就知道这张图的归属范围。3.3 定义图表的统一风格团队协作里最常见的灾难是同一个项目的架构图五个人画出来有六种风格。有人喜欢蓝色系有人偏好圆角矩形有人非要把线做成虚线。所以 diagram-design 上线第一天我就定义了一份图表风格规范并且把样式参数固化到每一个源文件里。Mermaid 支持通过%%{init}%%指令进行主题定制。下面是我项目中常用的一段初始配置%%{init: {theme: base, themeVariables: { primaryColor: #4F81BD, primaryTextColor: #fff, primaryBorderColor: #2E5387, lineColor: #333333, fontSize: 16px }}}%% graph TD A[用户请求] -- B[网关层] B -- C[业务服务] C -- D[数据持久层]这段配置的作用是统一色板和字体大小。团队里所有图表都从同一份基础配置开始视觉呈现天然一致。这个细节看似简单但对文档整体的专业感提升非常明显。3.4 三层架构图完整示例作为实战案例我用 Mermaid 定义了一张典型的三层架构图。这张图满足大多数后端项目的架构描述需求可直接复制套用graph TB subgraph 展示层 A1[PC 前端] A2[移动端] end subgraph 接入层 B1[统一接入网关] B2[鉴权服务] B3[限流服务] end subgraph 业务层 C1[用户中心] C2[订单中心] C3[商品中心] end subgraph 数据层 D1[(MySQL 主库)] D2[(Redis 集群)] D3[(对象存储)] end A1 -- B1 A2 -- B1 B1 -- B2 B1 -- B3 B1 -- C1 B1 -- C2 B1 -- C3 C1 -- D1 C2 -- D1 C2 -- D2 C3 -- D1 C3 -- D3这个示例里包含一个关键设计subgraph分组的命名是有讲究的。展示层、接入层、业务层、数据层是按照请求流转顺序自上而下排列的没有使用默认的随机布局而是通过代码顺序控制渲染位置。这让整个架构图读起来有明确的方向感比一张扁平的节点图好理解得多。3.5 渲染输出与验证流程源文件写完之后需要统一渲染成文档系统可用的图片格式。我用 CLI 工具将.md中的 Mermaid 块提取渲染为 SVGmmdc -i docs/diagrams/src/system-overview.md -o docs/diagrams/images/system-overview.svg这条命令会解析指定文件中的 Mermaid 代码块并输出 SVG 文件。如果执行过程中遇到语法错误CLI 会明确指出第几行有问题方便定位修改。为了确保每次修改后的图表都不破坏整体效果我写了一个简单的校验脚本循环处理src/目录下所有文件。如果渲染无报错脚本继续有报错则立即停止并提示文件路径和错误行号这样一来图表变更就有了自动化的质量关卡。4. 核心细节解析从语法到布局的进阶技巧4.1 卡住无数新手的节点文本空格问题使用 Mermaid 时最先遇到的一个细节问题是节点文本里如果有特殊字符渲染就会报错或者显示异常。比如想定义一个文本为“用户登录成功”的节点直接写A[用户登录成功]没问题但文本里带上括号时比如“用户信息(缓存中)”直接写就会报错。原因是括号在 Mermaid 语法里是特殊字符被解释器当作语法的一部分解析了。解决办法是使用引号包裹节点文本graph LR A[用户信息(缓存中)] -- B[查询结果(正常)]这个看似很小的细节在真正投入使用时经常遇到。文本里只要出现括号、引号、百分号等特殊字符都优先考虑用引号包裹避免语法歧义。4.2 方向控制为什么我的图总是横着长Mermaid 的图方向是通过首行代码控制的graph TB表示从上到下Top-to-Bottomgraph LR表示从左到右Left-to-Rightgraph RL和graph BT则相反。团队里不少新成员开始时不注意方向定义画出来的图总是横着铺开和文档结构不协调。我的建议是绝大部分架构图、流程图统一用TB即从上到下只有时序、操作步骤类图才考虑LR。这是因为文档阅读习惯是自上而下的图的方向和文档结构保持一致阅读体验最顺畅。4.3 布局干预subgraph 的正确用法Mermaid 的自动布局在节点数量少时表现良好但节点一旦超过 10 个自动布局就容易出现连线交叉、节点重叠。这时最有效的干预手段就是subgraph分组。不仅是视觉上把相关节点放在一个框里更重要的是它引导布局算法——同一组内部的节点会被优先放置在一起。这就相当于告诉布局引擎“这些节点是一伙的别把它们拆散。”我在上一节的架构图示例中已经展示了 subgraph 的用法四个分层各一个分组内部节点只和所属层相关。如果没有这四个 subgraph12 个节点相互连线大概率会出现混乱的交叉。4.4 样式定制让重要节点在图中“会说话”一张架构图如果所有节点都长一个样阅读者很难凭视觉快速抓到重点。我在方案里总结了一套简单的样式优先级规则核心服务、关键路径用高亮色工具类和辅助类用浅色数据存储用特殊形状。Mermaid 支持在节点定义时直接附加样式类graph TD A[用户请求] -- B[鉴权服务] B -- C[核心订单服务] C -- D[(MySQL)] class B critical; class C highlight;然后通过样式定义让特定类显示为指定颜色%% 在 Mermaid 中通过 classDef 定义样式 classDef critical fill:#FF6B6B,stroke:#C92A2A,color:#fff; classDef highlight fill:#FFD43B,stroke:#E67700,color:#333;这样一来关键服务在图中一眼可辨评审人员扫一眼图就能找到链路里最重要、最脆弱的环节沟通效率大幅提升。4.5 注释与多人协作的“潜规则”代码化设计的一个巨大优势是可以在源文件里写注释。Mermaid 支持%%注释语法注释内容不会被渲染到图中但会保留在源文件里。我要求团队在每个节点的定义旁写清楚“这个节点是什么”以及“为什么存在”。这不是为了啰嗦而是为了降低后续维护者的理解成本。半年后回来改图的人你的注释就是最好的一手资料。一个符合团队规范的源文件头部通常长这样%% 系统总览图 %% 维护人: 张三 %% 变更记录: 2025.01.10 新增限流服务模块 graph TB A[客户端] -- B[网关]实践下来这个习惯带来的价值被严重低估了。它让图表源文件不仅是生成图片的脚本更成为一份轻量级的架构决策记录。5. 实际问题排查与解决方案速查5.1 渲染中文乱码的三种排查方向用 Mermaid 渲染中文最常遇到的坑是输出图片里中文变成方框或乱码。这个问题的根源通常不在 Mermaid 本身而在于渲染环境缺少中文字体。排查顺序建议从这三个方向走第一检查系统是否安装了中文字体Linux 服务器上尤其容易缺第二检查渲染容器的字体配置用 puppeteer 渲染时容器里有没有中文字体直接影响结果第三检查输出格式SVG 在浏览器里打开正常但图片格式乱码多半是字体解析链路问题。我的一劳永逸方案是在渲染环境里安装 Noto Sans CJK 字体。安装之后中文字体渲染基本不会再出问题。5.2 布局溢出图片被截断的解决办法Mermaid 在内容较多时生成的 SVG 尺寸会比预期大。直接插入文档时会出现图片被截断或显示不全的情况。这种情况的最直接解法是在渲染时设置更宽的画面尺寸。mmdc 命令支持-w和-H参数mmdc -i input.md -o output.svg -w 1200 -H 900如果你用的是 Markdown 原生渲染也可以在前面定义的初始化配置里加入%%{init: {theme: base, themeVariables: {...}, flowchart: {useMaxWidth: true}}}%%useMaxWidth: true会让渲染出的 SVG 宽度自适应容器。这个配置在文档站里非常实用避免大图撑破页面布局。5.3 节点文本包含特殊字符时的转义策略前面提过用双引号包裹文本但这还不够彻底。当文本里同时出现引号和括号时双引号方案也会失效。这时候可以把普通引号换成 html 标签转义方式或者使用 Mermaid 支持的最直接方案直接使用 html 字符实体。比如文本里有或就用lt;和gt;代替。我通常的做法是能不用特殊字符就不用实在要用优先引号包裹其次 html 实体。这个策略覆盖了 99% 的业务场景。5.4 缓存问题改了代码图没变Mermaid 的 Markdown 预览插件有缓存机制。不知道的朋友会以为代码没生效反复改代码结果图始终是旧版。遇到这种情况最简单的解决方法是强制刷新预览窗口。在大多数编辑器里关闭再重新打开预览文件即可。CLI 方式不存在这个问题每次渲染都是新进程但本地预览插件确实需要手动处理。所以我的团队约定写图阶段用插件实时预览确认无误后一律用 CLI 命令生成正式图片文件。最终发布以 CLI 输出的文件为准插件预览只作为过程辅助。这个流程避免了“预览时好好的、发布后发现不一样”的尴尬情况。5.5 语法报错的定位技巧当 Mermaid 代码有语法错误时错误信息通常比较简短有时很难一眼看出问题。我的习惯是把报错段落拆出来单独放到一个最小测试文件里渲染。这样能快速缩小问题范围。最常见的错误源有几个括号不匹配、箭头符号写了一半、节点 ID 重复定义。其中节点 ID 重复的问题比较隐蔽——它不是传统意义的语法错误但会让渲染结果出乎意料因为同一个 ID 会被后续定义覆盖。我给团队的建议很简单每个节点的 ID 用语义化的英文或拼音命名不要用 a、b、c 这种无意义字符。比如A[用户请求]可以命名为UserRequest[用户请求]。如果图足够复杂这样做的好处非常明显——至少你能在报错信息里看懂是哪个节点出了问题。6. 影响范围与项目延伸的思考6.1 从架构图到全文档体系的图形资产diagram-design 最初只用来做架构图但落地后发现它的价值完全可以复制到所有文档场景。需求文档里的流程图、测试报告里的状态流转图、运维手册里的部署架构图全部可以用同一套规范来管理。我把这套逻辑沉淀成了团队的一组模板并把它扩展成了文档规范的一部分。当所有图形资产都变得可追溯、可评审、可复用之后团队技术文档的维护效率提升非常明显。过去半年我没再听到有人抱怨“文档里的图过期了没人管”。对于正在考虑引入这套方案的朋友我的建议是三步走先选一个高频使用的图表场景试点跑通团队里最多人用的那个场景之后再逐步扩展适用范围稳定后再考虑把渲染集成进 Apps 的自动化流程里实现文档即代码的完整闭环。6.2 当前方案的边界与未来的扩展空间方案就完全没有问题吗不是的。它有几个明显的边界非常复杂的类图仍然建议用专业 UML 工具对像素级视觉有要求的对外宣传图也不适合用代码化方案还有就是初期强制团队学习语法总会付出一些效率成本。但我们换来的是长期主义图表从没人维护的“一次性用品”变成了能被持续迭代的“资产”。业务流程变化的快节奏之下这一点尤为重要。未来这个方案还可以演进的方向包括接入可持续集成的流水线让每次提交自动生成最新的文档架构快照沉淀一支团队级“图表设计系统”让新加入的成员也能上手产出风格统一的专业图更进一步可以尝试把图形源文件当作接口数据进行其他维度分析。整体来看diagram-design 这套思路的空间比想象中要大得多。最后再分享一点我个人在这个项目里的体会代码化设计图表的真正价值不在于让绘图过程变快而在于让图表重新进入工程化的管理轨道。当一张图能够被 diff、被回溯、被评审的时候它就不再是文档里一张会过期的图片而变成跟随项目一起演进的活资产。如果你也在为团队文档的图表维护头疼不妨从一张用 Mermaid 定义的核心架构图开始把画图这件事从“设计工具”迁移到“写代码”的轨道上来。