时序图栏目 ·

返回消息要不要画?(虚线箭头)画到什么粒度才有价值

返回消息(虚线箭头)在时序图里到底要不要画?本文从研发/测试/架构/文档交付视角,讲清返回消息的语义、何时必须画、何时不画更专业,并给出粒度标准、反例修正、检查清单与 FAQ。

返回消息要不要画?(虚线箭头)画到什么粒度才有价值

你在画时序图(UML 序列图)时,大概率遇到过这几个纠结:

  • **每个调用都要画返回消息吗?**不画会不会“不规范”?
  • 返回消息到底表示什么:返回值HTTP 响应、还是“对方处理完了”?
  • 我画了很多虚线箭头,图变得像蜘蛛网:信息量上去了,但可读性掉没了
  • 评审时有人说“你这图不完整”,有人又说“太啰嗦”。到底怎么取舍?

结论先放在首屏:

  • 返回消息不是必须逐条画。在大多数“业务流程级”的时序图里,默认可以省略返回消息,由同步调用的语义隐含。
  • 你应该在这些情况下画返回消息:
    1. 返回值/结果会影响后续分支或状态(尤其是错误码、空值、版本号、签名结果)
    2. 存在异步/回调/并发,需要强调“何时完成/谁通知谁”
    3. 你要交付的是接口/协议/文档级,需要明确响应体字段或返回码
    4. 性能/时延/超时是讨论重点,需要标注“等待到哪里结束”
  • 返回消息的粒度标准:只画会改变读者决策的信息;其它“OK/200/true”不要机械补全。

下面把“为什么”“怎么画才不歧义”“常见反例怎么改”讲透,你可以按场景直接套用。


1. 返回消息(Return Message)到底是什么?

在 UML 时序图里,返回消息通常用虚线箭头表示,从被调用方指回调用方。它要表达的不是“形式上的回箭头”,而是:

  • 一次交互的结果(返回值、响应对象、错误、异常)
  • 调用方解除等待的时刻(同步调用结束点)
  • 或者(在某些画法里)表达“对方已处理/已接收/已完成”这类协议语义

关键点:

  • 如果你画的是同步调用(同步消息),很多工具/读者会把“返回”当成默认存在。
  • 如果你画的是异步消息,就不能再靠“默认返回”偷懒:因为异步消息通常不等待,是否有后续回执/回调,需要你明确画出来。

一句话:

返回消息的价值在于“把隐含的信息显式化”。如果显式化后没有增加可决策信息,就别画。


2. 什么时候“必须画返回消息”?(高优先级规则)

下面这些场景,返回消息往往是图能否被正确理解的关键。

2.1 返回结果决定后续分支(画 alt/opt 时常见)

如果后续逻辑依赖返回值,你不画返回消息,就会出现“分支凭空出现”的感觉。

典型例子:

  • 登录:验证码校验返回 OK/FAIL 决定是否继续发 token
  • 风控:riskScore 返回不同区间触发 拒绝/短信二次验证/放行
  • 下单:库存服务返回 扣减成功/失败/需预占 决定下一步

画法建议:

  • 返回消息写清 关键字段/关键码,不要只写“return”。
  • 如果只需要表达真假,写 valid=true/falseexists? 这种即可。

2.2 有错误码/异常语义需要落地(尤其是 API/协议文档)

当你在用时序图当“接口文档/对接说明”的一部分时,返回消息需要承载:

  • HTTP 状态码(200/400/401/429/500)
  • 业务错误码(例如 ORDER_NOT_FOUNDSIGN_INVALID
  • 重试建议(例如 Retry-After、幂等冲突提示)

否则,读者(研发/测试/接入方)就没法从图里知道“失败时怎么走”。

2.3 异步/回调/消息队列:必须把“结果返回路径”画清楚

异步消息最常见的误解是:

  • 以为发出消息就等于“处理完成”
  • 把回调当成返回

当存在:

  • MQ 投递、消费者处理
  • Webhook 回调
  • 事件总线、发布订阅
  • 并发 par 片段

你需要用返回消息/回调消息明确:

  • 确认收到(ack) vs 处理完成(done)
  • 谁在什么时机通知谁
  • 如果失败,是否有 重试/死信/补偿

这类图里,“返回消息”往往不是虚线 return,而是另一条完整消息(可能仍画成实线异步消息)。你要表达的是路径,不是线型。

2.4 你在讨论性能:等待边界(latency boundary)要画出来

当评审重点是:

  • 接口 SLA
  • 超时设置(connect/read)
  • 串行调用链路时延

返回消息可以帮助标出:

  • 调用方从发起到解除等待的区间
  • 哪段是同步阻塞,哪段是异步并行

这种情况下,返回消息的标签可以写:

  • 200 OK (t=35ms)
  • timeout after 2s
  • fallback 触发点

3. 什么时候“建议不画返回消息”?(更专业、更清晰)

多数团队最终会形成一个共同约定:

流程级/方案级时序图:默认省略同步调用的返回消息。

原因很现实:

  • 同步调用“有返回”是常识,重复画会制造噪音
  • 图面拥挤后,读者反而找不到关键分支与关键对象
  • 你会被迫写很多没有意义的 oksuccessreturn

3.1 纯粹的 happy path 串行流程

例如:

  • 用户点击 → 网关 → 订单 → 支付 → 返回页面

如果你只是交付“流程”,不强调返回体字段,那么每条返回都画出来意义不大。

3.2 返回值与后续动作无关(信息不改变决策)

例如:

  • logService.append() 的返回值通常不影响主流程
  • metrics.inc() 返回 ok 不影响业务

这种返回消息画出来只会让图像“更像代码”,但对评审没有帮助。

3.3 你已经用注释/约定表达“默认成功/失败另见错误流程图”

如果项目里有约定:

  • 主图只画成功路径
  • 错误路径单独一张“异常处理时序图/补偿图”

那主图里的返回消息就更应该克制。


4. 粒度怎么定:返回消息写到什么程度才有价值?

这是最容易“机械化”的地方。下面给你一个实用的粒度尺:

4.1 三档粒度:信号级 / 字段级 / 契约级

  1. 信号级(推荐默认)
  • 只写影响分支的关键信号
  • 例:valid=true/falserisk=HIGH/MID/LOWtoken?
  1. 字段级(接口联调/测试设计常用)
  • 写关键字段,不必展开全量 JSON
  • 例:{orderId, payUrl}{code, message}{signatureOK}
  1. 契约级(对外开放 API/强文档场景)
  • 明确响应码、错误码、幂等等契约
  • 例:HTTP 401 + code=AUTH_EXPIRED409 IDEMPOTENT_CONFLICT

选择原则:

  • 你要帮助读者做什么决定?
    • 方案评审 → 信号级足够
    • 联调/测试 → 字段级更有用
    • 对外文档/平台能力 → 契约级需要

4.2 一个“去噪”规则:能被旁注/约定替代的,就别写在线上

例如返回消息写 200 OK,如果你的所有调用都是 HTTP 并且默认成功是 200,那么这句话等价于噪音。

把它变成:

  • 注释:// 默认成功 200,仅在非 2xx 时画返回
  • 或者在失败分支里单独画:401/429/5xx

5. 最常见的 6 个反例(以及怎么改)

下面这些是“看起来对,但就是不专业”的典型坑。

反例 1:每条消息都画一个 return,标签写 return

问题:

  • 图面噪音巨大
  • 读者完全得不到信息

修正:

  • 同步调用默认省略返回
  • 只在关键点画返回,并写“能触发分支的结果”

反例 2:把异步回调画成虚线 return

问题:

  • 回调不是“函数返回”
  • 会误导读者以为调用方一直阻塞等待

修正:

  • 回调用一条独立消息(通常是异步消息)
  • 例如:PaymentGateway -> Merchant: webhook(paymentResult)

反例 3:返回消息写“成功/失败”,但没有任何判断条件

问题:

  • 测试无法据此设计用例
  • 研发无法确定分支条件

修正:

  • 至少写出触发条件或码:
    • code=INVALID_CAPTCHA
    • riskScore>=80
    • stock=0

反例 4:返回体展开成大段 JSON 塞在线上

问题:

  • 可读性灾难
  • 图变成“接口文档”的替代品,但又不完整

修正:

  • 只写关键字段
  • 其余字段用注释或链接到接口文档(例如 OpenAPI)

反例 5:返回消息方向画错(箭头回到错误对象)

问题:

  • 在多人协作图里,方向错一次,读者就会误解责任方

修正:

  • 返回消息必须回到等待方
  • 如果不存在等待(异步),就不存在“返回”,应画后续通知

反例 6:返回消息与激活条/执行规格不匹配

问题:

  • 你画了激活条,但返回在激活条中途结束
  • 或者返回在激活条之外出现

修正:

  • 同步调用:激活条从接收消息开始,到返回结束
  • 嵌套调用:内层返回先结束,外层再结束

6. 用“检查清单”决定要不要画返回消息

你可以把这段当成团队规范贴到文档里。

6.1 必画(满足任意一条)

  • 返回值/错误码决定后续分支(alt/opt 的条件来自返回)
  • 需要明确完成时刻(性能、超时、等待边界)
  • 异步场景需要明确回执/回调/完成通知路径
  • 交付的是接口/协议级文档,需要明确响应码或关键字段

6.2 可选(看读者是谁)

  • 返回值用于展示(例如 UI 展示列表),但不影响流程结构
  • 返回值用于日志/埋点/审计,但对业务分支影响不大
  • 团队评审希望用时序图替代部分接口文档

6.3 建议不画

  • 纯 happy path 串行流程
  • 返回值永远是“成功”,且不影响任何决策
  • 你已经用约定/注释说明默认返回

7. 场景拆解:接口调用 + 超时 + 重试时,返回消息怎么画更清楚?

以“客户端调用服务端接口”为例(研发/测试最常见):

  • 你真正想表达的不是每次 200 的返回,而是:
    • 超时点在哪里
    • 重试触发条件
    • 幂等键如何影响结果

推荐画法:

  1. 主路径只画一次成功返回:200 + data(信号级/字段级即可)
  2. alt 片段画异常返回:
    • timeout after 2s → 进入重试
    • 5xx → 重试/降级
    • 4xx → 不重试,直接失败
  3. 如果有幂等:返回里写 idempotentKey 相关结果(例如 409 或“返回旧结果”)

这样读者一眼就能知道“系统在异常时怎么行为”,而不是被 200 淹没。


8. 工具落地:怎么更快地把“该画的返回”画出来(还不乱)

如果你用的是在线时序图生成器,建议按这个流程,能显著减少返工:

  1. 先把生命线摆好(参与者/服务/外部系统)。左侧编辑器里按对象逐个点选生命线,命名统一(例如 Client/API Gateway/OrderService)。
  2. 只画主消息:同步/异步先区分,别急着补 return。
  3. 再补关键返回:在左侧编辑器点选某条消息,一键插入返回消息(虚线箭头),只写关键字段/码。
  4. 用组合片段收纳分支:alt/opt/loop/par 把异常与重试关进“盒子”,读者看图不会迷路。
  5. 右侧实时预览:每补一段返回就看一眼图的拥挤程度,拥挤了就说明你在画噪音。
  6. 导出交付格式:评审文档建议导出 SVG(清晰可缩放),PRD/测试用例里用 PNG/JPEG;需要进 draw.io 的话直接导出 draw.io 便于二次编辑。

如果你想把“文字交互描述 → 可交付时序图”这一步做快一点,可以用这个工具一把梭:

它支持:自动排版、左侧编辑器点选生命线/消息/组合片段、右侧实时预览、导出 SVG/PNG/JPEG/draw.io,并且可以用 AI 把你的文字描述补成更完整的消息与分支。


9. FAQ:团队最常争论的问题

Q1:UML 规范是不是要求必须画返回消息?

不要求你“每条都画”。UML 里返回消息是一种可用元素,但在实践中更常见的是:

  • 同步调用默认隐含返回
  • 只有需要强调结果/分支/时延时才显式画

如果你们团队希望更一致,可以把本文的“必画/可选/不画”写进绘图规范。

Q2:我不画返回,测试同学会说缺少失败路径,怎么办?

两种更高效的做法:

  • 在主图里用 alt 明确关键失败(401/429/超时/库存不足),这些必须画返回或错误消息。
  • 其余失败(比如 5xx 的各种细分)放到“异常处理图/补偿图”或测试用例文档。

测试真正需要的是“可验证的条件”,不是“每条都有一根虚线”。

Q3:返回消息写到字段级会不会太啰嗦?

如果你写字段级但图变乱,说明你把“接口文档”塞进了图里。

建议:

  • 图里只写 2~5 个关键字段
  • 其余字段用链接指向接口文档(OpenAPI/Markdown)

Q4:HTTP 场景里返回消息到底是 200 还是响应体?

你可以按目的选择:

  • 讨论流程/分支:写响应体里的关键信号(例如 code/status/needCaptcha
  • 讨论网关/鉴权:写 401/403/429 更有价值
  • 讨论性能:写 t=xxms 或标注超时边界

Q5:有激活条时,返回消息一定要画吗?

不一定。激活条表达“执行占用时间”,返回消息表达“交互结果”。

  • 你想强调耗时/阻塞:激活条就够了
  • 你想强调返回结果/错误码:返回消息更合适

很多时候二选一即可,别叠太多元素。

9.6 可直接复用的“返回消息标注模板”(写得少但信息密度高)

如果你想把返回消息写得更像“契约”,但又不把图写爆,可以用下面这些短模板(适合研发/测试/文档交付):

  • 同步成功(字段级)200 {id, status} / OK {token, expireAt}
  • 同步失败(码 + 关键原因)401 AUTH_EXPIRED / 429 RATE_LIMIT (Retry-After=60s)
  • 条件信号(用于分支)exists=false / hit=true / need2FA=true
  • 幂等语义(强烈建议画出来)
    • 首次成功:201 CREATED (idempotentKey=...)
    • 重复请求:200 REPLAY (sameResult)409 IDEMPOTENT_CONFLICT
  • ack vs done(异步必备)
    • 仅确认接收:ack(messageId)(这不是“处理完成”)
    • 处理完成通知:用另一条消息 done(result)webhook(result)

再给 3 个“写得对、也容易评审”的例子:

  1. 缓存读取:返回写 hit/miss 就够了;value 只有在 miss 后回源时才需要写。
  2. 权限校验:返回写 allow/deny + reason(例如 DENY: role=guest),测试用例能直接据此覆盖。
  3. 远程调用超时:返回写 timeout@2s,并在 alt 里画出 retryfallback,比一堆 200 有价值得多。

10. 一页总结:最实用的画法约定(可直接抄进团队规范)

  • 流程级时序图:同步调用默认省略返回消息
  • 只有当返回结果会影响决策/分支/等待边界时才画
  • 返回消息标签优先写:
    • 条件(valid?
    • 错误码(401/429/timeout
    • 关键字段({orderId, payUrl}
  • 异步场景不要用虚线 return 伪装回调;回调就是独立消息。
  • 图一旦拥挤,先删掉“永远成功”的返回;保留能解释异常与分支的返回。

如果你正在写 PRD/技术方案/测试用例,需要快速产出一张“既清楚又规范”的时序图,可以直接把文字描述丢进去生成,然后再在左侧编辑器按上面的规则补关键返回:

(导出建议:评审用 SVG,文档用 PNG,需要进 draw.io 二次编辑就导出 draw.io。)