时序图怎么画:从“文字交互”到“可交付时序图”的 6 步(含检查清单)
给产品/研发/测试/架构/文档写作者一套可落地的时序图(UML 序列图)画法:从需求文字提炼参与者与消息,补齐分支/异常/重试/回调,用 6 步画出能评审、能交付、能验收的时序图。附反例修正、检查清单与 FAQ。
先给你一套能直接照着做的答案:时序图 = “参与者 + 消息 + 约束”
很多人卡在“时序图怎么画”,不是因为不会画箭头,而是没有把问题拆开。
一张能交付的时序图(UML 序列图)至少回答三件事:
- 参与者是谁:谁在这个交互里出现?边界在哪里?(前端/网关/服务/外部系统/人)
- 消息发生了什么:谁先调用谁?同步还是异步?返回值要不要画?
- 约束与分支是什么:条件(if/else)、可选、循环、并发、超时重试、回调、补偿、幂等等怎么表达?
如果你只画了“主流程的一串箭头”,那通常不叫交付级时序图,只能算“示意图”。交付级时序图要能支撑:
- 研发:接口边界、调用顺序、异常策略
- 测试:mock 点、异常注入点、覆盖路径
- 架构:跨服务耦合、链路长度、同步/异步设计是否合理
- 产品/文档:把需求从“描述”变成“可执行”的协作材料
下面这 6 步是我最推荐的一套落地流程:从一段文字/PRD 里把时序图画到“能评审、能验收”。
想快一点把文字交互变成规范图,也可以用在线工具边写边看效果:手打时序图 - 时序图在线制作
第 1 步:把“叙述性文字”改写成“交互脚本”(别急着画)
画时序图之前,先做一个动作:把需求段落改写成逐条交互脚本。
1)把“描述”拆成“句式固定”的脚本
把每句话统一成这种结构:
- [参与者A] → [参与者B]:做什么(请求)
- [参与者B] → [参与者A]:返回什么(响应)
- 条件/约束:在什么情况下发生
举个常见例子(登录 + 短信验证码):
- 用户 → 前端:输入手机号并点击获取验证码
- 前端 → 认证服务:请求发送短信验证码(手机号)
- 认证服务 → 短信网关:发送短信
- 短信网关 → 认证服务:返回发送成功/失败
- 认证服务 → 前端:返回“已发送”或错误码
- 用户 → 前端:输入验证码并提交
- 前端 → 认证服务:校验验证码
- 认证服务 → 用户中心:查用户状态(是否冻结/是否新用户)
- 认证服务 → 前端:返回 token/登录失败原因
你会发现:脚本一写出来,图已经在脑子里了。
2)在脚本里先标注“你必须交代的坑”
建议在脚本旁边用括号先打标(后面要画成片段):
- (分支)验证码错误 / 过期 / 超次数
- (重试)短信网关失败重试 1 次
- (风控)命中风控时走人工校验
- (异步)短信发送是异步回执还是同步返回
别怕啰嗦。时序图的价值就在这些“坑”里。
第 2 步:定参与者与边界(生命线别乱放)
你要先回答:这张图的“系统边界”是什么?
交付时序图最容易翻车的地方是:参与者摆放含糊。
一个实用原则:
- 你负责的系统/服务放在中间
- **上游(调用你)**放左边
- **下游(你调用的外部/其他服务)**放右边
并且给参与者起“可定位”的名字:
- ✅
Web 前端、API Gateway、Auth Service、SMS Provider、User Service - ❌
系统、后台、服务端(太笼统,后续会扯皮)
生命线(Lifeline)怎么命名才不尴尬
建议使用:实例/角色 + 类型 的方式,尤其是你在画模块级或服务级交互时。
client: WebAppgw: APIGatewayauth: AuthServicesms: SmsProvider
这样后续你加“自调用”“并行片段”都不会糊。
反例:把数据库当参与者、把“缓存”当主角
不是说 DB/Redis 不能画,而是要问:这张图的核心问题是“跨参与者交互”还是“内部实现细节”?
- 如果你在讲“接口对接、回调、重试、幂等”,DB 通常不必出现。
- 如果你在讲“写库先后、事务边界、最终一致性方案”,那 DB/缓存可以出现,但要把它们当“被调用者”,并标注清楚一致性语义。
一句话:先确定沟通对象是谁,再决定参与者粒度。
第 3 步:画“主干消息”——只画 30% 的内容
现在才开始画箭头。
1)先只画主干(Happy Path),不要一上来就塞分支
从脚本里挑最稳定的一条路径:
- 请求:谁 → 谁
- 同步返回:谁 → 谁
先让评审者看懂“到底是谁在跟谁说话”。
2)把消息写成“接口级语言”,别写成“动作口号”
- ✅
POST /auth/sms/send、verifyCode(phone, code)、createOrder() - ❌
进行校验、处理业务、执行逻辑
你不用写到每个字段,但至少要把意图 + 关键参数写出来:
sendSms(phone, scene)(scene 很关键:登录/注册/找回密码)verifyCode(phone, code, scene)
3)同步/异步先定清楚:箭头类型别乱
如果你画 UML,至少要区分:
- 同步消息:调用方等待返回(常见为实线 + 实心箭头)
- 异步消息:调用方不等待(常见为实线 + 空心箭头)
- 返回消息:虚线返回(可画可不画,但要一致)
实务上,一个判断标准很好用:
- “调用方会不会阻塞等待结果?”——会:同步;不会:异步。
小技巧:在工具里画的时候,先把消息类型选对。比如在编辑器左侧点选“消息类型”(同步/异步/返回),右侧实时预览能立刻看到箭头样式变化,避免画到一半才发现风格不统一。
第 4 步:把“坑”补成组合片段(alt/opt/loop/par)
时序图专业不专业,往往就看你会不会用组合片段。
先记住:片段不是为了好看,是为了“无歧义”
当读者看到一段消息时,他必须知道:
- 这是必走还是可选?
- 这是分支还是循环?
- 这是并发还是“看起来分叉”?
对应的片段:
alt:分支(if/else)opt:可选(if)loop:循环(for/while/重试)par:并行(同时发生)
反例 1:用两条箭头表示 if/else,但没写条件
很多人这样画:
- 认证服务 → 前端:成功
- 认证服务 → 前端:失败
读者会问:什么时候成功?什么时候失败?条件在哪?
修正:用 alt,每个分支写 guard 条件。
[code valid]返回 token[code invalid]返回 error
反例 2:把重试画成“重复三次消息”,但没有重试策略
重试不是“重复画几条箭头”。你至少要交代:
- 谁重试(调用方?中间层?SDK?)
- 重试次数/间隔
- 是否幂等
- 超过次数后怎么处理(降级/失败/补偿)
修正:用 loop,并在循环标题里写策略。
loop retry <= 2, backoff=200ms
反例 3:并发画成“从一个点分叉出去”,但实际不是并发
如果只是“先做 A 再做 B”,那不是并发。
修正:只有确实同时发生、互不等待时,才用 par。
例如下单后:
- 订单服务并行发起:
- 发消息给营销(埋点/优惠)
- 发消息给风控(异步审核)
并行片段能提醒研发/测试:这两条链路的失败策略可能不同。
第 5 步:把“可交付细节”补齐:激活条、返回消息、边界条件
主干 + 分支之后,你还差最后一口气:把图补到“别人照着就能做”。
1)激活条(Activation)画多长?
别把激活条当装饰。
- 同步调用:激活条通常覆盖“从发起请求到收到返回”的时间窗口。
- 异步消息:发送后调用方不等返回,激活条不应该一直拉长。
一个常见修正:
- 你画了“异步投递 MQ”,却把调用方激活条拉到“消费完成”——这会误导读者以为是同步阻塞。
2)返回消息要不要画?
建议用一个标准:
- 如果返回值对后续分支有影响(成功/失败、错误码、状态),画。
- 如果返回值只是“OK”且不影响后续,可以不画,但要在图中保持一致风格。
交付时最怕:一半消息画返回,一半不画。读者会以为你漏了。
3)边界条件:至少补齐这 6 类
在时序图里,“边界条件”不是可选项。建议至少补齐:
- 参数校验失败(空值/格式/签名)
- 权限/鉴权失败(token 失效、角色不足)
- 资源不存在/状态不允许(订单已取消还支付?)
- 超时(上游等待多久?下游超时怎么算?)
- 重试/幂等(重复请求如何处理)
- 外部依赖异常(第三方不可用、回调延迟)
这些通常用 alt/opt/loop + 注释(note)表达。
如果你经常需要补这些边界,用“边写边预览”的方式会快很多:左侧编辑器里点选生命线、消息、组合片段(alt/opt/loop/par),右侧实时预览能即时看到结构是否闭合;并且自动排版会把片段对齐,避免你在对齐上浪费时间。
第 6 步:做一次“交付前验收”:检查清单 + 读者视角走读
画完以后不要急着发。
交付检查清单(建议复制到评审评论里)
参与者与边界
- 参与者命名可定位(不是“系统/后台”)
- 图的边界明确(这张图解决哪个范围的问题)
- 参与者粒度一致(不要一边是“订单服务”,一边是“数据库表”)
消息与顺序
- 每条消息写的是“接口意图 + 关键参数/关键返回”
- 同步/异步箭头类型一致且符合语义
- 顺序的关键点(先后会影响结果的点)在图里能看出来
分支与异常
- 分支用
alt,且每个分支都有 guard 条件 - 可选用
opt,循环/重试用loop - 至少覆盖:校验失败/鉴权失败/状态不允许/超时/重试幂等/外部依赖异常
可读性与可维护
- 一屏能读懂主干(必要时拆多张图)
- 复杂片段有注释(note),说明策略而不是堆箭头
- 图的风格统一(是否画返回消息、是否画激活条)
读者视角走读(30 秒自测)
把自己当成“第一次接手这个需求的同事”,从上到下读一遍:
- 我能在 10 秒内说清“谁跟谁交互、解决什么问题”吗?
- 我能指出“失败/超时/重试”发生在哪里吗?
- 我能根据图写出至少 5 条测试用例吗?
如果做不到,说明你还缺关键信息,别急着优化排版。
两个典型场景:用 6 步快速拆解(让你有参照物)
下面用两个高频场景,示范怎么从文字到可交付时序图。
场景 A:登录(验证码 + 失败重试 + 风控)
交互脚本提炼(第 1 步)
- Web 前端 → AuthService:
sendSms(phone, scene=login) - AuthService → SmsProvider:
send(phone, template) - SmsProvider → AuthService:
accepted | rejected - Web 前端 → AuthService:
verifyCode(phone, code, scene=login) - AuthService → RiskService:
check(phone, device, ip) - AuthService → UserService:
getUser(phone) - AuthService → Web 前端:返回 token 或错误
关键坑(第 4/5 步要画出来)
alt:验证码正确/错误/过期/超次数opt:命中风控 → 触发二次校验/人工验证loop:SmsProvider 发送失败重试(次数/间隔)note:错误码约定(给前端提示与埋点)
测试同学最在意的点
- 风控超时怎么办?默认放行还是默认拒绝?(这必须在图里体现)
- 重试是否会导致短信重复发送?如何幂等?
场景 B:下单-支付-回调(同步/异步、超时、补偿)
这个场景非常适合用时序图,因为它天然包含:
- 同步下单
- 异步支付回调
- 重复回调(幂等)
- 超时未支付的关单补偿
参与者建议(第 2 步)
client: WebAppgw: APIGatewayorder: OrderServicepay: PayServicethird: ThirdPaymq: MessageQueue(如果你确实用事件驱动)
必须画出来的片段(第 4/5 步)
alt:库存不足/价格变更/风控拒绝par:创建订单后并行触发通知/风控异步loop:第三方回调重试(可能 3~10 次)alt:回调签名合法/非法opt:重复回调(幂等命中)opt:超时关单补偿(谁触发、何时触发)
这种图画清楚之后,研发能据此实现幂等;测试也能直接拿来设计“重复回调、乱序回调、延迟回调”的用例。
常见问题 FAQ(把你画图时最容易纠结的点一次解决)
Q1:一张时序图画多细才合适?
看你的交付目的。
- 需求评审:到“参与者 + 关键消息 + 关键分支/异常”即可
- 技术方案评审:需要补齐“超时、重试、幂等、补偿、协议约定”
- 测试用例评审:需要能从图上直接拆出用例路径(主干 + 关键异常)
实操建议:
- 超过一屏读不完,就拆成 2~3 张:主干一张,异常/回调一张,补偿/关单一张。
Q2:返回消息到底要不要画?
用“是否影响分支”来决定,并保持一致。
- 影响后续决策:画,并写出关键返回(错误码/状态)
- 不影响决策:可不画,但别一半画一半不画
Q3:要不要把数据库/缓存/内部函数调用画进来?
多数情况下不需要。
- 你在沟通“跨服务交互” → 不画 DB
- 你在沟通“事务边界、写库顺序、最终一致性” → 可以画,但要配注释说明一致性语义
时序图不是代码调用栈。把粒度画到“让人能协作”即可。
Q4:时序图能替代接口文档吗?
不能替代,但能显著减少接口文档的“误读”。
- 接口文档:字段、约束、示例
- 时序图:顺序、分支、异常、谁负责
最理想的组合是:一张时序图 + 关键接口文档链接/简要说明。
Q5:我不会 UML 标准符号,会不会画错?
你不需要把 UML 当考试。
最低要求是:
- 参与者/生命线清晰
- 同步/异步不混
- 分支/循环/并发不歧义
符号细节可以逐步规范化。最怕的是“看起来像 UML,但语义全靠猜”。
给你一个“反例 → 修正”的快速对照(用来补字也用来补质量)
下面这些是我在评审里最常见的“看起来画了,但不专业”的问题,以及对应修正方式。
反例 1:把“失败处理”写成一句话
- 反例:
支付失败 -> 提示用户
问题:失败原因很多,处理策略也不同。
修正:拆成 alt 并写 guard:
[余额不足]返回提示 + 保留订单[签名校验失败]记录安全日志 + 告警[超时]触发查询/补偿 + 前端提示“处理中”
反例 2:没有标注谁负责重试
- 反例:
第三方支付回调失败 -> 重试
问题:第三方重试?我方重试?MQ 重试?策略不同。
修正:在 loop 或 note 里写清楚:
loop third retry up to 8, interval=5s/30s/5m- 我方收到回调后:
verifySign()失败不重试、直接拒绝;业务处理失败返回 200 还是 500?(这也要在图里体现)
反例 3:把“并行”当“顺序”画
- 反例:订单服务先调用营销再调用风控,读者会以为必须按顺序
修正:用 par:营销与风控并行,互不阻塞。
反例 4:缺少“幂等”表达
- 反例:回调到达两次,图里看不出系统怎么处理
修正:用 opt 或 alt:
[idempotent hit]直接返回成功(或返回已处理)[first time]更新订单状态 + 发消息
最后给一个轻量建议:用“文字 → 图”的闭环减少返工
如果你的团队经常在“文字描述”和“图”之间来回改,建议建立一个闭环:
- 先用第 1 步把文字写成脚本(这本身就是需求澄清)
- 用编辑器快速点选生命线/消息/组合片段,把脚本落成图
- 右侧实时预览检查结构是否闭合、分支是否有 guard
- 导出交付格式(SVG/PNG/JPEG 便于评审;需要工程化协作时导出 draw.io)
这样你会发现:画图不再是“美术活”,而是一种把协作语言变得无歧义的过程。
如果你想把这套流程跑得更快,可以直接用这个工具把脚本落成图并自动排版:
- 在线生成与编辑:手打时序图 - 时序图在线制作
- 支持导出:SVG/PNG/JPEG/draw.io(方便贴文档、贴 PR、贴评审)
- 也可以让 AI 先根据你的“交互脚本”生成初稿,再由你补齐分支/异常
你可以直接复用的“6 步模板”(复制粘贴用)
最后附一个你可以复制到文档里的模板,写完再画:
- 目标与范围:这张图要解释什么?不解释什么?
- 参与者:A(上游)、B(我方)、C(下游/外部)……
- 主干交互(Happy Path):
- A → B:……
- B → C:……
- C → B:……
- B → A:……
- 关键分支(alt/opt):
- [条件1] ……
- [条件2] ……
- 循环/重试/并发(loop/par):
- loop:次数/间隔/幂等
- par:并行任务及失败策略
- 边界条件与验收:超时、异常、补偿、幂等、测试用例路径
把模板填完,你基本就不会“画不出来”。