1. 从一张草图到一套系统:diagram-design 到底在解决什么问题
第一次看到 “diagram-design” 这个项目标题,很多人会以为它只是某个画图工具的皮肤或者模板集合。但真正做过系统设计、技术方案评审、跨团队对齐的人会立刻意识到:图不是画给自己看的,是画给别人看的。一张好的架构图、流程图、状态机图,能让一场两小时的会议压缩到二十分钟;一张糟糕的图,能让一群聪明人在同一个问题上反复绕圈。
diagram-design 这个项目,核心就是围绕“如何把复杂系统用图讲清楚”这件事,提供一套可复用的设计方法、结构规范和落地工具链。它要解决的问题非常具体:团队里每个人都会用不同的工具画图,有人用在线白板,有人用代码生成,有人直接截图 PPT,最后导致图与图之间风格割裂、语义不一致、维护成本极高。更麻烦的是,当系统迭代之后,图往往没人更新,慢慢变成“历史文物”,谁也不敢信。
这个项目适合三类人参考。第一类是技术负责人或架构师,需要频繁输出系统全景图、部署图、时序图,并且希望这些图能跟着代码一起演进。第二类是产品经理和项目管理者,需要把业务流程、状态流转、角色权限用图表达清楚,减少和研发之间的理解偏差。第三类是独立开发者和技术博主,需要一套低成本、可版本管理、能快速出图的方案,而不是每次打开笨重的桌面软件从头拖拽。
我自己的经历是,早期做项目时用纯手工拖拽工具画架构图,每次改一个模块名字就要重新对齐半天,后来换成代码生成图的方案,又发现团队里非技术同学完全无法参与修改。diagram-design 这类项目之所以有价值,是因为它在“手工拖拽的灵活”和“代码生成的严谨”之间找到了一个可操作的中间地带。接下来我会从整体设计思路、核心细节、实操过程、常见问题四个层面,把这件事拆开讲透。
2. 整体设计思路:为什么是“规范先行,工具后置”
2.1 先定义图的语义层,再选渲染工具
很多团队做图的第一步是打开工具,第二步是拖方块,第三步是连线。diagram-design 的思路完全反过来:先定义这张图要表达什么语义,再决定用什么工具渲染。这听起来像废话,但实际操作中,90% 的混乱都来自语义没定义清楚就开始画。
举个具体例子。你要画一个“订单系统”的架构图。如果直接开画,你会纠结:订单服务是一个方块还是三个方块?数据库要不要画在里面?缓存算不算一个独立节点?消息队列放在哪一层?这些问题本质上不是画图问题,而是系统边界和抽象层级的问题。diagram-design 的做法是先用一个简单的文本清单定义清楚:
- 图的类型:部署架构图 / 业务流程图 / 数据流图 / 状态机图
- 抽象层级:系统级 / 服务级 / 模块级 / 类级
- 关注点:数据流向 / 调用关系 / 部署拓扑 / 状态变迁
- 受众:研发团队 / 运维团队 / 业务方 / 外部合作方
这四行定义清楚之后,图的形状基本就确定了。比如“服务级 + 调用关系 + 研发团队”对应的就是标准的服务依赖图,每个服务一个方块,箭头表示调用方向,不需要画数据库内部表结构,也不需要画部署机房。语义层定义是 diagram-design 最核心的设计决策,因为它把“画什么”和“怎么画”解耦了。
2.2 用文本描述结构,用渲染器输出视觉
diagram-design 推荐的落地方式是:结构用文本描述,视觉用渲染器生成。文本描述的好处是天然支持版本管理、代码评审、差异对比。你改了一个服务名,git diff 里清清楚楚;你新增了一条调用关系,PR 里一目了然。相比之下,二进制图片文件的版本管理几乎等于没有管理。
文本描述的具体形式可以是多种多样的。常见的有:
- DSL 类:类似 PlantUML、Mermaid、Graphviz DOT 的领域特定语言
- 结构化数据类:YAML 或 JSON 描述节点和边,再用脚本渲染
- 代码注解类:在代码里用注解标记依赖关系,自动生成图
diagram-design 并不绑定某一种具体格式,而是强调结构描述与视觉渲染分离这个原则。你可以用 Mermaid 写流程图,用 Graphviz 画依赖图,用自定义 YAML 描述部署拓扑,只要保证结构描述是纯文本、可 diff、可评审的,就符合这个项目的设计哲学。
提示:不要试图用一种 DSL 画所有类型的图。流程图、时序图、状态机图、部署图各有最适合的表达方式,强行统一只会让描述变得臃肿难读。
2.3 分层维护:全局图、领域图、细节图
diagram-design 另一个关键设计是分层维护。很多团队的问题不是没有图,而是只有一张“大而全”的图,上面密密麻麻几百个节点,谁看谁晕。正确的做法是分三层:
| 层级 | 受众 | 内容 | 更新频率 |
|---|---|---|---|
| 全局图 | 所有人 | 系统边界、核心服务、外部依赖 | 季度级 |
| 领域图 | 领域研发 | 领域内服务、数据流、关键接口 | 月度级 |
| 细节图 | 模块负责人 | 类关系、状态机、时序细节 | 随代码更新 |
全局图只画到服务级别,不展开内部模块;领域图展开到关键组件;细节图才画到类和接口。这样每张图的信息密度可控,维护成本也分摊到了不同角色身上。分层维护的本质是让图的更新责任和系统变更责任对齐,谁改了什么,谁就更新对应层级的图。
2.4 为什么不用纯手工拖拽工具作为主力
手工拖拽工具不是不能用,而是不适合作为主力维护手段。原因有三点。第一,手工图无法 diff,改了什么全靠记忆和肉眼对比。第二,手工图无法自动校验,节点之间的引用关系错了也没人发现。第三,手工图无法批量生成,十个服务要画十张类似的图,只能重复劳动。
diagram-design 的立场是:手工工具可以用来做一次性演示图或高度定制化的视觉稿,但系统性的、需要长期维护的图,必须走文本描述加渲染的路线。这个取舍背后是对“图的生命周期”的清醒认识——图不是画完就结束,而是要和系统一起生长。
3. 核心细节解析:节点、边、布局、样式四要素
3.1 节点定义:粒度决定图的可读性
节点是图的基本单元。diagram-design 对节点的核心要求是粒度一致。什么意思?同一张图里,不能有的节点代表一个服务,有的节点代表一个函数。粒度不一致的图,读者无法建立统一的认知模型。
定义节点时,我习惯问三个问题:
- 这个节点在系统里是一个独立部署单元吗?
- 这个节点有独立的生命周期吗?
- 这个节点是受众关心的一级概念吗?
如果三个问题都是“是”,那它就应该是一个节点。如果只有部分为“是”,那它可能应该被合并到父节点里,或者拆成子图单独表达。
节点的命名也有讲究。diagram-design 建议使用领域术语加角色后缀的命名方式,比如order-service、payment-gateway、user-db。避免使用service1、moduleA这种无意义命名,也避免使用订单服务模块这种中英混杂且带冗余后缀的命名。命名的一致性直接决定了图的可搜索性和可讨论性。
3.2 边定义:方向、类型、标签
边是节点之间的关系。diagram-design 强调边必须携带方向、类型、标签三个信息。方向表示依赖或数据流向;类型表示是同步调用、异步消息、数据读取还是控制流;标签补充说明具体接口或协议。
很多图的问题出在边上。比如画了一条从 A 到 B 的箭头,但没说是 A 调用 B 还是 B 调用 A,也没说是 HTTP 还是消息队列。这种图在评审时必然引发歧义。diagram-design 的做法是给边定义一套最小标签集:
- 同步调用:
A --> B : HTTP /api/order - 异步消息:
A -.-> B : MQ order.created - 数据读取:
A --> B : SQL read - 控制流:
A --> B : trigger
这套标签集不需要很复杂,但必须团队统一。统一之后,任何人看到图都能准确理解关系语义,不需要额外解释。
3.3 布局策略:让图自己讲故事
布局是图的视觉呈现。diagram-design 不建议过度依赖自动布局,因为自动布局往往把图排得“均匀但无重点”。好的布局应该让读者一眼看到核心路径。
常见的布局策略有:
- 从左到右:适合数据流和调用链,入口在左,出口在右
- 从上到下:适合层级结构和部署拓扑,上层依赖下层
- 中心辐射:适合以某个核心服务为中心的依赖图
- 分组泳道:适合跨角色的业务流程,每个角色一条泳道
选择布局的依据是图的叙事逻辑。你想让读者先看什么,后看什么,布局就要引导这个顺序。比如画订单创建流程,核心路径是“用户下单 -> 订单服务 -> 库存服务 -> 支付服务”,那这条路径就应该放在图的中央水平线上,其他分支服务放在上下两侧。
3.4 样式规范:颜色、形状、线型的一致性
样式不是装饰,是语义的视觉编码。diagram-design 建议团队定义一套最小样式规范:
| 视觉元素 | 语义 | 示例 |
|---|---|---|
| 蓝色方块 | 内部服务 | order-service |
| 绿色方块 | 外部依赖 | payment-gateway |
| 灰色方块 | 数据存储 | order-db |
| 实线箭头 | 同步调用 | HTTP |
| 虚线箭头 | 异步消息 | MQ |
| 红色边框 | 待废弃 | legacy-service |
这套规范一旦确定,所有图都遵循。好处是读者不需要每次重新学习图的视觉语言,看一眼颜色和线型就能快速定位关键信息。样式规范不需要很复杂,五到八条足够覆盖大部分场景。
注意:样式规范要写进团队文档,并且在图评审时作为检查项。否则新人加入后很容易引入不一致的样式,慢慢就退化了。
4. 实操过程:从零搭建一套可维护的图系统
4.1 第一步:盘点现有图资产,分类打标
动手之前先盘点。把团队现有的所有图收集起来,按类型、层级、受众、更新频率四个维度打标。这一步的目的是搞清楚哪些图值得保留,哪些图应该废弃,哪些图需要重构。
我自己的经验是,一个中等规模团队通常有几十到上百张图,其中真正还在维护的不超过三分之一。大量图是“一次性产物”,画完就再也没更新过。盘点之后,把图分成三类:
- 活跃图:还在指导实际工作的,纳入统一管理
- 归档图:历史项目留下的,标记归档,不再更新
- 废弃图:已经完全不准确的,直接删除或标记废弃
这一步能砍掉大量维护负担,让后续工作聚焦在真正重要的图上。
4.2 第二步:定义团队的图规范文档
规范文档不需要很长,但必须包含以下内容:
- 图的类型清单:团队允许哪些类型的图,每种图的用途和受众
- 节点命名规范:命名格式、大小写、分隔符
- 边标签规范:关系类型、标签格式
- 样式规范:颜色、形状、线型的语义映射
- 分层维护规则:全局图、领域图、细节图的更新责任和频率
- 评审流程:新图如何评审,变更如何通知
这份文档是 diagram-design 落地的核心。没有规范,工具再好也会用乱。规范文档建议放在团队知识库里,并且在新人入职时作为必读材料。
4.3 第三步:选择文本描述格式和渲染工具
根据团队的技术栈和图的类型,选择合适的文本描述格式。下面是一个简单的选型对照:
| 图类型 | 推荐格式 | 渲染工具 | 适用场景 |
|---|---|---|---|
| 流程图 | Mermaid | 内置渲染 | 业务流程、审批流 |
| 时序图 | Mermaid / PlantUML | 内置渲染 | 接口调用、交互流程 |
| 状态机图 | Mermaid / PlantUML | 内置渲染 | 订单状态、工单状态 |
| 依赖图 | Graphviz DOT | Graphviz | 服务依赖、模块依赖 |
| 部署图 | YAML + 自定义脚本 | 自定义渲染 | 部署拓扑、机房分布 |
| 架构图 | Structurizr DSL | Structurizr | C4 模型架构 |
选型的原则是优先使用团队已有工具链支持的格式。如果团队已经在用 Markdown 写文档,Mermaid 是最自然的选择,因为大多数 Markdown 渲染器都支持。如果团队有专门的架构管理需求,Structurizr 这类 C4 模型工具更合适。
4.4 第四步:建立图与代码的关联机制
图要跟着代码走,就必须建立关联机制。diagram-design 推荐两种方式:
方式一:图文件与代码同仓库。把图的文本描述文件放在代码仓库的docs/diagrams目录下,和代码一起提交、一起评审。代码变更时,如果影响了架构,评审人可以直接在 PR 里要求更新图。
方式二:自动生成加人工校验。对于依赖关系图,可以从代码的 import 或注解中自动提取依赖,生成初版图,再由人工校验和补充。这种方式适合服务数量多、手工维护成本高的场景。
两种方式可以结合使用。核心图手工维护,辅助图自动生成。关键是图不能脱离代码独立存在,否则必然过期。
4.5 第五步:把图评审纳入研发流程
图评审不是额外负担,而是设计评审的一部分。在技术方案评审时,要求方案里必须包含对应的图;在代码评审时,如果变更影响了系统结构,要求同步更新图。这样图的更新就变成了研发流程的自然环节,而不是额外的“文档工作”。
我自己的做法是在 PR 模板里加一条检查项:“本次变更是否影响系统结构?如果是,是否更新了对应层级的图?”这条检查项看起来简单,但能显著提高图的更新率。
4.6 第六步:定期巡检和清理
图系统也需要定期维护。建议每季度做一次图巡检,检查以下内容:
- 是否有图引用了已下线的服务
- 是否有图超过三个月未更新但系统已变更
- 是否有图违反了样式规范
- 是否有图的信息密度过高,需要拆分
巡检的结果形成清理任务,分配给对应的负责人。图系统的健康度取决于清理频率,不清理的图系统会在半年内退化成一堆无人信任的图片。
5. 常见问题与排查技巧实录
5.1 图太多不知道从哪开始整理
这是最常见的问题。我的建议是从最痛的那张图开始。哪张图每次评审都被吐槽?哪张图每次新人来了都看不懂?哪张图每次系统变更后都要花半天重画?从这张图开始,用 diagram-design 的方法重构它。一张图跑通之后,再推广到其他图。
不要试图一次性整理所有图,那会让人崩溃。一张一张来,每张图都是一次方法论的实践。
5.2 文本描述写起来太麻烦,不如拖拽快
这个感受在初期非常真实。手工拖拽五分钟能画完的图,写文本描述可能要十五分钟。但区别在于:手工图改一次要重新拖五分钟,文本图改一次只要改一行字。图的寿命越长,文本描述的优势越明显。
我的经验是,一次性演示图用手工工具,长期维护图用文本描述。判断标准很简单:这张图三个月后还会有人看吗?会,就用文本描述。
5.3 自动布局出来的图太丑,没法看
自动布局确实经常不尽如人意。diagram-design 的建议是自动布局加人工微调。大多数文本渲染工具都支持布局提示,比如指定节点方向、分组、层级。花十分钟调整布局提示,能让图的可读性提升一个档次。
如果自动布局实在无法满足,可以考虑用文本描述生成结构,再导入可视化工具做最终排版。但这样会失去纯文本 diff 的优势,需要权衡。
5.4 团队非技术同学不愿意写文本描述
这是推广时的最大阻力。解决办法不是强迫非技术同学写 DSL,而是提供模板和辅助工具。比如为常见的业务流程图提供填空式模板,非技术同学只需要填节点名和关系,不需要学语法。或者提供一个简单的表单界面,填写后自动生成文本描述。
diagram-design 的核心是结构描述与视觉分离,不是必须手写 DSL。只要结构描述是可版本管理的文本,用什么方式生成的不重要。
5.5 图更新了但没人知道
图更新后需要通知机制。常见做法有:
- 在团队群里发变更摘要
- 在图的文件头部维护变更日志
- 把图嵌入到相关的技术文档里,文档更新时自然带动图更新
- 在 CI 里加检查,图变更时自动通知相关人
关键是让图的变更可见。不可见的变更等于没变更。
5.6 常见问题速查表
| 问题 | 可能原因 | 解决方向 |
|---|---|---|
| 图无人更新 | 更新责任不明确 | 把图更新纳入 PR 检查项 |
| 图风格不一致 | 缺少样式规范 | 定义最小样式规范并评审 |
| 图信息密度过高 | 抽象层级混乱 | 分层维护,拆分为全局图和细节图 |
| 图无法 diff | 使用二进制格式 | 改用文本描述格式 |
| 新人看不懂图 | 缺少图例和说明 | 每张图附带图例和上下文说明 |
| 图与代码不一致 | 缺少关联机制 | 图文件与代码同仓库,同步评审 |
5.7 我踩过的几个坑
第一个坑是过度设计规范。一开始我定义了二十多条样式规范,结果没人记得住,反而导致执行率下降。后来砍到六条,执行率反而上去了。规范要少而精,能覆盖 80% 场景就够了。
第二个坑是试图用一张图表达所有信息。我曾经画过一张包含所有服务、所有数据库、所有消息队列的“全景图”,结果这张图成了“谁看谁晕”的典型。后来拆成三层,每层只表达该层受众关心的信息,可读性立刻提升。
第三个坑是忽略图的上下文。一张图单独存在时,读者不知道它的背景、范围、更新时间。后来我在每张图的开头加了简单的元信息:图的类型、受众、最后更新时间、相关文档链接。这个小改动让图的可信度大幅提升。
第四个坑是没有定期清理。图系统运行半年后,积累了大量过期图,新人分不清哪些是当前的、哪些是历史的。后来建立了季度巡检机制,过期图及时归档或删除,图系统的信噪比才保持住。
6. 工具选型与团队适配的几点经验
6.1 小团队:从 Mermaid 加 Markdown 开始
五到十人的小团队,不需要复杂的工具链。Mermaid 加 Markdown 足够覆盖大部分场景。图文件放在代码仓库的docs目录下,和代码一起评审。渲染直接用支持 Mermaid 的 Markdown 编辑器或文档平台。成本低,上手快,维护简单。
6.2 中等团队:引入分层维护和样式规范
二十到五十人的团队,图的数量和类型都会增加,需要引入分层维护和样式规范。这时候可以考虑用 Structurizr 或类似的架构管理工具,把全局图、领域图、细节图分开管理。样式规范要写进文档,并且在图评审时检查。
6.3 大型团队:自动化生成加人工校验
超过五十人的团队,手工维护所有图不现实。这时候需要自动化生成加人工校验。依赖关系图、部署拓扑图可以从代码和配置中自动提取,人工只负责校验和补充关键信息。核心架构图仍然手工维护,但数量控制在可管理范围内。
6.4 工具选型的核心判断标准
不管团队大小,工具选型的核心判断标准只有三条:
- 结构描述是否可版本管理:必须是纯文本,能 diff,能评审
- 渲染是否可自动化:能集成到 CI 或文档平台,不需要手动导出图片
- 学习成本是否可接受:团队成员能在半天内学会基本用法
三条都满足,就是合适的工具。缺一条,长期维护都会出问题。
7. 图系统的长期维护心得
图系统和代码系统一样,需要持续投入才能保持健康。我的经验是,图的维护成本应该控制在研发时间的 5% 以内。超过这个比例,说明图系统设计得太重,需要简化;低于这个比例,说明图系统可能已经荒废,需要检查。
具体做法上,我习惯把图维护拆成三类任务:
- 日常更新:随代码变更同步更新,纳入 PR 流程,成本最低
- 月度巡检:检查图的准确性和一致性,发现过期图及时处理
- 季度重构:对信息密度过高或结构混乱的图进行重构,提升可读性
这三类任务的时间分配大概是 70%、20%、10%。日常更新是主力,巡检和重构是保障。只要日常更新做到位,巡检和重构的压力就会小很多。
另外,图系统的价值最终体现在沟通效率上。如果一张图能让评审会议缩短十分钟,一年下来节省的时间就非常可观。反过来,如果一张图每次都要花时间解释,那它就是在消耗而不是创造价值。定期问自己:这张图还在帮我们省时间吗?如果答案是否定的,就该考虑重构或废弃了。
最后分享一个我一直在用的小技巧:每张图的开头加一行注释,写明“最后更新人”和“最后更新原因”。比如// updated by A, reason: order-service split into order and payment。这行注释看起来不起眼,但当你三个月后回头看这张图时,能立刻知道它为什么变成现在这样。图的历史和代码的历史一样重要,只是大多数人忽略了。