Skip to the content.

变更识别规则

文本 diff 只能告诉你”这一行变了”。本工具要回答的是什么语义变了, 因为不同类型的变更传播方式完全不同。

稳定符号标识

每个符号都有稳定 key,形如:

模块路径::全限定类名#方法名(参数类型,参数类型)
order-service::com.example.order.OrderService#createOrder(Long,Money)

base 与 head 用同一个 key 对齐,所以即使方法在文件里换了行号也能认出是同一个符号。

方法级变更

变更类型 判定依据 传播方式 风险种子
METHOD_SIGNATURE_CHANGED 签名哈希变化(参数或返回类型) 编译影响 → 所有调用方 0.90
METHOD_VISIBILITY_CHANGED public → 非 public 编译影响 0.85
METHOD_CONDITION_CHANGED 分支/返回条件指纹变化 行为影响(控制依赖) 0.80
METHOD_BODY_CHANGED 方法体哈希变化,签名不变 行为影响(较弱) 0.55
METHOD_REMOVED 符号在 head 缺失 编译影响 0.95

为什么区分条件变化和普通实现变化? 条件变化意味着”同样的输入可能走不同分支”, 这是行为变化最强的信号;而方法体内部重构(改变量名、提取局部方法)往往不影响调用方。

字段与类型

变更类型 判定依据 风险种子
FIELD_TYPE_CHANGED 字段类型变化 0.85
FIELD_ADDED / FIELD_REMOVED 字段出现/消失 0.40 / 0.90
ENUM_CONSTANT_ADDED / REMOVED 枚举值增删 0.40 / 0.90
TYPE_HIERARCHY_CHANGED extends / implements 变化 0.75
TYPE_ADDED / TYPE_REMOVED 类型增删 0.40 / 0.90

契约级变更

变更类型 判定依据 风险种子
API_ADDED 新端点 0.20
API_REMOVED 端点消失 0.95
API_CONTRACT_CHANGED 同一端点响应/请求类型变化 0.85
SQL_CHANGED SQL 语句体变化 0.70
DDL_CHANGED 表结构变化(.sql 脚本与内联 DDL) 0.60 – 0.95,见下
CONFIG_CHANGED 配置值变化 0.60
BUILD_CHANGED pom.xml 变化 0.60

SQL_CHANGED 与 DDL_CHANGED 的区别: 前者是语句变了(可能只是 WHERE 条件改了), 后者是表结构变了(加列、删列、改列类型)。前者不一定跨程序扩散——只有写入字段集合跟着变才算; 后者是否扩散取决于具体操作,而不是「表结构变了就一定影响所有读写方」。

数据库侧的变更风险按具体操作与兼容性区分:

数据库侧变更 风险种子 是否跨程序扩散
删字段 0.95 是:读方、写方、映射方
删表(整表变更) 0.95 是:表级读写与映射
表重命名(整表变更) 0.90 是:旧表名的表级读写与映射;新表名不扩散
改字段类型:收窄 / 不兼容 0.90 是:读方、写方、映射方
改字段类型:放宽 / 兼容 0.45 否,只计数
写入字段集合变化 0.85 是:只命中读方
加字段 0.40 否,只计数;SELECT * / 无列名 INSERT 记盲区
加必填字段(NOT NULL 且无默认值) 0.70 是:省略该字段的完整 INSERT(写方);自动填充列除外
建表(整表变更) 0.60 否:新表不可能是既有依赖
删除迁移脚本 0.60(文件变化) 否:已执行的 migration 不回滚
实体字段删除 / Java 类型变化 0.85 / 0.85 只命中映射方,低置信度

加字段与放宽类型不扩散,是因为对方按列名读写时看不见新列,已有值也仍然可表示; 删字段、收窄类型则会让对方读到的值消失或失真,且不一定报错——这是数据库契约里最需要人工确认的一类。 例外是 NOT NULL 且无默认值的加列:对方省略该列的完整 INSERT 会被数据库拒绝,属于确定的写侧不兼容, 因此按 0.70 命中写方;列可空、有默认值,或由数据库自动填充(AUTO_INCREMENT / GENERATED / IDENTITY)时不算必填。UPDATE / DELETE 不命中,SELECT * 与动态 SQL 仍记盲区。

一条 ALTER TABLE 里的多个动作会拆成多条 DDL_CHANGED,每条带自己的 ddl / table / column / oldType / newType / renamedTo 与风险种子:加列按 0.40(必填 0.70)、 删列按 0.95、收窄按 0.90……而不是让整条语句共用一个结论。列重命名只报旧列名一条 (ddl=DROP_COLUMN、renamedTo=新列名),新列名不会再被记成一次「加列」。

兼容性规则与数据库身份校验见 数据库影响判断。

为什么 API_REMOVED 种子最高? 端点消失时所有消费方都会立刻失败, 没有”可能”的余地。而 API_ADDED 只是新增能力,不影响已有调用方。

消息契约

变更类型 判定依据
TOPIC_ADDED / TOPIC_REMOVED 主题出现/消失
MESSAGE_ROUTE_CHANGED Topic、Exchange、Queue、Routing Key 或 Tag 变化
MESSAGE_FIELD_ADDED / REMOVED / TYPE_CHANGED 消息 DTO 字段变化
MESSAGE_REQUIRED_CHANGED 字段必填性变化
MESSAGE_SCHEMA_CHANGED JSON / Avro / Protobuf Schema 变化

消息契约的风险在于它不报错:字段改了但消费方还在读旧字段,程序照常运行, 只是数据悄悄错了。所以这类变更即使风险分不高,也值得复核。

未解析的调用去哪了

解析失败的调用点不会被悄悄忽略,而是进入报告里的「分析边界与盲区」清单:

未解析调用  order-service · OrderService.java:42
            getBean("dynamicHandler").handle(x) — 反射调用,静态不可解析

盲区条目本身就是结论的一部分:它告诉你”这部分我没法保证”。