ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

将图表当作代码:用文本化描述与工程化思维设计技术图表

将图表当作代码:用文本化描述与工程化思维设计技术图表 1. 项目核心为什么我决定好好设计图表而不是随手画一张这事儿得从一次让我很没面子的周会说起来。当时我负责的内核模块要做一次架构评审临时打开在线画板拉着框线画了几张模块关系图。图上箭头歪歪扭扭模块边界忽大忽小评审还没开始就有同事在底下小声问这个方块是代表一个类还是一个进程我解释了半天自己都觉得逻辑站不住。图表这东西一旦画得含糊它非但没有帮你把思路讲清楚反而会把原本清晰的逻辑搅成一团浆糊。后来我在实际项目里认真研究了 diagram-design 这个方向才意识到问题不在画图软件上而在设计图表这件事本身的工程化缺失上。很多人以为画架构图、流程图、时序图核心能力是会用工具会拖拽几个形状加几条连线。真实情况远不是这样图表设计真正要解决的是信息层级、视觉密度、阅读顺序、符号语义的一致性以及最重要的——更新维护的成本。如果一张架构图需要手动改十次才能跟上代码变化那它从诞生那天起就已经开始腐烂了。这个项目正好踩中了我所有的痛点。我把它理解成一套用文本描述图表、用规则约束图表、用工程手段维护图表的完整方案而不是某个具体软件的操作手册。它适合的人也很明确正在维护复杂系统的后端工程师、需要频繁输出技术方案的架构师、带团队做设计评审的技术负责人以及所有被画图一小时维护一整天折磨过的人。我在这段时间里把 diagram-design 的完整思路过了一遍也亲手搭建了一套可以落地的实践流程。这篇文章会把我的设计思路、工具选型、实操步骤、踩坑记录全部摊开来讲。不保证适合所有人但至少能让你少走我走过的那些弯路。2. 整体设计思路拆解从画图到设计图表的关键转变2.1 先分清你要画的是哪一种图我在最开始犯的错误就是一股脑把所有图表需求混在一起处理。架构图、流程图、时序图、部署图、思维导图它们的阅读逻辑和信息密度完全不同混为一谈做出来的东西必然是四不像。我给图表做过一个粗粒度的分类按信息传递方式来划分关系图表达谁跟谁相连比如服务间调用关系、模块依赖关系。这类图的重点是连线的方向和权重的表达。流程性图表表达事情按什么顺序发生比如业务审批流程、CI/CD流水线。这类图重点是节点状态和分支条件的完整性。结构展示图表达系统由哪些部分组成比如分层架构图、目录结构图、部署架构图。这类图重点是层级嵌套关系和边界的清晰度。状态与时序图表达某个实体随时间怎么变化或多个对象之间按什么顺序交互比如TCP状态机、一次HTTP请求走过的完整链路。这类图重点是时间轴或状态迁移的准确性。不同的图设计策略完全不同。关系图的布局要避免线条交叉流程性图表要强调泳道和分支的表达结构展示图必须严格控制层次深度不要超过四层不然视觉上直接糊成一团。状态图则是所有图表里最容易被低估的它看起来简单但画清楚需要非常严格的触发条件和状态描述。我建议你在开始任何一张图之前先花三十秒问自己我想让读者看完这张图之后脑子里留下哪一句话如果回答不出来说明这个图本身的需求还没成立。2.2 工具选型为什么我选择了文本描述这条路线市面上图表工具有很多从专业级的绘图软件、在线的多人协作白板到程序员熟悉的基于文本的绘图脚本各有各的受众。我在项目里首选的方案是文本化描述也就是类似代码一样用文本定义节点和连线、再由程序渲染成图的思路。原因很简单第一文本可版本化。只要图是用文本定义的它就能进版本控制库、能被对比、能回溯历史。改动一个节点下次提交就清清楚楚。这一点对技术方案设计几乎是刚需。我见过太多用可视化白板画的架构图改版五次后存档了五个版本文件根本说不清哪个是最新的。第二文本可复用。你可以像封装函数一样封装常用的节点组合。比如统一风格的服务模块、数据库、消息队列图形写一次到处引用从根上解决了风格统一的问题。第三文本可自动校验。这是很多人忽略的一个好处。如果图是用语法定义的脚本就能检查它语法是否规范甚至检查节点连线是否合法。比如不允许出现游离节点、不允许存在没有指向的箭头。可视化工具里画错了很难发现文本定义就可以轻松做自动化检查。不过文本定义也有它的学习门槛尤其是语法细节比较多一开始需要一点适应期。但只要你画的图是给团队看的、是长期维护的这点前期投入非常值得。2.3 设计的原则约束比自由更高效真正让我对 diagram-design 思路产生好感的是它对约束这件事的坚持。它不会给你无限自由去发挥而是用语法和规则把图画限制在几种合理的范式里。这个思路听起来反直觉但太对了。给一个工程师完全自由的画布他画出来的东西大概率是灾难。无限自由意味着每个元素都要决策这个圆角矩形放在哪这条线从哪里绕过去颜色用这个蓝还是那个绿决策越多出错越多维护成本越高。图表设计的核心不是你能画出什么而是你应该画什么、可以省略什么。文本化描述的基因天然内置了这种约束力它的连接方式由定义了算节点样式有默认值布局自动计算。你要做的只是把关系说清楚剩下的让工具帮你完成。我个人的经验是一套成熟的图表设计体系至少要包含允许画什么节点类型定义、允许怎么连连接合法性和默认长什么样视觉规范三个层面。没有这三个层面的约束一张图再精美也只是看起来好看谈不上可维护。3. 核心细节解析与实操要点3.1 节点与关系的语义设计比样式更重要的基本功很多人在画图时第一反应是选颜色、调宽度、换字体整个注意力都放在样式的调整上。但我做了几个项目之后发现真正决定图表质量的是语义设计也就是你把什么东西定义成一个节点以及你把什么关系画成一条连线。节点的定义粒度直接决定阅读成本。比如画一个支付系统的时序图用户、商户系统、支付平台、银行渠道这四个节点画出来大家一看就懂。可同样的系统如果我想把用户拆分成客户端App、浏览器端H5、小程序端三个节点图的复杂度瞬间翻倍信息量并没有等比例增加。所以我的经验是除了封面架构图这种需要完整展示系统边界的场景多数技术图应该采用角色化节点而不是实例化节点。连接关系的语义比样式更关键。一条线到底表达调用、依赖、继承、数据流还是配置关联必须在整张图里保持一致性。我见过最混乱的图一张图里箭头有的表示调用方向有的表示数据流向有的纯粹是视觉装饰阅读的人完全靠猜。规范的做法是在图的左下方或者图例区明确标注每种线形的含义并且全篇严格执行。线条的交叉和绕行也是被严重低估的问题。很多画图工具会自动布局但自动布局经常为了少交叉而牺牲阅读顺序最后画出来的图调查半天才找到逻辑入口。文本化描述的好处是你能手动控制节点的排列顺序可以把关系最紧密的节点放在相邻位置从根上减少交叉。3.2 信息层级一张图只讲一个故事一个非常普遍的错误是希望用一张图表达所有信息。架构图画了所有模块、所有接口、所有数据库、所有部署节点还试图顺带表现出调用频率高亮。结果就是一张图信息量爆炸什么都想说什么都讲不清楚。我现在的做法是严格遵循一张图一个核心信息的原则。如果要表达系统这么复杂的信息我不会画一张巨图而是拆成多张相互引用的子图一张系统上下文图只画外部系统、用户和当前系统的边界不画内部细节。一张容器图放大系统内部画出应用、数据存储、消息组件之间的关系。再往下拆组件图具体到某个应用内部有哪些模块模块之间怎么调用。信息层级是一个从粗到细的漏斗每一层只回答一个问题然后向下一层递进。这样做的好处是每张图的阅读成本都非常低读者能用最短时间抓住核心逻辑不感兴趣的细节直接跳过。对于单张图内部的视觉层级也有一个经验节点的视觉权重要和它的重要性成正比。画架构图时核心业务模块可以用实线边框辅助模块用虚线边框外部系统用更淡的填充色。读者一眼扫过去就能自动区分主次。3.3 从零搭建一张可维护的关系图这里说一个具体的实操案例。假设我要给一个微服务系统画一张核心服务调用关系图传统做法是打开画图工具拉十几个方框手动连十几条线我现在的做法如下第一步先用文本把所有节点定义出来。每个节点包括名称、类型服务/数据库/中间件、职责描述。这一步不需要考虑布局只需要保证节点清单完整。第二步定义所有的连接关系。每条连接包括源节点、目标节点、关系类型、说明。比如用户服务调用订单服务同步RPC超时500ms订单服务订阅消息队列的订单事件异步订单服务读写订单数据库主从集群第三步声明布局分组。把同一层级的节点放在一组比如接入层放用户服务、网关服务业务层放订单服务、支付服务、库存服务数据层放各类数据库。分组的意义不只是为了好看更是为了控制连线范围避免跨层乱连导致图面失控。第四步渲染输出然后做一次视觉走查。重点检查的是连线是否标注了方向、有没有没有业务的孤立节点、跨分组的连线是否过多。跨组连线超过总数的百分之二三十就说明你的分组逻辑可能需要调整。这一套流程看起来很朴素但它真正的价值是每一次修改都只需要改动文本里的几行而不是在画布上重新拖拽调整。经过几次迭代之后你会无限感激这个选择。3.4 色彩与密度的控制留白也是信息表达在配色这个问题上我给自己的硬性要求是一张图里的主色不超过三个。多一个颜色就多一分噪音。很多工具默认的配色方案花里胡哨看着很炫但对于技术图来说色彩的唯一作用就是区分类别和强调重点不是拿来装饰的。我常用的配色策略是这样默认色所有节点的基本填充色全部统一通常是中性灰或非常浅的蓝。强调色只用在当前这张图想让你关注的东西上比如核心调用链路上的关键节点。警示色表达异常、风险、不推荐路径一般是红色系使用频率非常低。在密度控制上我有一条很朴素的标准如果你需要眯着眼睛才能看清图上的文字那这张图的密度一定超了。不要纠结于在一张图里塞进所有细节细节可以在配套说明文档里补充图面永远是给阅读者降低认知负担的不是用来炫技的。3.5 结构展示图的层次控制结构展示图最容易犯的毛病是把层次画得过深。系统设计里有一种流传很广的说法任何一张架构图的层级不要超过三层超过三层读者就记不住了我非常认同。画分层架构图时我一般只保留三层上层用户端或接入层中间层核心业务逻辑层底层基础设施或数据层三层之外的东西不要画在同一张图里。如果确实需要展示更深的结构可以在文字旁边标注详见组件图。另外结构展示图里嵌套边界的使用要克制。用一个大方框圈住一组内容的做法很直观但嵌套层级一旦变多视觉上就变成了俄罗斯套娃反而很难阅读。一个经验法则嵌套边界不要超过两层。外层表示分组内层表示模块再往里的细节交给文字说明或单独的子图。4. 实操过程与关键环节实现4.1 明确画图目标的步骤在做任何一张技术图之前我现在的固定流程是先写一段画图宣言这是在一个技术群里跟一位前辈学到的思路。这段文字通常包含四个要素这张图的读者是谁、核心要传达的信息是什么、图里必须出现哪些元素、明确不画哪些元素。比如画一张电商系统的部署架构图读者运维团队和刚刚加入项目的新人。核心信息所有服务都跑在容器编排平台上通过统一的接入入口对外提供服务外部存储都放在云服务商托管集群上。必须出现的元素接入网关、服务节点约十二个、配置中心、日志系统、主从数据库、消息队列集群、缓存集群。不画的东西具体的POD副本数、资源配额、网络策略细节。有了这段宣言整个画图过程就有了边界。每新增一个元素我都会对照宣言问自己它是必须出现的吗如果不是克制住画上去的冲动。很多图之所以越画越乱就是因为画的过程中不断有新想法涌进来顺手加一笔的习惯最终毁掉了整张图的清晰度。4.2 定义并统一你的图例我现在每画一张正式的技术方案图都会在图旁边配一个图例区。别小看这个细节它几乎是最能体现专业度的地方。图例区一般包含节点形状的含义说明圆角矩形表示服务圆柱表示存储菱形表示决策判断实线箭头和虚线箭头的含义区分同步调用 vs 异步事件强依赖 vs 弱依赖颜色标记的含义核心链路高亮、故障节点标记把这个事情讲得夸张一点图例是图表领域里的接口文档。没有图例的图就像没有注释的接口调用方只能靠猜猜对了是运气猜错了是常态。维护了图例之后哪怕隔了三个月回头看这张图也能很快重新进入上下文。颜色和线型定义出来之后就要严格执行。我见过有人图例里写了红色表示异常下面内容里又用红色框标注了一个正常的新业务模块这种不一致会让读图的人瞬间对整张图的信任度归零。4.3 从文字描述到图表的翻译方法文字描述和图表之间有一条鸿沟能把文字顺畅翻译成图表的人技术表达能力通常都不会差。我总结了一套从需求文档到图表映射的翻译思路。第一步把文字稿拆成主语-谓语-宾语三元组。主语是动作的发起者谓语是交互动作宾语是交互的目标。比如订单服务调用库存服务扣减库存拆出来就是订单服务-调用-库存服务。第二步识别角色的先后顺序和并行关系。如果文字里有同时、异步、而在之后这类词说明流程存在分支需要在图里画成条件判断节点或分支并发节点。第三步识别边界条件和异常路径。大部分第一版图只画了happy path完全没有画异常处理。真实系统里异常路径往往更值得花笔墨去表达。比如支付超时、库存不足、消息重复消费这些分支画进去之后图才真正有了工程价值。第四步也是我自己后来才领悟到的关键点——试着用一句话把整张图的内容说出来。如果说得出来图大概率是清晰的如果一句话说不出来说明这张图的目标还是模糊的。4.4 图表生成的自动化实践我在一个内部工具类项目里实践过一个小型自动化流程思路对你有参考价值。整个流程是研发先撰写文本化图表的定义代码提交到代码库持续集成流水线里放置了一个校验任务它会自动检查图表语法的合法性、是否有孤立节点、是否有未标注方向的连线校验通过后自动渲染成图片文件发布到内部文档站上。这套流程跑通之后效果非常明显。之前团队里经常出现文档上的架构图和实际代码已经对不上的问题。现在每次图表的变更都需要走一次代码评审相当于架构变更也有了一个轻量级的检查关卡。图表不再是一张事后补画的图片而是和代码一样值得维护的一等公民。这里顺便说一句自动布局。自动布局算法这两年进步很大但完全依赖自动布局去设计大型图表仍有风险。自动布局擅长做小图节点多了之后它倾向于把图面撑得很宽阅读效率反而下降。我现在的做法是小图节点少于十五个放心交给自动布局大图手动指定分组结构和关键节点位置再让工具在组内做微调。这个组合算是兼顾效率和可控性的一种解法。4.5 不同绘图场景的具体落地方案对比为了让你有个更直观的判断我把日常最常见的几种需求场景和推荐方案整理了一下场景核心需求推荐的图表定义方式备注系统架构评审表达模块边界、依赖关系文本定义 分组布局重点保持分层清晰控制跨层连线业务流程说明表达分支判断、泳道职责文本定义 泳道分组按角色或系统划分泳道避免跨泳道连线过多接口时序说明表达对象间消息顺序DSL风格脚本把消息描述清楚由工具自动排列时序部署架构展示表达物理节点和网络区域文本定义 区域嵌套嵌套不超过两层额外标注安全边界快速白板沟通快速传达想法给身边人手绘或白板工具沟通用途不必追求精确规范重点在速度这个表格的表达泳道一词在文字说明里没有配合图和图例去解释清楚的话容易有歧义实操时我会在泳道名旁边加一行文字说明每个泳道代表一个角色/系统。文字描述永远是对图表最可靠的补充。5. 复杂图表的设计实战与拆解5.1 一个完整时序图的表达方式时序图在技术文档里用的频率非常高但也是被画得最糟糕的图之一。很多人画时序图就是画几条竖线加几个箭头完全不考虑生命线虚线那条纵轴的语义也不标注返回值和异常分支。我在项目里画时序图时采用的是由脚本文本定义的方式。我会先定义参与交互的对象列表再定义消息序列每条消息包含发送方、接收方、消息名称、说明文字。关键字和描述的组织尽量贴近于可读的文本语言比如用户请求下单接口订单服务校验用户权限订单服务创建本地订单记录订单服务发送创建事件至消息队列事件消费者读取订单消息库存服务监听事件并扣减库存定义好之后工具会根据消息顺序自动生成时序图并且自动处理生命线的激活区间。我只需要在出现分支或异常的地方额外写上条件标注即可。这个方式最大的好处是修改一次消息列表重新渲染就是一张新图不存在拖拽连线拖到怀疑人生的情况。涵盖的细节越清晰生成的时序图越有说服力。5.2 架构图里的层次拆分方案画复杂系统架构图时除了我前面提到的一张图一个核心信息还有一个特别重要的实操技巧先画边界再填内容。这个顺序很多人是反过来的。他们先画了一堆节点最后随手拉一个大方框把它们圈起来。这样画出来的边界往往是凑出来的不能准确表达分组逻辑。正确的顺序是先确定这个系统要分几个大区。比如客户端区、接入区、业务区、数据区、基础设施区先把这些区域用大方框或泳道画出来每个区域在图上占据固定的位置。再去考虑每个区域内部放什么节点、节点之间的交互是什么。这样画出来的架构图整体骨架是先行的逻辑上自然成立。边界和区域画完之后一定要检查一件事区域之间的跨区连线是否过多。跨区连线过多说明你的区域划分维度可能不对。一个经典案例是按业务模块分区的系统图如果模块之间的相互调用非常密集改成按调用链路分层来表达反而更清晰。5.3 状态图的拆分与简化技巧状态图是所有图表里最容易被过度设计的。很多工程师画状态机能画到二十多个状态最终图面变成一张密密麻麻的蜘蛛网。我在实践中体会到的状态图设计原则是状态收敛事件分离。状态收敛指的是把那些可以被同类事件驱动的近义状态合并。比如支付中、等待支付结果、支付确认中可以合并为一个支付处理中。它们之间的差异对系统使用者来说没有感知上的区别合并掉之后图面立刻清爽很多。事件分离指的是状态迁移虽然可能在同一个状态上有多个触发事件但在图的表达上要把它们拆开标注清楚是哪个事件导致这次迁移。比如从待支付状态可以迁移到已取消用户主动取消和已支付支付成功回调这两个迁移事件不同状态也不同必须画清楚不能画成一个模糊的箭头。画状态图的另一个经验是把非法的迁移路径也画出来。我见过很多状态机画了合法路径但完全不提哪些迁移是禁止的。导致开发和维护者误以为系统支持那些迁移代码实现时就会产生隐藏Bug。正确的做法是用警示色标注非法迁移路径或者配一份状态迁移矩阵表列出所有合法与非法组合。5.4 不同渲染方式对大型图表的支持对比选型阶段我做过一组对比实验针对的是一张包含约五十个节点、八十条连线的系统关系图。使用文本描述类工具时渲染出来的SVG图片非常锐利缩放不失真浏览器里看很舒服。使用在线白板类工具时手动排布五十个节点非常痛苦自动布局出来的效果又稀疏阅读时需要在不同区域之间来回跳转。使用专业绘图软件时功能确实强大但版本管理完全靠手动存档多人协作时经常出现互相覆盖的情况。我的结论是对于大型图表可靠性从高到低的选型顺序是文本定义渲染 专业绘图软件 在线白板。而对于小型快速沟通图表在线白板的效率可能更高。选型没有绝对优劣关键看你的图是给谁看的、要活多久。6. 常见问题与排查技巧实录6.1 对齐与排版的杂症画图过程中最典型的问题是节点对齐混乱。文本框忽高忽低、连线歪歪扭扭节点间距忽大忽小整张图看起来就像喝醉了一样。多数画图工具都提供对齐辅助线但很多人要么没注意到要么嫌麻烦不用。我的建议很简单所有节点之间的间距保持一致所有同层节点的高低保持一致。这两个一致做到位哪怕配色一般、字体普通图的整体观感也不会差。应对方法是在画完后横向扫视整张图如果你的视线能找到一条清晰的阅读路径这张图的排版基本过关。如果需要左右上下跳来跳去那就要重新调整节点布局了。6.2 中文字体与等宽对齐的问题这一点专门提醒用文本定义方式画图的同学。很多文本化图表工具在计算节点宽度时默认假设每个字符等宽。但中文字符在渲染时通常比英文字符宽如果不做处理中英文混排会导致节点宽度和文本宽度对不上最终渲染出来的布局错位。我踩过的坑是在图表定义的节点里写了一段中等长度的中文描述渲染出来节点宽度被撑爆另外一些节点又因为宽度计算不足导致文字溢出边框。排查这个问题的套路是优先把节点名称和描述拆成短词避免一个节点里塞长句子必要的时候给工具明确设置中文字符的渲染宽度系数。如果工具不支持中文字符宽度设置我会尽量减少节点内的中文字符数量更详细的信息放文档不在图上堆字。6.3 游离节点与悬空连线游离节点指的是没有任何入边和出边的节点悬空连线是只有一端连接在节点上、另一端漂在空中的线。游离节点的出现通常是因为修改图表的时候删掉了某个连接关系但忘了删除节点本身。这类节点不表达任何有效信息只会给读图的人造成困惑。排查方法是利用脚本工具在持续集成流水线里加一个检查任务全图扫描统计每个节点是否有至少一条边连接。发现游离节点就返回失败并输出节点名。同理悬空连线的检查也可以在命令行里跑一个脚本统计图中线条的首尾端点是否都能找到所属节点找不到就报警。6.4 版本迭代时图表与代码经常脱节图表最怕的就是画完即弃。代码一直在改图却停在半年之前。要解决这个问题不能靠自觉维护要靠机制。我所在的团队后来把重要图表的定义文件和代码放进了同一个仓库并在提交说明里添加了约定如果这次提交涉及模块结构调整或接口变更必须同步更新对应的图表定义否则代码评审不通过。最初几天大家会觉得麻烦但跑通一轮迭代之后都体会到了好处。因为图表定义文件也会出现在代码提交记录里任何人改动架构时都能直观地看到这张图对应代码库里的哪个模块溯源变得非常方便。6.5 新成员读不懂图怎么办团队里有新同事加入看不懂老图或者需要很久才能看明白通常不是新人的问题而是图本身的设计出了问题。我现在画完一张图之后都会请一位不了解这个项目的同事来试读问三个问题这张图主要讲什么你从最左侧还是从最上方的位置开始看的有没有哪根线或哪个框让你感到困惑他的回答能精准地暴露图中语义含糊的地方。新同事读不懂图还有一个常见原因图里没有入口指引。复杂的图必须要有从这里开始阅读的引导比如给核心入口节点加一个特殊标记或用序号标出阅读顺序。不加引导的复杂图对新人来说就是个迷宫。7. 经验总结把图表当成代码来设计跑完整个 diagram-design 的实践流程之后我最深的感受是图表设计的本质不是艺术创作而是信息工程。一张好的技术图表它的信息和代码一样需要被管理、被审查、被维护。我会把图表定义文件写进项目的技术文档体系里让它在评审时有据可查、在迭代时有版本可依、在排障时能快速定位到对应模块。这比过去那种画个图挂墙上的模式要靠谱得多。最后再说一个我最近才开始练习的小习惯画图之前不去想用什么软件而是先在一张空白纸上用三句话描述这张图的核心信息。这三句话想清楚了再用任何工具画出来的图质量都不会差到哪去。这个习惯帮我减少了大量的返工也让我能更坦诚地面对大多数图表本来就不需要画的事实。
返回列表