兼容性
更新计划
语义化版本(Semantic Versioning,简称 SemVer)是一种版本控制策略,旨在通过版本号传达软件的变化信息。SemVer 版本号由三部分组成:主版本号(Major)、次版本号(Minor)和修订号(Patch),格式为 MAJOR.MINOR.PATCH。例如,Milky 的第二个正式版的版本号是 1.1.0。
Milky 计划每季度更新一个 Minor 版本(Milky 称之为累积更新)。称从一个累积更新发布开始到下一个累积更新发布的时间段为一个更新周期,每个更新周期维持约 3 个月。Milky 在更新周期的前 2 个月期间接收新的 Feature Request(abbr. FR)并且评议是否纳入下一个累积更新;最后一个月期间,仅评议已有的 FR,而此后的 FR 将至少推迟到下一个更新周期。
Milky 在一般情况下不会发布 Patch 版本,除非当前版本存在影响使用的严重 Bug。目前尚无发布下一个 Major 版本的计划。
字段兼容性
Milky 十分注重不同版本之间的兼容性。然而,由于更新同时涉及协议端(实现侧)和应用端(适配器/SDK 侧),所以字段的增减必然引发不兼容现象。Milky 在不同 Minor 版本之间避免对现有字段的重命名、语义变更和删除,以降低对用户的影响。Milky 的兼容策略是保证协议端向后兼容、应用端向前兼容,即确保旧版本的应用端能够继续与新版本的协议端通信,而反过来的情况则不作保证。
概念介绍
Milky 在兼容性设计中给结构体划分了两种类型:
- 请求结构体:应用端传递给协议端的数据结构,包含:
- 各 API 的请求参数
- OutgoingSegment
- OutgoingForwardedMessage
- 响应结构体:协议端传递给应用端的数据结构,包含其余所有的数据结构,例如:
- 各 API 的响应结果
- Event
- IncomingSegment
请求结构体中的字段分三类:必需(Required)字段、可选(Optional)字段、具有默认值(Default)的字段;响应结构体中的字段分两类:可空(Nullable)字段和不可空(Non-Nullable)字段。
此外,结构体的类型也有两种情况:
- 简单类型:不存在子类型的结构体,例如 Friend 结构体。
- 联合类型(Union Type):同一个结构体根据
type等区分字段(Discriminator)的不同,可能包含不同的字段集合。例如 Event 结构体,根据event_type字段的不同,可能包含与消息事件相关的字段,或与通知事件相关的字段。
简单类型的兼容性策略
简单类型结构体的兼容性策略相对简单:
- 对于请求结构体,只增加可选的或具有默认值的字段,或删除字段,或给现有字段增添默认值,而不修改现有字段或增加必需字段。
- 对于响应结构体,则只增加字段,不删除或修改现有字段。
联合类型的兼容性策略
联合类型的兼容策略是只增加子类型、不删除子类型。
这个策略看似简单,而且从增添特性的角度出发可以理解。然而这种策略对应用端并不友好。譬如,应用端的反序列化逻辑中仅考虑了 A 和 B 两种子类型,而更新加入了 C 子类型,这时会导致应用端无法处理新的 C 子类型。因此,有必要对应用端的反序列化逻辑进行一定的标准化。
应用端对联合类型进行反序列化的场景主要包括:
- 处理事件推送,例如消息事件、群通知事件;
- 解析部分 API 的响应结果,例如:
get_messageAPI,其中 IncomingSegment 是联合类型;get_group_notificationsAPI,其中 GroupNotification 是联合类型。
Milky 作以下规定:
- 在处理除消息事件以外的事件推送时,遇到未知的事件子类型,不应抛出错误,而应忽略或打印警告信息;
- 在处理消息事件推送以及处理有关消息获取的 API 响应结果时,遇到未知的消息段子类型,不应抛出错误,而应将其转换为
text消息段,消息段的内容由应用端自行决定,但应当反映该消息段在当前版本的 Milky 中不受支持; - 在处理其他联合类型时:
- 如果该联合类型包含在数组中,遇到未知的子类型,不应抛出错误,而应当在反序列化时跳过这一元素;
- 如果该联合类型是单个元素,遇到未知的子类型,可以抛出错误。