时序图栏目 ·

时序图怎么画:从“文字交互”到“可交付时序图”的 6 步(含检查清单)

给产品/研发/测试/架构/文档写作者一套可落地的时序图(UML 序列图)画法:从需求文字提炼参与者与消息,补齐分支/异常/重试/回调,用 6 步画出能评审、能交付、能验收的时序图。附反例修正、检查清单与 FAQ。

先给你一套能直接照着做的答案:时序图 = “参与者 + 消息 + 约束”

很多人卡在“时序图怎么画”,不是因为不会画箭头,而是没有把问题拆开。

一张能交付的时序图(UML 序列图)至少回答三件事:

  1. 参与者是谁:谁在这个交互里出现?边界在哪里?(前端/网关/服务/外部系统/人)
  2. 消息发生了什么:谁先调用谁?同步还是异步?返回值要不要画?
  3. 约束与分支是什么:条件(if/else)、可选、循环、并发、超时重试、回调、补偿、幂等等怎么表达?

如果你只画了“主流程的一串箭头”,那通常不叫交付级时序图,只能算“示意图”。交付级时序图要能支撑:

  • 研发:接口边界、调用顺序、异常策略
  • 测试:mock 点、异常注入点、覆盖路径
  • 架构:跨服务耦合、链路长度、同步/异步设计是否合理
  • 产品/文档:把需求从“描述”变成“可执行”的协作材料

下面这 6 步是我最推荐的一套落地流程:从一段文字/PRD 里把时序图画到“能评审、能验收”。

想快一点把文字交互变成规范图,也可以用在线工具边写边看效果:手打时序图 - 时序图在线制作


第 1 步:把“叙述性文字”改写成“交互脚本”(别急着画)

画时序图之前,先做一个动作:把需求段落改写成逐条交互脚本

1)把“描述”拆成“句式固定”的脚本

把每句话统一成这种结构:

  • [参与者A] → [参与者B]:做什么(请求)
  • [参与者B] → [参与者A]:返回什么(响应)
  • 条件/约束:在什么情况下发生

举个常见例子(登录 + 短信验证码):

  • 用户 → 前端:输入手机号并点击获取验证码
  • 前端 → 认证服务:请求发送短信验证码(手机号)
  • 认证服务 → 短信网关:发送短信
  • 短信网关 → 认证服务:返回发送成功/失败
  • 认证服务 → 前端:返回“已发送”或错误码
  • 用户 → 前端:输入验证码并提交
  • 前端 → 认证服务:校验验证码
  • 认证服务 → 用户中心:查用户状态(是否冻结/是否新用户)
  • 认证服务 → 前端:返回 token/登录失败原因

你会发现:脚本一写出来,图已经在脑子里了。

2)在脚本里先标注“你必须交代的坑”

建议在脚本旁边用括号先打标(后面要画成片段):

  • (分支)验证码错误 / 过期 / 超次数
  • (重试)短信网关失败重试 1 次
  • (风控)命中风控时走人工校验
  • (异步)短信发送是异步回执还是同步返回

别怕啰嗦。时序图的价值就在这些“坑”里。


第 2 步:定参与者与边界(生命线别乱放)

你要先回答:这张图的“系统边界”是什么?

交付时序图最容易翻车的地方是:参与者摆放含糊

一个实用原则:

  • 你负责的系统/服务放在中间
  • **上游(调用你)**放左边
  • **下游(你调用的外部/其他服务)**放右边

并且给参与者起“可定位”的名字:

  • Web 前端API GatewayAuth ServiceSMS ProviderUser Service
  • 系统后台服务端(太笼统,后续会扯皮)

生命线(Lifeline)怎么命名才不尴尬

建议使用:实例/角色 + 类型 的方式,尤其是你在画模块级或服务级交互时。

  • client: WebApp
  • gw: APIGateway
  • auth: AuthService
  • sms: SmsProvider

这样后续你加“自调用”“并行片段”都不会糊。

反例:把数据库当参与者、把“缓存”当主角

不是说 DB/Redis 不能画,而是要问:这张图的核心问题是“跨参与者交互”还是“内部实现细节”?

  • 如果你在讲“接口对接、回调、重试、幂等”,DB 通常不必出现。
  • 如果你在讲“写库先后、事务边界、最终一致性方案”,那 DB/缓存可以出现,但要把它们当“被调用者”,并标注清楚一致性语义。

一句话:先确定沟通对象是谁,再决定参与者粒度。


第 3 步:画“主干消息”——只画 30% 的内容

现在才开始画箭头。

1)先只画主干(Happy Path),不要一上来就塞分支

从脚本里挑最稳定的一条路径:

  • 请求:谁 → 谁
  • 同步返回:谁 → 谁

先让评审者看懂“到底是谁在跟谁说话”。

2)把消息写成“接口级语言”,别写成“动作口号”

  • POST /auth/sms/sendverifyCode(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 类

在时序图里,“边界条件”不是可选项。建议至少补齐:

  1. 参数校验失败(空值/格式/签名)
  2. 权限/鉴权失败(token 失效、角色不足)
  3. 资源不存在/状态不允许(订单已取消还支付?)
  4. 超时(上游等待多久?下游超时怎么算?)
  5. 重试/幂等(重复请求如何处理)
  6. 外部依赖异常(第三方不可用、回调延迟)

这些通常用 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: WebApp
  • gw: APIGateway
  • order: OrderService
  • pay: PayService
  • third: ThirdPay
  • mq: 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:缺少“幂等”表达

  • 反例:回调到达两次,图里看不出系统怎么处理

修正:用 optalt

  • [idempotent hit] 直接返回成功(或返回已处理)
  • [first time] 更新订单状态 + 发消息

最后给一个轻量建议:用“文字 → 图”的闭环减少返工

如果你的团队经常在“文字描述”和“图”之间来回改,建议建立一个闭环:

  1. 先用第 1 步把文字写成脚本(这本身就是需求澄清)
  2. 用编辑器快速点选生命线/消息/组合片段,把脚本落成图
  3. 右侧实时预览检查结构是否闭合、分支是否有 guard
  4. 导出交付格式(SVG/PNG/JPEG 便于评审;需要工程化协作时导出 draw.io)

这样你会发现:画图不再是“美术活”,而是一种把协作语言变得无歧义的过程。

如果你想把这套流程跑得更快,可以直接用这个工具把脚本落成图并自动排版:

  • 在线生成与编辑:手打时序图 - 时序图在线制作
  • 支持导出:SVG/PNG/JPEG/draw.io(方便贴文档、贴 PR、贴评审)
  • 也可以让 AI 先根据你的“交互脚本”生成初稿,再由你补齐分支/异常

你可以直接复用的“6 步模板”(复制粘贴用)

最后附一个你可以复制到文档里的模板,写完再画:

  1. 目标与范围:这张图要解释什么?不解释什么?
  2. 参与者:A(上游)、B(我方)、C(下游/外部)……
  3. 主干交互(Happy Path)
    • A → B:……
    • B → C:……
    • C → B:……
    • B → A:……
  4. 关键分支(alt/opt)
    • [条件1] ……
    • [条件2] ……
  5. 循环/重试/并发(loop/par)
    • loop:次数/间隔/幂等
    • par:并行任务及失败策略
  6. 边界条件与验收:超时、异常、补偿、幂等、测试用例路径

把模板填完,你基本就不会“画不出来”。