最近在梳理一个遗留系统的重构方案,评审会上有人把新架构图投到屏幕上,会议室里先是安静了三秒,然后就开始吵。有人指着某个方框问"这是进程还是容器",有人问"这两个箭头是调用还是数据流",还有人拿着旧图对比,发现同一块服务在两张图里画在不同位置。这场面我太熟悉了,几乎每个技术团队都经历过——图是画了,但画完没人看得懂。
做 diagram-design 这件事,本质上不是"画图",而是"设计沟通载体"。图从诞生那刻起就是给人看的,它要回答读者的疑问:系统分几块?依赖怎么走?哪里是边界?数据往哪流?如果图回答不了这些问题,那它就只是墙纸。这篇内容我打算把过去几年画架构图、评审架构图、以及给团队定绘图规范时积累的东西拆开讲,包括图层怎么分、符号怎么定、颜色怎么用、以及最要命的——图怎么才能不被扔进仓库吃灰。适合带团队的技术负责人、做方案设计的开发同学,还有刚入行需要学会"画一张能过关的图"的新人。
1. 为什么优秀的架构图这么少:先认清图纸和代码的本质差异
1.1 代码解决的是机器的校验问题,图解决的是人的认知问题
很多开发同学画图画不好,根子不在工具,也不在审美,而在思维方式。我们写代码写惯了,脑子里天然带着"线性执行"和"编译校验"这套逻辑。代码有语法检查,有类型系统,写错了编译器会报错,改坏了测试会翻车。但图没有这些——图是画布上的一堆矩形和箭头,没有任何东西会在你画错的时候弹窗警告。
所以画图的第一性原理是:你不是在画"系统写真",你是在画"认知地图"。这跟城市地铁图是一个道理。地铁图并不按真实地理比例尺画,它突出的是站与站之间的先后关系、换乘关系、线路归属。没有哪个乘客会拿地铁图去丈量公里数,乘客要的是"怎么从这站到那站"。架构图也一样,它的服务对象是人的大脑,不是机器。
我经常跟团队说一句话:"写代码的时候,你的甲方是编译器;画图的时候,你的甲方是十天后的你自己。"一张图如果放十天再拿回来看,自己都认不出那几个菱形是什么意思,那这张图的信息密度就是零。代码的精确性来自语法系统,图的精确性来自作者自己设定的符号体系——这就是为什么画图必须立规矩,后面我会专门讲符号规范。
1.2 一张好图的三个"验收标准":我评审架构图时的习惯判据
如果你问我,作为评审人,怎样快速判断一张架构图是好图还是废图,我会用三个标准过一遍。
第一是可读性。把图递给一个没有参与项目的同事,让他只看十秒,然后说出这张图在讲什么。说不出来,就是不合格。这条标准能过滤掉八成"自嗨型"架构图。我发现一个规律:新手画图特别喜欢把能想到的模块全画上去,美其名曰"完整",结果图面信息密度爆炸,主链路淹没在支线里,谁看谁晕。可读性的本质是"有主次",一眼扫过去能立刻抓住主干。
第二是准确性。图里的每个矩形、每个箭头都必须有明确的语义,并且和真实系统对得上。比如箭头到底是"接口调用""异步消息"还是"数据落盘",必须一一对应,不能一个箭头表示三种关系。我见过团队画的图,一个大框里面写"xxx中间件",下面拉了三根线,一根连数据库一根连接口,至于中间件具体起了什么作用,图上完全无法验证。这种图的危害比没有图更大,因为它让人误以为自己理解了系统。
第三是可演进性。图的维护成本要低,改一处服务拆分不用重画半小时。这点直接决定了图会不会死。我会问:如果这个月我们把消息队列从A换成B,画图的人需要动几个元素?答案是越少越好。那些用纯绘图工具手摆节点、画一个体系要一个下午的图,基本死得都很快,因为没人愿意维护。可演进性还有一个隐藏要求:图源文件必须能进版本管理,能被diff,能追历史。后面第五部分我会展开讲这个。
2. 分层而不是分好坏:业务图、系统图与部署图各画各的
2.1 先回答四个问题,再决定画哪一层
几乎每个团队都有一张"万能架构图",恨不得把所有信息都塞进去。结果就是不同角色的读者各取所需,谁也没法完整使用。解决这个问题的办法不是"画得更细",而是"分层"——把架构图按视角拆成业务层、系统层和部署层,每层为特定读者服务。
动手画图之前,先停下来问自己四个问题。
第一个问题:谁看这张图?业务方看的图,重点是角色、流程、规则;开发看的图,重点是模块、依赖、接口;运维看的图,重点是服务、资源、网络。读者决定抽象层级,这永远是第一优先级的决策。
第二个问题:读者看了这张图之后,要做什么决策?业务方可能是确认流程顺不顺,开发可能是确认拆几个服务、依赖方向对不对,运维可能是确认扩容点在哪儿。图的存在意义是帮读者做决策,不是帮读者"了解一切"。
第三个问题:哪些关系是这张图必须表达的核心关系?选最重要的两三种关系画出来,比如"调用关系"和"数据落盘",其他一律省略。一张图表达的关系类型越杂,理解成本就越高。
第四个问题:哪些细节可以下沉到下一张图?比如系统层的图里,某个服务内部的类结构、表结构完全不用画,那是应用层图的事。画图的时候心里要有一条原则:图是分层的,一层画不下所有细节,也不应该画下所有细节。
2.2 用订单系统的例子拆解三个层次各自的信息边界
拿大多数团队都有的订单系统来举例,三个层次画出来的东西截然不同,信息边界非常清晰。
业务架构图面向的是产品经理和业务方。这张图上画的是角色和规则:用户发起订单、支付系统确认收款、库存系统扣减库存、配送系统安排发货,以及超时未支付自动取消这条规则。它不需要出现任何技术名词,模块名字要写成"订单发起""支付确认""库存扣减"这种业务动作,不写"OrderService"和"Redis Lua脚本"。业务架构图回答的是"业务流程通不通"。
系统架构图面向的是服务端开发。这张图才出现 OrderService、PaymentClient、InventoryService、MQ 等具体的服务模块,箭头表达的是接口调用关系和异步消息关系,图中必须能看出"订单创建成功后通过消息触发库存扣减"这条链路。系统架构图回答的是"模块依赖合理不合理、有没有循环调用、故障会不会顺着调用链传播"。
部署架构图面向的是运维和SRE。这张图画的是服务跑在什么环境上,几个实例,挂在哪个网关后面,数据库是主从还是分片,Redis 是集群还是哨兵,日志往哪个采集端送。它回答的是"扩容在哪儿扩、故障在哪儿断、流量怎么进"。
这里有个团队里非常常见的通病:把三层画在同一张图上。图形上表现为——业务动作和内部服务出现在一起,逻辑节点和物理节点都画成一样的方框。读者看到这种图,大脑会强制切换视角,一会儿理解业务,一会儿理解技术,来回切换几次就放弃了。所以我强烈建议:一张图只干一件事。分三张图画,每张图都有明确读者,反而比一张"大全图"更受欢迎。
3. 布局、形状、颜色与命名:一张图让人十秒看懂的设计规范
3.1 方向、边界与箭头:语义先于美观
很多人画图栽在符号语义混乱上。先定一个大原则:图里每个元素的形状、边框、颜色,都必须代表一个固定语义,全图保持一致。语义一旦定了,就别因为"这个模块想突出一下"而随意改动。
先看方向。全图的主流程必须统一方向,要么从左到右,要么从上到下。从右到左或者从下到上的回流路径要画成明显的"支线",不能和主流程混在一起。主从左到右,返回路径就走下方回折,这也是阅读习惯决定的——人的视线天然从左往右扫,你把调用方向画反,读者得顺时针绕图一圈才能理清链路。
再看边界。容器的边框要能表达归属关系。比如一个服务内部的多个组件,必须用一个外框包起来,外框上标注服务名,内部才是组件节点。外部系统用另一种样式表示,比如粗边框或者不同的底色,让读者一眼识别"这个东西不归我们管"。边界画清楚了,图的上下文就清楚了。
箭头是架构图里最重要的元素,也是最容易被乱用的。我建议图例里至少区分三种箭头:实线箭头表示同步调用,虚线箭头表示异步消息,带实心圆点的连线表示数据落盘或读取。箭头旁边不要只图省事写"调用"两个字,最好把接口名或消息主题标注出来,比如"POST /api/v1/orders"或"topic: order.created"。这里也要强调一个反向情感:别画双向箭头。双向箭头表面上省了一条线,实际上把"谁发起、谁响应"的关键信息抹掉了,排查调用链时特别误事。
3.2 颜色和密度的"极简原则":视觉装饰是维护成本
颜色在架构图里是语义工具,不是装饰工具。我见过有人用八种颜色给每个模块上色,理由是"好看",结果一张图花里胡哨,读者根本分不清颜色的含义。我的规范是:一张图最多三种语义色,比如主链路用一种色,故障/风险路径用一种色,外部系统用灰色统一处理。其余元素全部用默认底色的黑白线框。颜色一旦承担语义,就不能随便改,否则维护的人会疯掉。
密度控制同样重要。认知心理学里有个"7±2"法则,人脑工作记忆能同时处理的组块数量有限。我把它延伸到架构图上:一个容器框里,能直观扫出来的节点别超过7个,超过7个就说明这一层抽象错了——要么该继续分组,要么该拆成子图。节点文字也尽量压缩,一个节点一行字,标注只用"服务名+关键接口",别把配置参数、KV列表全塞进去,那些细节放文档里。
还有间距问题。节点之间的留白不是浪费,而是给读者"分块"的空间。间距过小会让所有元素黏成一坨,难以分组阅读;间距拉大到足以看清边界就够了。我画完图有个固定动作:整体缩小到50%再缩略看一眼,如果缩略后还能分清模块边界,这张图的间距才是健康的。
3.3 命名与图例:跨团队协作时的隐形沟通成本
图画的读者往往不只是自己团队,还有上下游团队。此时命名的"口径一致性"就凸显出来了。同一个服务,代码里叫 order-service,你们团队内部叫"订单中台",图上又写"订单域服务",三个名字来回换,别的团队根本对不上号。我的建议是:一切以代码仓库名为准。图上凡是能对应到真实服务/数据库/消息主题的节点,命名必须和代码仓库、配置文件里的名字完全一致,最多在后面加括号写中文名。
图例不是可选项,是跨团队架构图的必需品。图例要声明的内容包括:每种线型的含义、每种颜色的含义、容器边界的含义、以及这张图对应的代码版本或更新日期。我通常会要求在图右下的角落固定放一块"图例+版本信息",哪怕只是一个文本标注,也能避免"这图是不是过期了"这种灵魂拷问。
跨团队协作时,我还有一个实际体验:同一个术语在不同团队里意思可能完全不同。比如"服务A调用服务B",在有些团队语境里包含RPC同步调用,在另一些团队里专指HTTP接口。所以在图例里加一行"调用=同步RPC/HTTP"这样的描述,看起来啰嗦,实际上能省掉很多合作时的扯皮时间。
4. 从草稿到成品的绘制流程:先关系后布局,先黑白后加彩
4.1 第一步:用文本把实体和关系写清楚
我画图的习惯和别人不太一样:第一步是不打开绘图工具的。我会先把要表达的信息用纯文本列出来,格式很简单,就是一张关系清单。比如要画订单系统架构图,我会先写:
- 参与者:用户、运营后台
- 服务:OrderService、InventoryService、PaymentClient、DispatchClient
- 存储:订单库、Redis(订单状态缓存)
- 关键链路:用户创建一个订单 -> 订单库写入 -> 发消息扣库存 -> 支付回调更新状态
- 外部系统:支付网关、配送开放平台
- 风险点:库存扣减失败需要补偿,图里要标出这条异常分支
这份清单就是图的"源代码"。它的作用是强迫你在画线之前先想清楚:到底有哪些实体,哪些关系是主干,哪些是支线。我见过很多同学直接开工具,画了删删了画,一个小时过去了,图和初始想法一样混沌——就是因为没有这一步文本梳理。文本写法跟代码里先写接口定义再写实现是同一个道理,先定契约,再画视图。
4.2 第二步到第四步:草图优化与视觉规范的具体手法
关系清单理完之后,我才会开白板工具画草图。草图阶段只看关系和布局,完全不管配色和样式。具体操作就是先按主链路顺序,把节点从左到右(或从上到下)排开,然后把清单里标出的异常分支、外部系统、存储节点放在主链路两侧。布局的核心目标是"交叉线最少"。两条线交叉一次读者还能反应,交叉三次以上基本就是要重画了。减少交叉的办法就是把一些边绕个大弯,或者把相关节点聚成一组再连线。
草图满意后,再进第三步:加容器边界。把归属同一服务的节点用外框包起来,外部系统放到边界线的外侧或底部区域。这一步做完,图的层次就出来了——读者能直观看到"哪些东西在我掌控内,哪些东西在外面"。
第四步才是上视觉规范:上色、定字体、拉间距。给主链路加语义色,给外部系统加灰底色,把文字大小统一成两级——一级是容器/服务名,二级是节点名和接口名。第四步和第三步之间有一个重要的中间检查:在加颜色之前,先把黑白版截图发给一个不懂项目的同事看,问一句"大概讲了什么"。黑白版能通过可读性测试,再上色才有意义。因为颜色是辅助信息,如果去掉颜色这张图就废了,那说明结构本身有问题,靠颜色救不回来。
4.3 工具选型的真实取舍:按图的"生命周期"选择载体
工具选型是我见过团队纠结最多的地方,其实没那么复杂,按这张图的生命周期来选就行。
第一种是临时讨论图。比如方案评审会上现场画的草图,生命周期只有一场会议。这种图用白板工具快速画,讲究的是"快"和"够用",画完拍照归档就完成了,不值得投入精致度。
第二种是沉淀型架构图。生命周期以月计,要给多个团队反复看,甚至进文档、上墙。这种图需要精细布局、规范符号、清爽配色,适合用矢量绘图类工具精雕细琢。但我必须提个醒:尽量少用"纯鼠标拖拽"型工具画这种图,因为后续维护的人要花大量时间对版本、挪位置、改线形,改一次半小时起步。
第三种是代码仓库里的"活图"。这是我最推荐用于核心架构的形态——用纯文本描述语言来定义图,再渲染成图形。这种方案的好处是图源文件就是几行文本,天然能进版本管理,能diff、能review、能pin到某个代码版本上,接口变了顺带改一行文字就行。团队里经常有人说"架构图老是过期",用这种活图机制就是我能找到的最优解。在把架构图当作工程资产而不是一次性画布的团队里,这种"文本即图"的做法可以算一劳永逸。具体选哪种,取决于这张图的维护频度——维护频度越高,越该往"文本化、版本化"的方向走。
5. 架构图的死亡方式与续命方案:给图做版本管理
5.1 图与代码的漂移:无人维护的架构图比没有图更糟
架构图最常见的死因不是画得丑,而是过期。我用"图与代码漂移"这个词来形容这个现象:代码每天都在变,架构图却是某个时点的快照,过了几个月就和真实系统对不上了。这时候如果有人拿旧图排查线上问题,照着图里的服务名和拓扑去找,结果发现服务早就拆了,链路早就改了——不仅浪费半小时,还会让整个团队对"架构图"失去信任。更可怕的是,团队一旦失去对图的信任,后续就没人愿意画图,也没人愿意看图,系统知识就全散落在个人脑子里了。
防止漂移的第一道防线,就是把图当成代码来管理。凡是最终进文档、进wiki、上墙的正式架构图,必须同时附带一个可编辑的源文件,并且放进版本管理仓库——绝不能只放一张导出的PNG。只有PNG的话,后人想改就只能重画,重画一次就劝退一个人,图自然就死了。
第二道防线是建立"图片更新触发机制"。比如每次做技术方案设计时,把"更新受影响架构图"作为验收清单里的一项;或者代码里某个核心模块发生变化时,PR描述里必须写明对应架构图是否过时——不更新图就不予合并。看起来严格,但坚持一年之后,团队的图基本上就处于"和代码同步漂移"的健康状态了。
5.2 常见坑位清单:评审架构图时我每次都会踩到的地方
下面这几个坑,是我这些年评审团队内部架构图时反复见过的,列出来给大家避雷。
第一坑:一张图混入多个抽象层次。容器和代码类画在一起,逻辑节点和物理节点同样式。这个问题我在第一部分反复提过,但每次评审还是会遇到。原因很简单:分层图要求画的人先想清楚读者,而很多人开画之前根本没做这一步。解决办法就是开头那四个问题,动手前必须过一遍。
第二坑:箭头语义不标。图上画了十几根线,全部只有箭头没有标注,你说它是调用也行,说它是消息也行,说它是数据流也行。箭头没语义,等于没画。排查问题的时候,谁也不知道这根线上实际跑的是什么协议,最后还是要回到代码里去翻。
第三坑:只画理想态,不标当前态。很多架构图画的是"重构完成后的样子",但现实是重构还在灰度阶段,半边旧系统半边新系统。图里不标出灰度区域,读者以为全部切换了,一排查发现还在走旧逻辑。正确的做法是:在图上明确标出"已切换""灰度中""待下线"三种状态,哪怕只是加个颜色点。
第四坑:循环依赖被"画没了"。代码里有循环调用是个问题,但架构图里经常看不到——因为画图的人潜意识会回避交叉,硬生生把A调用B、B调用A画成两根平行线放在图的两端,看着不交叉,掩盖了循环。我的做法是:如果系统存在循环调用,架构图必须用醒目的方式标出来,比如用风险色加粗。图是暴露问题的,不是掩盖问题的。
第五坑:过度装饰。有些图为了美观加了阴影、圆角、渐变、图标,好看是好看了,但改一次图要折腾半小时。装饰性元素不携带语义,徒增维护成本。我一直和团队说:架构图的美,应该来自清晰的结构和克制的配色,不来自Photoshop级别的装饰。
5.3 协作时的冲突解法与个人维护习惯
多人维护同一张架构图,最大的问题是互相覆盖。尤其在矢量绘图工具里,A同学挪了服务位置,B同学在那拖了个新节点,保存完整个布局乱了。我推荐的解法分两种情况。
如果图的版本管理做得好,团队直接用文本化方案协作,多人改图就是改文本,冲突交给版本管理工具处理,review的时候一行行diff下来,谁改了什么都清清楚楚。这是我认为的最优路径,唯一的门槛是画图的人要学一点文本语法,但那个学习成本比起"反复重画图"的成本,低得可以忽略。
如果团队暂时还离不开可视化工具,那就约定"区域负责制":每块区域的节点管理员固定一个人,其他人想改必须通过管理员,或者至少基于管理员的最新版本在自己分支上改。没有这个约定,多人同时开一张图一定会破坏布局。我见过的最痛苦场景,就是两个人花了一下午把一张架构图排得整整齐齐,第二天打开发现另一个同事导入了旧版本存档,整个下午白干了。
我个人的习惯是:图不求一次完美,但求每次变更都顺手。一个模块的文本描述改了,顺手更新图源文件里的一行字,整个过程不超过十秒,这才是架构图能长期活着的关键。那些画一张图花一整天、之后一年不碰一次的图,本质上只是给汇报用的装饰品,不是工程资产。
最后分享一个我自己坚持了很久的小技巧:每张正式架构图的右下角,永远留一行小字记录"最后更新日期 + 更新的PR编号"。这一行字,就是这张图的生命线。看到这张图的人能立刻确认它的时效性,维护它的人也能顺着PR编号快速了解到底改了什么。这个习惯看上去简单,却是我见过的所有"图纸管理"经验里性价比最高的一个。