设计取舍
这份文档说明能力边界以及为什么不做得更多。静态分析无法在一般情况下证明 跨模块行为是否变化,如实标注边界比假装完备更有用。
为什么只扫描其他程序
这是产品定义,不是技术限制。
一个工具要回答”我这次改动会影响谁”,如果同时报告自身影响,会出现两个问题:
- 噪音淹没信号:仓库内部的调用方动辄上百,真正需要跨团队协调的只有几条
- 责任边界模糊:自身影响由改代码的人自己就能看到(IDE 会提示),跨程序影响才是盲区
所以引擎不对自身索引做传播,报告里的模块地图、盲区与计数也全部来自对方程序。
已知边界
| 场景 | 现状 | 为什么 |
|---|---|---|
| 多态 / 接口调用 | 记录声明方法 + 已知实现的分派边,置信度较低 | 完整的分派分析需要全程序指针分析,成本远超收益 |
| Spring 依赖注入 | 按字段/构造器类型解析,不还原 Bean 装配图 | 装配图在运行期才确定,且可被条件注解改变 |
| 反射 / 动态代理 / 代码生成 | 无法解析,进入盲区清单 | 静态不可判定 |
| Feign / RestTemplate / OpenAPI | 按路径模板匹配,动态拼接的 URL 可能漏报 | 拼接值在运行期才知道 |
动态 SQL(<if> 拼接) |
解析静态部分,动态部分进入盲区 | 同上 |
SELECT * / 列名无法解析 |
不命中字段级数据库契约,计数并标注「字段盲区」 | 列清单由运行期表结构决定,静态拿不到 |
| 数据库身份 | 从 JDBC URL / 显式 schema 推断;未知时保留匹配但降低置信度 | 不连接数据库,也不假设同名表就是同一张表 |
| 值语义(如 status 1 → 2) | 写入列集合不变时不产生数据库 delta | 需要比较赋值表达式与业务契约,首版未做 |
| 事务 / 锁 / 提交时机 | 不分析 | 需要事务边界解析与并发测试,静态无法证明 |
| 配置中心(Nacos/Apollo) | 只分析仓库内配置与代码引用 | 外部配置不在代码里 |
| 框架入口(Controller/Listener/定时任务) | 由容器调用,仓库内没有静态调用方;单独计数并说明 | 这不是遗漏,是事实 |
| 消息中间件 | 支持 Spring Kafka / Spring AMQP / RocketMQ;不读取 Broker 真实消息 | 读 Broker 需要连接生产环境 |
| 动态消息目标 | Topic/Queue/RoutingKey/Tag 来自运行期参数时进入盲区 | 不猜测具体值 |
| 消息 Schema | 支持 JSON DTO 及 Avro/Protobuf 文件;不拉取 Schema Registry | 首版范围 |
| Kafka Streams / Spring Cloud Stream | 不在范围内 | 拓扑 DSL 是另一套语义 |
| 首次提交(无父提交) | 基线为空,所有符号算新增;报告明确标注 | 没有可比对象 |
为什么这些取舍是合理的
静态分析的定位
静态分析不能证明“改动无影响”,只能提供”有影响的证据”。 所以本工具的设计目标是:
- 少漏:宁可报一条低置信度的候选,也不静默丢弃
- 可解释:每条影响都有路径、用法、文件行号——人能自己判断
- 诚实:不确定就标低置信度,解析不了就进盲区清单
反过来,追求”精确无噪音”会让工具变成”什么都没发现”,反而更危险。
为什么置信度阈值是 0.15
低于这个值的路径通常已经衰减了 6 层以上,链接可靠度不到 20%。 再往下传,路径长度爆炸但信息量趋近于零。
为什么不自动修复
工具给出的是判断依据,不是补丁。跨程序影响的处置需要业务判断: 这个接口改动是否要通知对方团队?是否要保留兼容层?这些不是静态分析能回答的。
如果要扩展
几个自然的扩展方向,以及各自的代价:
| 方向 | 代价 |
|---|---|
| 跨仓库合并索引 | 需要处理同名类冲突,且要持续同步多个仓库的版本 |
| 运行时链路数据接入 | 需要部署探针,但能显著提升置信度 |
| Schema Registry 集成 | 需要网络访问与凭据管理 |
| 增量索引 | 需要处理缓存失效,收益在大仓库才明显 |
当前实现选择”每次从快照重建索引”,牺牲一点速度换取”索引永远和代码一致”—— 这个取舍在小到中型仓库上是划算的。