为架构写测试
「服务必须可独立部署」这样的架构目标,在没有检查手段之前只是愿望。适应度函数把愿望变成判定:该模块不得引用另一模块、该端点 p95 必须在 200 毫秒内、该服务不得与其他服务共享 schema。检查在每次变更时运行,属性要么此刻成立,要么构建变红。
静态检查与运行时检查
静态检查读取代码与配置:导入图、依赖方向、禁止使用的注解、schema 归属。它快速且确定。运行时检查读取遥测:延迟预算、错误率上限、以调用扇出衡量的耦合度。运行时检查能发现静态分析看不到的属性,但噪声更大,需要稳定的观测窗口。多数团队两者并用:静态检查做门禁,运行时检查做告警。
// fail the build if the domain layer imports infrastructure code
expect(moduleGraph(domain)).not.toImport('src/infra/**');
选择属性
挑选对业务重要且正在被侵蚀的特性:模块化、延迟、安全态势、数据归属。从一两条检查开始,先让它值得信任,再逐步增加。间歇性失败的适应度函数会教会团队忽略它,这比没有检查更糟。
处理违规
每条检查都需要带负责人和有效期的例外通道。没有出口的硬规则,会在第一次紧急发布时被关掉。例如,遗留模块可以被加入白名单六周并关联跟踪单,白名单像其他技术债一样被定期评审;例外到期后,构建重新失败。
与代码评审的对比
代码评审只能发现评审者恰好记得检查的东西,适应度函数每次都检查同一属性。评审在品味和上下文上仍然重要,但对于可以计算的规则,它是糟糕的执行机制。把可计算的规则自动化,把人的注意力留给其余部分。