1. 为什么大多数技术图只配叫“临时草稿”
先讲一个真实场景。某次方案评审,新来的同事用在线白板画了一张系统架构图。图上有六个模块、五条虚线箭头、三个数据库图标,配色用了四种高饱和度颜色。投影一亮,会议室安静了三秒,然后技术总监问了一句:“你能带我们按顺序走一遍吗?”
他做不到。那张图上每个元素都画了,但元素之间没有主次、没有分组、没有边界,箭头的含义也混着用——有的是调用关系,有的是数据流,有的是部署关系。所有人都在猜。最终评审结论是:方案整体可行,但图需要重画。
这不是个例。我在这些年看到太多技术团队把“画图”当成“写文档的附属品”,觉得图就是个示意,差不多就行。但一张技术图真正的身份是沟通协议:你和读者在同一个信息平面上,用统一的语法交换对系统的理解。画得不严谨、不统一、不克制,本质是在制造认知摩擦。
那什么才是一张合格的 diagram?
以我的标准,一张真正合格的架构图、流程图、时序图至少要做到三件事:读者能在三十秒内看懂主链路;每个元素的语义是确定的;图可以持续演进而不变形。三十秒钟不是一个夸张指标,评审会议上你留给每张图的时间其实就是这么短。三十秒内看不懂,后面讲解再多都会让人觉得系统不清晰。
这套标准听起来简单,实际操作中却很少被满足。我自己经历过从“随手画”到“较真画”的过程,也折腾过 Mermaid、PlantUML、Draw.io、Excalidraw 一堆工具,最终沉淀出一套相对稳定的工作流和约定。这篇文章就是把这些年和 diagram-design 相关的方法、教训、选型逻辑完整捋一遍,适合那些需要画架构图、业务流程、时序关系、模型关系,又不想让图变成一次性消耗品的人。
2. 选对工具,但更要选对图的语言体系
2.1 工具选型背后的核心矛盾
先解决工具问题,因为工具会直接影响你能画出什么程度的图。但我要先把话说在前面:工具只是载体,真正决定图的质量的是你有没有一套稳定的“图的语言体系”。这是我在不同团队里反复验证过的结论——用同一个工具,有人画出来是白板,有人画出来是工程蓝图。
画图工具的核心矛盾就两个:所见即所得的灵活度和“图即代码”的可维护性。灵活度高,意味着你能自由摆放每一个方框、连线、标签,但图一旦复杂起来就很容易变成一团乱麻;可维护性强,意味着图被人改造成本低,但语法学习和样式约束会限制你的表达。
这几类工具我用下来,感受非常不同:
- 白板类(在线协作白板、PPT):适合头脑风暴和早期讨论,灵魂是随意。但这类图只适合“讨论瞬间”,几乎不具备任何工程资产属性,因为没有强约束,图很容易演化成个人审美产物。
- 通用画图类(diagrams.net、draw.io 一类):灵活性极高,也支持文件格式管理。适合一次性交付的架构图,但需要画的人自己有极强的纪律性,手动对齐、手动分组、手动统一样式。团队协作时容易发生“有人用虚线框,有人用虚线圈”这种风格失序。
- 代码化绘图(Mermaid、PlantUML、Graphviz):图的表达方式从“拖动”变成了“写”,天然和代码仓库、文档、评审流程衔接紧密。这类工具的弱点在于布局和样式的控制力有限,想画得非常精美要费不少功夫。
我自己最终的选择是组合方案:设计阶段用白板类工具做发散,思考和推演阶段用图形语法类工具快速打底稿,最后一切需要长期留存、团队共用、进文档进代码的图,全部统一走“diagram as code”路线。不是因为它画起来最快,而是因为它让图变成了可评审、可追踪、可复用的一部分工程资产。
2.2 图不是“画”出来的,是“选”出来的
很多人打开工具就开始画方框,这是最大的一个误区。动手之前最应该做的一件事,是确定这张图的类型。
不同类型的图,传递的信息结构不同,读者动线也不同。把架构图画成流程图,把时序图画成圆饼,都会造成语义扭曲。我一般把技术图粗略分为四大类,对应的场景完全不同:
| 图类型 | 核心承载信息 | 典型问题 | 读懂需要的时间 |
|---|---|---|---|
| 架构图 | 系统的组成部分、分层、部署与依赖关系 | 模块职责不清、调用关系误用 | 通常 5-30 秒 |
| 时序图 | 跨对象或跨模块的交互顺序、消息往返 | 缺少超时/异常分支、角色划分混乱 | 需要按顺序读,1-3 分钟 |
| 流程图 | 业务流程、状态流转、决策分支 | 判断条件语义模糊、泳道缺失 | 沿主路径读,注意分支 |
| ER 图 | 数据实体、字段、关系与约束 | 关系基数标注被省略、主外键不标 | 需要对照业务理解,偏慢 |
一张好的 diagram-design,往往在开始前就已经知道自己属于哪一类,而不是画到一半发现“好像还得加个泳道”。
鼠标停留在工具箱上方时可以问自己:我想让读者看完后获得什么?是“系统由哪几部分组成”,还是“一次请求经历了哪些对象”,还是“数据模型之间是什么关系”?问题不同,图的类型就不同,接下来的画法也完全不同。
3. 五层拆解法:让复杂系统图一次成型
工具选定、图型确定后,我们终于进入 diagram 设计的核心方法论。这套五层拆解法是我在不断踩坑中形成的,适用于大部分系统架构图、跨模块流程图,希望对你有帮助。
3.1 第一层:确认读者
这一步在做任何图形之前。读者不同,抽象级别截然不同。给老板看的图,强调的是成本、风险、边界和系统全貌;给后端同事看的图,强调的是服务粒度、依赖关系、通信方式;给新员工看的图,强调的则是主链路和关键名词解释,也要把容错机制画得比实际更显眼一些。
我在真实项目上见过最惨痛的例子,是团队把一份面向内部开发者的部署拓扑图,原封不动放进对外技术方案里,术语没换、粒度没调,客户技术负责人一张图看了十分钟。沟通效果可想而知。
3.2 第二层:确定抽象级别
这个层级的核心原则是同一张图只表达一个抽象级别。最常见的问题就是一张架构图既要画逻辑架构(服务、模块、职责),又要画物理部署(主机、容器、网络端口、IP),结果图里既有“用户订单服务”这种逻辑概念,又有“nginx.conf 配置”这种物理细节,逻辑语义和物理语义混在一起。
我的做法是,如果系统复杂度高,我会主动拆成逻辑架构图、部署架构图、数据流向图等多张图,也不必试图用一张图解决所有问题。一张图信息过载,读者大脑就会自动降载,这时候真正重要的信息反而会被漏掉。
3.3 第三层:锁定主链路
任何系统都有主链路。确认读者和抽象级别之后,先画出主链路,再补充分支和细节。主链路是图的生命线,也是读者快速理解系统的第一根绳索。
以订单系统为例,主链路往往是:商家创建订单、支付回调、库存扣减、履约发货。先把这条链路用粗线画清楚,再把支付状态异常、库存不足、超时关单这些分支用虚线连接到主链路,图的骨架就立住了。
这个顺序不能颠倒。我见过很多图之所以看半天看不懂,不是细节缺失,而是细节抢占了视觉重心——分支画得跟主链路一样粗,配色一样亮,读者根本不知道从哪开始读。
3.4 第四层:组件分区与语义约定
当系统元素达到二十个以上时,不对组件做分区,视觉上就会失去控制。常用分区维度包括业务域分区、层次分区、环境分区三种。
业务域分区是按业务模块分,比如订单域、支付域、库存域;层次分区是纵向分层,比如接入层、应用层、服务层、存储层;环境分区是按运行环境分,如客户端区、网关区、内网区、外网区。不同团队可以选不同策略,但同一张图里只能有一种主导的分区逻辑,不能混用。
同时必须建立一套固定的语义约定。比如:
- 实线箭头表示同步调用,虚线箭头表示异步消息或事件通知;
- 方框代表有状态服务或模块,圆角框代表无状态逻辑;
- 红色/橙色高亮代表本次改造涉及的部分,灰色表示背景参考;
- 箭头方向必须遵守物理意义,数据流自动画从请求者指向被调用者。
这套约定一旦确定,就要固化下来并传递给团队。
3.5 第五层:细节密度控制
最后是细节密度。判断标准很简单:删除一个元素,若主链路仍然完整清晰,这个元素就不该出现在当前图上。我总结的密度阈值是:单张架构图元素总数尽量控制在二十五个之内,超过就需要考虑拆分或分层展开。
很多技术人画图有个冲动——把所有设计都塞进一张图里,觉得这样才显得细致。但图作为沟通载体,核心功能是对信息做粗颗粒度压缩,真正的细节应该放在文档文字或代码里。图上信息过密,读者注意力会被均摊,结果就是什么都读不进去。
4. 四种高频图型的专项约定与容易翻车的点
五层拆解法是通用框架,落到具体的图型时,每一种图都有自己特有的约束。这里挑四种高频图型的专项约定说透。
4.1 架构图:只画真实存在的依赖
架构图要做两件事:清楚展示系统由哪些部分组成,以及各部分之间如何协作。但实际项目里,很多图的问题恰恰出在这两件事过度表达上。
过度表达最典型的是箭头滥用。把“JVM 依赖 jar 包”画成“服务 A 调用服务 B”,把“数据库连接池配置了某库”画成“应用与数据库是依赖关系”,这把不同维度、不同性质的依赖混在一张图里,语义就被破坏了。
画架构图我的个人守则,一是所有连线必须能在实际代码或配置清单里找到对应物,画不出对应物的一律不画;二是先确认节点是否都是真实存在的边界,逻辑边界(进程、模块、层)和物理边界(主机、容器、集群)不混画在同一张图内;三是依赖关系要标方向,标注接口方式(HTTP、RPC、消息队列、数据库直连)。
一个我踩过的坑:某个业务架构图里画了一个“调度中心”,画得很大很核心。结果代码审计时发现根本没有这个服务,实际调度逻辑是散布在各服务里的定时任务。图上多出来的这个模块误导了全组人,花了两天排查才搞清楚任务到底怎么被触发。这张图从第一天起就是错的。
4.2 时序图:消息的“线”,是技术图里最需要诚实的部分
时序图的价值在于精确表达消息顺序。两条消息之间谁先谁后是语义的一部分,换顺序就意味着换交互逻辑。如果我只能给一条时序图约定,那就是:每条消息必须对应真实存在的接口、回调或事件,不能只是“泛泛的交互”。
画时序图还要符合以下约定:
- 参与者是对象、模块或系统,用矩形框表示,头部对齐;
- 每个参与者下方画一条生命线,消息按时间从上到下;
- 每条消息用一个箭头加标签标注“接口名(参数摘要)→ 返回”;
- 异步回调用半箭头,同步调用用实心箭头,返回消息用虚线;
- 交互中的超时、重试、异常分支必须画出来,不能省略。
我最常看到的时序图问题是“只有主路径,没有异常路径”。一个典型的分布式事务时序图,只画成功提交的正常流程,失败回滚完全没画。但真实系统若是失败回滚链条更长、参与者也更多,那张图的价值就只剩概念演示。
试过一个星期后回看自己画的时序图,如果发现已经看不懂当时为什么这样画,多半是消息标签没写接口名和关键入参。时序图不是流程图,它记录的是交互契约的演化过程,不写入真实契约信息,就只是自我安慰。
4.3 流程图:泳道与语义分层
流程图是日常使用频率最高、却也最容易画乱的一类图。我坚持的约定是:必须要有泳道,且泳道按“负责执行的人/系统/服务”划分。
没有泳道的流程图是一种“看似清晰、实则混沌”的状态:你知道下一步是什么,但不知道谁在执行这一步。而业务系统中最容易出问题的恰恰是责任边界——这个动作到底是用户在前端做的,还是后端服务做的,还是定时任务做的?加了泳道后,图上每一个节点都自动带上了责任主体。
流程图的节点语义也要统一。圆圈或圆角矩形代表开始和结束,菱形代表判断,矩形代表动作,平行四边形代表输入输出,箭头代表流转方向。判断条件必须写清楚“是”“否”两个分支,且每个分支要说明条件本身,不能出现一个菱形下面只连了一条线,没有分支的情况。
很多用户操作文档里的业务流程图错误,是因为把“前端页面展示”这个状态也画成了动作,整个流程就变成一堆方框的堆叠,因果非常混乱。我的经验是,画流程图时时刻提醒自己:状态的驻留不是动作,流转的驱动才是动作。
4.4 ER 图:关系是设计决策,不是装饰
ER 图常见的问题是只画了表和字段的关系,把关系行当成装饰线。但 ER 图最重要的信息是关系的类型和约束语义:一对一还是一对多,是否强引用,删除时是级联还是拒绝。
画 ER 图我会遵循这些约定:
- 每个实体框内列出关键字段,主键加下划线标粗,外键明确标注;
- 实体之间的关系线要标出关系两端的多重性,如1、0..、1..;
- 关系线上用名词短语描述关系语义,如“下属于”“包含”“引用”;
- 不要把数据库索引、分区、存储引擎信息塞进实体图,这些属于物理设计细节,应另开物理模型图。
ER 图的专业性体现在关系基数标注。我评审别人 ER 图时第一眼就找两个东西:主键有没有下划线,关系线上有没有基数标记。都没有,就说明设计还没有经过严格思考,很大概率存在逻辑漏洞,因为基数直接影响业务约束的落地策略。
5. diagram as code:把图变成研发流程的一部分
5.1 从“画一次”到“持续演进”
如果团队的项目图还在“用一次画一次、画完就丢”,那图的资产价值就大打折扣。图真正值得投入时间管理,是在系统持续演进、团队协作频繁的语境下。这时候“diagram as code”就不是一个装酷的选择,而是把图纳入研发流程的必然路径。
我用 Mermaid 这类文本化图语法的核心收益有三个:
第一,天然纳入版本控制。图的每次修改都是 diff 可追踪的,改了什么一清二楚,不像白板图改完就无迹可查。
第二,能够在评审流程中嵌入。图上流程可以和代码一起提交,同一次 PR 里就能看到系统结构和功能实现的对应。
第三,自动更新机制强。当架构变更时,任何人改了图文本,其他人 pull 下来就能同步看到最新状态。
5.2 图的存储位置与文档自治
图的生命周期管理有一个反直觉的结论,是我从实际项目中得到的:图最好和被描述的内容存得足够近,但控制权要足够集中。这意味着,架构图放代码仓库,跟设计文档放一起,跟部署配置放一起;但图的样式和语义约定要集中到一个团队共享的规范文件里,有人违反约定要直接打回。
常见的项目结构我推荐这样组织:
docs/ ├── design/ │ ├── system-overview.md # 总体说明 │ └── diagrams/ │ ├── order-flow.mmd # Mermaid 时序图源文件 │ ├── service-map.puml # PlantUML 架构图源文件 │ └──>鸿蒙应用模型选型:Stage模型与FA模型对比及迁移实践指南
1. 为什么开发者都在纠结 Stage 模型和 FA 模型前阵子在做一个鸿蒙应用的重构,Pod里一半人说“趁早切Stage”,另一半人说“FA跑得好好的,别折腾”。这种拉扯相信很多开发者都不陌生。新版应用市场审核、API版本兼容、多设备适配这些事摆到台面…
diagram-design:从结构到视觉,如何设计一张真正好用的图
1. 从“diagram-design”这个标题里能读出什么第一次看到“diagram-design”这个标题,我脑子里蹦出来的不是某个具体工具,而是一类非常典型的工作场景:有人需要把脑子里那张“说不清楚但确实存在”的结构图,变成一张能放进文档、能…
MPC高级转向控制:车辆动力学建模与实时约束优化
1. 项目概述:这不是“调个PID”就能糊弄过去的转向控制你打开某自动驾驶仿真平台,把车模放进环形赛道,拖拽一个“Steering Controller”模块进去,参数面板上只有Kp、Ki、Kd三个滑块——这是很多初学者对“高级转向控制”的第一印象…
UltraGrid打印深度指南:WinForms表格精准输出A4报表
1. 项目概述:为什么UltraGrid打印不是“点一下就完事”的事在某高校实验室开发一套设备状态监控系统时,我接手了一个遗留模块——用C# WinForms写的本地客户端,核心数据展示全靠Infragistics的UltraGrid控件。客户提了个看似简单的需求&#…
人类视交叉上核单细胞+空间转录组图谱:从建库到生物学发现全拆解
做单细胞和空间转录组分析这些年,我拆解过的脑区图谱不算少,但轮到把人类视交叉上核(SCN)的空间和单细胞转录组图谱完整过一遍的时候,我还是专门腾出了一整块时间。SCN不是什么网红脑区,大部分人可能一辈子…
代码评审流于形式?一套可落地的评审流程重构实践
我先把话说在前头:绝大多数团队的代码评审,根本不是“评审”,是“盖戳”。PR 一上来,reviewer 扫一眼,看到绿勾就 LGTM,看到代码风格不合意就丢两句“这里最好拆一下”“命名再想想”,改完合入&…