搞技术文档的人大多低估了图的价值。架构图、流程图、时序图、状态机,看着不起眼,但新人上手、跨团队协作、方案评审,翻的第一样东西往往就是图。我最近梳理团队文档基础设施时,被“图过期”这件事反复折磨,最后决定把整套绘图工作流推倒重来,项目代号就叫diagram-design。它的目标非常聚焦:让图变成像代码一样可审查、可维护、可版本化的工程资产。
这篇文章不是某个绘图画图软件的教程,而是我从零搭建一套图解设计工作流的完整记录。如果你正在为“知识库里的图总是过期”“多人画图风格不统一”“改一个接口名就得重画整张图”这类问题发愁,那这篇内容应该能给你一份可以直接照抄的答案。文里所有参数、色值、脚本逻辑,都来自我实际跑过的配置,不是拍脑袋编的。
1. 为什么我把图解工作流搬进代码仓库
1.1 一张过期的架构图,是怎样拖垮团队效率的
事情的起因很普通。我们团队知识库里存了几十张架构图、流程图,全部是拿桌面绘图工具画完导出的PNG。刚开始用着还行,后来业务调整频繁,问题开始集中爆发。最典型的一次,线上架构从单集群拆成多集群,调用关系整个变了一遍,但知识库里的架构图还停在三个月前。一个新入职的后端同学照着旧图排查故障,顺着一条已经不存在的调用链查了两个多小时,最后才发现是图过期了。
那时候我意识到,问题的根源不是“大家懒得更新”,而是图这种内容天然长在工程上下文里。它描述的是代码里的模块关系、服务依赖、数据流向,可它一旦以图片形式脱离代码库,就失去了被维护的触发机制。代码有提交记录、有Code Review、有CI检查,图什么都没有。一张PNG被丢进文档目录,就相当于被遗弃了,没有人会因为代码变更自动想起去更新它。
1.2 图变成文本之后,三个红利会立刻显现
第一个红利是版本可Diff。以前改图是导出新PNG、覆盖旧文件,改了什么完全无迹可循。图变成文本之后,每次提交都能看到这次改动加了哪个节点、删了哪条连线、改了什么标签。代码评审的时候,可以像审代码一样审图,这个变化带来的效率提升非常直接。
第二个红利是风格可约束。团队协作最怕的是风格不统一,有人用红点表示故障,有人用红点表示重点信息,同一张图在不同人手里画出来像两个团队的产出。文本化之后,所有样式参数集中在主题文件里,字体、颜色、圆角、线宽全部统一。约定变成了默认,不再依赖每个人自觉。
第三个红利是内容可复用。公司公共的基础设施、环境标识、团队分组,这些元素几乎每张图里都会出现。传统画图方式需要反复复制粘贴,文本化之后可以抽成公共片段,新图画图时直接引用,既省事又保证一致。
1.3 但也有边界,不是所有图都该代码化
我并不是建议把所有图都改成代码。文本化图解适合表达结构关系、逻辑流程、时间顺序,比如系统架构图、模块依赖图、时序图、状态机。它不适合做像素级精细的界面交互稿,也不适合开会时随手画的白板草图。判断标准很简单:这张图是不是精确的、是不是需要长期维护的?如果答案是肯定的,就往代码化走;如果只是一次性沟通用的草稿,手绘比任何工具都快。
2. 选型实录:四类Diagram引擎,我最终留了哪个
2.1 主流的文本化Diagram引擎都在解决什么问题
选型是diagram-design项目最早遇到的分岔路口。市面上的文本化图表方案看着很多,但按设计哲学分其实就四类:底层图论描述型、类自然语言型、Markdown生态型和现代声明式DSL型。它们的理念差异远大于功能差异。
| 类型 | 代表方案 | 核心理念 | 明显优点 | 明显短板 |
|---|---|---|---|---|
| 图论描述型 | Graphviz/DOT | 用节点和边描述结构,布局完全交给算法 | 自动布局强,集群和层次表达成熟,稳定可靠 | 语法偏底层,画时序图非常啰嗦 |
| 类自然语言型 | PlantUML | 用接近英文的语句描述交互 | 时序图、用例图写起来特别快 | 渲染依赖Java运行时,样式定制空间有限 |
| Markdown生态型 | Mermaid | 紧跟Markdown文档生态 | 上手快,代码托管平台原生支持好 | 复杂布局容易乱,节点多了难以驾驭 |
| 声明式DSL型 | D2 | 兼顾可读性与美观的现代DSL | 语法干净,默认样式好看,布局算法现代 | 生态相对年轻,自定义形状还不够丰富 |
这个表格里的点评是我实际用下来的体感,不是从官方文档抄的。不同版本的细节体验有差异,但设计哲学上的差别是稳定的,选型就是要抓这种稳定差异。
2.2 为什么最终选了“主语言+备选引擎”的组合
很多人以为选型是选出一个“最好的”,我最后的方案恰恰不是这样。diagram-design最终定的是“主语言+备选引擎”:
- 架构图、层级图、集群图,用底层布局能力强的语言,因为它能精确控制分组、排序和边约束,结构图对布局的确定性要求极高;
- 时序图、用例图,用类自然语言表达,写起来最快,几行就能把一次完整交互描述清楚;
- 嵌入Markdown文档的轻量示意图,用文档生态兼容性最好的语法,让团队成员零成本参与修改。
有人会觉得混用工具会增加心智负担,我的看法正好相反。如果只保留一种语法,必然出现拿着锤子找钉子的情况,用一种很费劲的语法去画另一种类型的图,长期维护下来才是最累的。真正的关键是“一种用途只留一种首选方案”,并且所有工具的样式配置都收拢到同一套规范里。这样既不混乱,又能发挥各自的长处。
2.3 我判断一个Diagram引擎能不能入队的三个标准
第一,渲染质量能不能达到对外文档的水准。方法很简单,把同一张图用不同方案各渲染一次,放大到200%看细节,检查线条是否平滑、中文是否清晰、节点对齐是否工整。这一步能直接筛掉一大半工具,很多方案缩略图看还行,一放大就露馅。
第二,源文件的可读性好不好。图的源码最终会被团队里几十个人维护,语法越接近自然语言,协作门槛越低。我当时拿“订单服务调用支付服务”这样一句话,分别用四种语法写了一遍,直观感受哪种最容易理解。写出来之后,DOT的高密度语法和类自然语言方案的直观之间差距是非常明显的。
第三,自动布局在30个节点以上还能不能看。这是最容易翻车的地方。很多方案画10个节点的图很漂亮,画到30个节点,交叉线和重叠立刻失控。测试时我直接挑了一张40节点左右的真实架构图,观察谁能在不人工干预的情况下,布局最接近人肉排版的水平。这个标准筛完,基本就剩一两个候选了。
3. 一套立即可用的Diagram样式规范
3.1 版式与分层:一页图只讲一件事
样式规范的第一个维度是版式。diagram-design定下的规则是:单图核心节点不超过12个,完整节点不超过30个,超过就必须拆图。一开始有人觉得这是限制发挥,后来大家发现,能拆成“总览图加子系统详图”的架构,本身才是健康的架构。一张图想要装下所有东西,最后的结果一定是所有东西都看不清。
阅读方向默认从上到下,和大多数文档的阅读习惯一致。如果画的是数据流、链路追踪这类时间感特别强的图,就改成从左到右。规范里不允许同一张图混用两种方向,否则读者视线会被来回拉扯。边界分组用泳道或集群,数量控制在5组以内,超过5组,阅读时的记忆负担会明显上升,这个我是找人实测过的。
还有一个实战经验:图里的注释文字不要超过正文。图和文字是承接关系,不该互相抢戏。图里只保留服务于主要论点的标签,详细的解释放到图外的文档段落里。很多新手画图喜欢把能想到的说明都塞进图里,结果图变成了一篇带边框的文档,反而没人愿意看。
3.2 色彩语义化:给每种颜色一个固定身份
颜色是diagram-design里最容易失控的环节。我见过太多图,一张图里七八种颜色,而且同一个颜色在不同图里表示完全不同的含义,时间一长根本没人记得住。解决办法是建立一套语义色板,每种颜色绑定一个固定身份。
| 用途 | 色值 | 使用场景 |
|---|---|---|
| 主链路 | #2563EB | 核心流程、主调用关系 |
| 辅助信息 | #64748B | 次要节点、上下文说明 |
| 成功/健康 | #059669 | 正常状态、可用节点 |
| 警告/风险 | #D97706 | 待观察、降级、限流 |
| 危险/故障 | #DC2626 | 故障、异常路径 |
| 外部依赖 | #7C3AED | 第三方系统、团队边界之外 |
这套色板背后有三条硬性规则。第一,颜色不承担唯一的信息载体。比如“故障”这个状态,不能只靠红色表达,旁边必须有文字或图标配合,这是对色觉障碍者的基本尊重,也是防止图被灰度打印或黑白复印后彻底失去信息。第二,主色不超过3个,其余全是强调色,强调色在全图中的面积占比不超过10%。第三,所有图文件里不写死色值,一律引用色板中的语义名称,方便未来整体换肤。我们后来做过一次微调,把涉及安全合规的颜色统一替换了,只改了一处定义,全库所有图同步生效,这个回报直接证明了语义化色板的威力。
3.3 字体、字号与标签命名,决定图能不能被“扫读”
字体的坑后面会专门讲排查过程,这里先给结论。凡是最终要用于网页展示、投标文档、对外PPT的图,不要直接用系统默认中文字体,必须统一指定中英文回退栈,否则同一张图在不同电脑上会渲染出不同效果。我在主题文件里固定的是这样一组:
"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif字号规范也定得很细:图标题16px,节点标题14px,节点内说明文字12px,连线标签11px。连线标签是最容易被忽视的细节。很多人要么不写,要么写“调用”“依赖”这种完全没有信息量的词。diagram-design的规范是:连线标签要么写具体协议和接口,比如“gRPC CreateOrder”,要么写事件名,比如“Kafka order.created”,否则就不要写。与其写一个“调用”让读者自己猜调的是什么,不如留空,让读者顺着线直接看两端的节点,信息反而更明确。
节点命名统一成“名词短语 / 动词短语”的结构,例如“订单服务 / 创建订单”。这个格式在扫读时特别友好,眼睛扫到的第一个词知道这是谁,第二个词知道它在干什么,不用在脑子里做二次解析。这个规范看起来简单,实际对图的可用性提升非常大。
3.4 间距、圆角与线宽:细节参数决定专业感
这些表面参数,实际上决定了一张图有没有“设计感”。我定下的一组参数是:
- 普通节点圆角8px,集群和区域圆角12px,层级区分靠圆角大小,内外层次一眼可辨;
- 节点内边距16px乘8px,文字和边框之间留足气口,文字顶着边框的图会显得很廉价;
- 节点之间的最小间距24px,在布局参数里对应node separation;
- 主链路连线2px,辅助链路1.5px,异步或计划中的路径用虚线,线型承担一部分语义,但同样必须配合文字。
这些数值不是玄学。之后我曾把一张带完整参数的图发给前端同学,他在Web端用同一套参数复刻,渲染结果几乎一致。这说明只要参数统一,跨工具复现是完全可能的,这也让“设计规范”这件事变得可验证,而不是嘴上说说。
4. 复用与维护:让Diagram成为受控的工程资产
4.1 抽公共源:公司名、环境名、团队边界都不该硬编码
图会过期的第二个大原因,是图里硬编码了环境信息。diagram-design的规范是,环境名、公司内部基础设施、团队分组,全部做成公共模块,图文件里用引用方式引入。比如某个环境标识,在公共模块里是这样定义的:
env-prod: "生产环境" team-payment: "支付团队"作图时直接引用变量。将来部署架构从双环境演进成多环境,只需要改公共模块这一处,所有引用它的图自动更新。这件事在传统绘图软件里几乎做不到,也是我坚持代码化最充分的理由之一。
它还顺带解决了一个隐含问题:内部代号和对外名称不一致。我们团队内部习惯叫“pay-core”,对外文档写“支付核心服务”。之前每张图各写各的,同一张图里两种叫法并存,非常影响专业度。现在公共模块里建立一次映射,所有图统一显示对外名称,再也不会出现叫法混乱。
4.2 Review链路:图要进代码评审里,而不是进聊天记录
图进入版本库之后,自然就走代码评审流程。但这里有个体验问题必须处理好:评审者不一定看得懂图的源文本,直接让人工开源文件是灾难级的体验。我们接了一个自动导图环节,每次提交自动生成SVG和对应的位图预览作为附件,评审者在网页界面里直接看渲染结果,不用关心源文本长什么样。
我们还在项目说明里写了一条“改图三军规”:
- 可以改样式,但必须引用公共主题,不允许局部写死色值;
- 可以加节点,但总节点数超过30个必须拆图;
- 可以改连线,但连线标签必须使用具体协议或事件名。
这三条是从我们踩过的坑里直接浓缩出来的,每一条背后都有至少一次“图没人看得懂”或者“改一处引发三处问题”的教训。
4.3 CI里做三件事:语法检查、规模告警、颜色白名单
让图真正“受控”,不能靠人自觉,必须交给机器。diagram-design在持续集成流水线里挂了三个检查,代码量不大,但对团队习惯的约束力远比十页规范文档强:
- 语法检查:所有图源文件必须通过对应引擎的解析,语法错误直接让流水线失败,这保证入库的图一定能渲染;
- 规模检查:节点数超过30个、分组数超过5个,不阻断流水线,但打一个warning评论,提醒作者认真考虑拆图;
- 颜色检查:图中出现的所有颜色必须在主题色板白名单内,出现白名单之外的色值就是错误,直接阻断合入。
第三项检查上线之后的第一个月,全库图里揪出了17处“随手挑的颜色”。没有这个检查,这些颜色就会混进文档,逐渐腐蚀掉整个视觉体系。所以我的经验是,规范只有变成流水线里的自动化检查,才算真正有了执行力,停留在文档里的规范约等于不存在。
4.4 图和配置联动,从机制上消除“图过期”
进阶思路是一条后来被证明最划算的设计:如果一张图的内容本来就来自某个配置、某个服务拓扑、某个发布清单,那就不要让图和配置分开维护。我的做法是在文档构建流程里加一个小型生成器,读取配置文件,生成图的源文件,配置是唯一事实来源,图只是它的一个视图。新服务上线,配置更新,图自动更新,从机制上消灭了“图过期”。
这条对纯手工画的示意图不适用,但凡是结构关系能被结构化数据描述的图,都值得这么做。能把“维护一张图”这件事变成“维护一份配置”,对长期运营的文档体系来说,收益是颠覆性的。
5. 三个把我坑到凌晨的问题:中文字体、大图布局与导出清晰度
diagram-design落地过程中实际踩过的坑远不止这些,但下面三个最典型,从现象到排查到解决都有完整的链路,值得单独写下来。
5.1 中文字体:PNG导出后全是方块和乱码
现象是本地预览一切正常,一换到CI容器里导出PNG,所有中文变成方块或乱码。第一步排查先怀疑文件编码,检查后确认源文件是UTF-8存活,没问题。第二步怀疑引擎版本差异,来回换版本也没有改善。后来我直接登录CI容器,手动执行同样的导出命令,发现命令行里同样乱码,问题基本锁定:这个容器里根本没有中文字体。
根因是渲染端在Linux容器里没有安装任何中文字体,字体回退链条断裂,渲染不出字形就画成方块。解决办法分两层,容器层面安装中文字体包,我用的方案是安装Noto Sans CJK;项目层面在主题文件里固定字体栈。两层都做到位,才能保证本地预览和CI渲染一致。这里的最大教训是,排查问题要去问题发生的环境里手动复现,不要只在本地靠猜,那个过程会浪费大量时间。
还有一个额外的细节,中文字体文件普遍很大,动辄几十兆,安装字体包时别顺手装三四套,容器镜像体积会明显膨胀。一个Noto Sans CJK就足够覆盖多数场景。
5.2 大图布局:40个节点以上,图“能画但没法看”
30个节点以内的图,自动布局效果非常理想。一旦超过40个节点,连没有环路的拓扑都能被布局引擎画成一团毛线,交叉线遍布,逻辑关系完全被视觉噪声淹没。我一开始以为是参数没调好,花了大量时间调试间距、排序、边权重,只能说略有改善,不能根本解决。
最后得出的结论是:对复杂系统,正确做法是“一图一层,层间用链接”。第一张图画总览,每个子系统只保留一个节点,不展示任何内部细节;每个子系统单独一张详图,完整呈现内部服务和依赖;总览图的节点上带链接,点击跳到对应的详图。一张图画一个层次,而不是用一张图装下所有层次。这个道理听起来直白,但遇到真实场景时,克制住“一张图画完”的冲动是反人性的,需要规范来强制约束。
如果确实需要在单图里承担较多节点,布局参数值得微调。比如在DOT里,ranksep控制层间距,nodesep控制同层节点间距,默认参数在节点多了以后会把图拉得过长或过宽,略微调大这两个值,可读性会有肉眼可见的提升。但记住这只是辅助手段,不是根本解决,根本解永远是拆图。
5.3 导出清晰度:默认PNG在投屏和PPT里全是毛边
这个问题的现象在电脑屏幕上根本看不出来,直到有一次把架构图投到大屏上,文字边缘全是锯齿,粗细不匀,整张图显得非常廉价。检查导出参数发现,默认渲染用的是96dpi左右的位图分辨率,缩略图看着正常,一放大自然原形毕露。而且PNG一旦生成,后期想修成本很高。
diagram-design的规范后来定成三条:文档和评审环境里优先用SVG,矢量格式多倍放大不失真;必须用位图时,按2倍到3倍目标分辨率渲染,并且设置最小宽度阈值,我常用的是2400px;不要在聊天软件里直接粘贴位图,经过一次压缩画质会断崖式下降。导出脚本里直接把这些参数固定,不给人手动选择的机会,才算根治了这个反复出现的问题。
收尾:如果只做一件事,先把最重要的那张图画进仓库
diagram-design这套工作流落地三个月之后,团队知识库里的图从“没人敢改”变成了“随手可改”,对我而言这才是最有成就感的转变。回头总结,整个过程最关键的并不是选择了某个工具,而是想明白了三件事:图是工程资产,不是一次性产出;图需要像代码一样被Review、被检查、被版本管理;图的样式必须讲规范,规范必须被自动化执行。
如果现在的你也想在自己的团队里推行这套思路,我给的建议是不要从大而全的规范开始。先挑一张最重要的、改动最频繁的架构图,把它文本化,放进代码仓库,配上最简单的主题文件和一条自动导出预览图的脚本。跑通这一个例子,后面每一步该怎么走都会变得很清楚,而且你会自然获得向团队推广时最有说服力的实证。