Appearance
演进式架构与适应度函数
定位:架构方法论深化系列之二。架构图是快照,系统活在时间里。这篇总结"架构是持续决策过程"的落地方法。内容包括:适应度函数怎么建、增量变更怎么走(绞杀者模式)、架构量子和可演进性怎么度量。
架构不是一次性决策
传统架构观的问题:架构师在项目初期画一张大图,随后进入"维护期"——图过时,人离开,决策理由失传。演进式架构观换一个前提:
- 架构 = 结构 + 一组受保护的架构特征(性能、安全、可模块化……)+ 持续验证它们的机制。
- 决策不是一次做对,而是保持每一步都可逆、可验证、低成本地走下一步。是持续的手艺活,不是一次性的雕塑。
- 演进的敌人往往不是技术而是组织。团队边界与服务边界不对齐,小步演进会淹死在协调成本里(见《康威定律与团队拓扑》)。
- 与《技术决策与ADR》的关系。ADR 记录"当时为什么这么选",适应度函数负责"选完之后别被人改坏"。一个管决策留痕,一个管决策存活。
一个直接推论:评估架构的标准从"是否最优"变成"能否演进"。能以小步、低风险、可回退的方式变更的架构,赢过任何一张完美但僵硬的大图。
适应度函数:把"重要的东西"变成可执行的检查
定义:对某项架构特征的客观化完整性评估,理想形态是能跑在 CI 里的自动化检查。分类两个正交维度:
| 分类维度 | 类别 | 含义 | 例子 |
|---|---|---|---|
| 粒度 | 原子(atomic) | 单独运行、检验单一特征 | 单接口 P99 延迟断言;循环依赖检查 |
| 粒度 | 整体(holistic) | 联合多项特征整体评估 | 全链路压测下的"容量+一致性"联合判定 |
| 主观性 | 客观 | 机器判定,无人类裁量 | 代码覆盖率阈值、依赖规则 |
| 主观性 | 主观(含群体评估) | 人来评,用 checklist 结构化 | 架构评审打分、代码健康度互评 |
工程上值得建的几类:
性能类 :核心接口 P99 < 200ms(压测流水线断言,超阈值红牌)
稳定性类 :变更后错误率环比涨幅 < x%;资源水位上限
耦合类 :模块依赖方向检查(分层规则、禁止反向依赖,ArchUnit 思想)
代码健康类 :圈复杂度阈值、重复率、热点文件的变更爆炸率
契约类 :调用方契约测试全绿(见《API设计与契约治理》)关键在自动化 + 阻断性。写在 wiki 里的架构规范等于没有规范。只有出现在 CI 红灯里、挡住合并的适应度函数,才真正参与自然选择。
落地时的三个常见坑:
| 坑 | 形态 | 解法 |
|---|---|---|
| 指标通胀 | 什么都要测,红灯常年挂,团队学会"忽略红灯" | 每个域只保护 2-3 个最重要的特征,其余放报表不放 CI |
| 阈值拍脑袋 | P99 阈值和真实业务毫无关系,误报/漏报都多 | 从线上真实分布取分位数反推,季度校准一次 |
| 只测不改 | 红灯挂两周没人修,检查沦为装饰 | 红灯必须在 SLA 内修复,修不动就显式降级并记账为债(见《技术雷达与技术债管理》) |
增量变更与绞杀者模式
大爆炸重写是演进式架构的反面。对必须替换的老系统,用绞杀者模式按子域逐步搬迁:
阶段一:搭门面(Facade),流量先不动
请求 ──> ┌──────────────┐
│ 老系统(整体) │ 新系统只旁路复制数据做比对
└──────────────┘
阶段二:按子域逐个搬迁
请求 ──> ┌──────────────┐
│ 门面/路由层 │──子域A──> 新系统A ✅
│ (按域切流开关) │──子域B──> 新系统B ✅
└──────┬───────┘──其余──> 老系统(收缩中)
│
(每切一个域,老系统就瘦一圈)
阶段三:老系统归零下线,门面视情况移除
请求 ──> 新系统A / 新系统B ...要点:
- 入口契约不变:对调用方永远呈现同一个门面,搬迁对上游无感,兼容性压力收敛在门面内部。
- 按业务子域切,不按技术层切:先搬订单读、再搬订单写、最后搬结算。而不是"先换数据库再换应用"这种全量横切。
- 双跑比对先行:新链路先以影子流量跑数周,比对新旧输出差异,归零后才切真实流量。切流阶梯见《系统切流通用方案》。
- 同族的增量手法还有扩展-迁移-收缩(并行修改)。新路径上线 → 存量逐步迁到新路径 → 老路径流量归零后删除。本质是绞杀者在"单个行为"粒度上的应用。四层检查点见《系统设计如何全方位考虑兼容性问题》。
绞杀者与两种失败路径的对照:
| 大爆炸重写 | 绞杀者模式 | |
|---|---|---|
| 交付节奏 | 冻结期数月,一次性切换 | 每个子域独立交付,持续见效 |
| 风险形态 | 切换日 all-in,失败即回不去 | 每步可回退,爆炸半径限于单子域 |
| 需求响应 | 冻结期老系统只修不加,需求积压 | 老系统继续接需求,搬到哪切到哪 |
| 常见死法 | 老新并行维护成本压垮团队,砍掉重写 | 门面层无人治理长成新的上帝模块 |
最后一行是绞杀者自身的坑:门面是临时建筑,迁移完成必须拆或收缩。否则绞杀者老树死了,门面成了新的绞杀者。
架构量子与可演进性度量
架构量子 = 能独立部署、独立产生业务价值的最小单元(及其配套的通信与数据设施)。
- 单体只有一个量子:任何变更都要整体发布,演进粒度粗。
- 微服务的价值不在"小",在于把系统切成多个量子:订单域能日级演进,不必拖着结算域一起发版。
- 判断拆分是否有效就看量子是否真实。一个"服务"若离开别的服务既不能部署也不能验证,它不是量子,只是进程层面的装饰。
单量子(单体): 多量子(真微服务):
变更 ──> 整体构建 ──> 整体发布 订单域变更 ──> 只发订单服务 ──┐
(任何小改动 结算域不动 │独立演进
都动全身) └── 互不阻塞,各自按节奏发布
(假的例外:改一个接口要
三个服务同版本上线 = 没有量子)可演进性本身也要度量,候选指标:
| 指标 | 含义 | 信号 |
|---|---|---|
| 变更提前期 | 代码合并到生产见效的时长 | 越长,量子越假 |
| 部署频率 | 单个量子的独立发布次数 | 长期 < 每周一次,说明耦合住了 |
| 变更成功率 | 发布后无需回滚/热修的比例 | 低 = 每步并不可逆,演进在赌 |
| 适应度函数通过率 | CI 中架构类检查的红灯频次与修复时长 | 红灯长期挂着 = 债务在复利,见《技术雷达与技术债管理》 |
| 可逆性 | 一次变更能多快回退且不丢数据 | 靠灰度与回滚,见《发布工程与灰度体系》 |
演进的运营节奏
演进不是等"想清楚了"再动,而是固定节拍地小步走:
- 季度演进规划:从适应度函数的历史数据里找恶化最快的特征,作为下季度演进主题。而不是架构师拍脑袋选感兴趣的。
- 每个迭代至少一个结构性小改:拆一个模块、消一条反向依赖、还一小笔债。演进停止的架构不是"稳定",是"僵化前夜"。
- 大改必带退出机制:任何"先共存"的设计(双写、双跑、门面)立项时就写清拆除条件和时间表。与《技术雷达与技术债管理》的淘汰环同思路。
- 演进决策留痕:拆哪个量子、先搬哪个子域,都属于难回头的结构性决策。按《技术决策与ADR》落记录,避免半年后没人记得"为什么先搬订单"。
可演进性追问链
接手或评审一个架构时,按这条链连问:
1. 这套架构上一次成功的结构性变更花了多久? -> 一年以上,演进能力存疑
2. 架构的哪些特征是"受保护"的? -> 列不出来 = 没有适应度函数
3. 这些保护是 CI 阻断还是文档约定? -> 文档约定等于没有
4. 最小的可独立部署单元是什么? -> 答不出量子在哪 = 假拆分
5. 老逻辑的退出机制是什么? -> 只加不减的架构终将僵死
6. 下一层债务先在哪爆发? -> 连不上债务视角 = 演进无排期
7. 门面/双写这些过渡物,拆除条件写在哪? -> 没写 = 明天的绞杀者是门面自己一句话总结
把架构当成活的。重要的特征写成适应度函数进 CI。大的变更走绞杀者小口啃。量子划分决定演进粒度,债务与可逆性持续度量。架构师的产出不是一张图,而是一套让系统自己变好的选择压力。
2026-10 新增(架构方法论深化系列)。