第 7 章 软件配置与变更管理
学习目标
- 理解配置管理(CM)的目标:可追溯、可复现、可回滚
- 掌握配置项、基线、配置库的概念
- 掌握 Git 分支模型与标签策略
- 理解构建管理与制品管理
- 掌握变更控制流程(CCB)
- 掌握发布管理与回滚预案
7.1 配置管理的目标
软件配置管理(Configuration Management):对软件开发全过程中产生的所有"配置项"进行标识、控制、记录与审计,确保:
- 可追溯:任何版本的每个文件都能找到"谁、何时、为何"修改;
- 可复现:任何历史版本都能被重新构建出相同制品;
- 可回滚:线上出问题能迅速回到上一个已知良好状态;
- 受控并发:多人协作不互相覆盖。
没有 CM 的团队:找不到能用的版本、改坏无法回退、发布内容说不清——这些是"事故"而非"意外"。
配置管理的四个子活动
| 子活动 | 做什么 | 典型产出 |
|---|---|---|
| 配置标识 | 定义配置项、命名与编号规则、版本规则 | 配置项清单、命名规范 |
| 配置控制 | 变更审批、分支/合入控制、基线管理 | 变更记录、合入日志 |
| 配置状态记录 | 记录每个配置项的状态与历史 | 变更日志、审计报告 |
| 配置审计 | 功能配置审计(FCB)+ 物理配置审计(PCA) | 审计结论 |
- FCB(功能配置审计):交付物是否覆盖基线要求(该发布的都发了吗?);
- PCA(物理配置审计):交付物是否与受控版本一致(发出去的就是评审过的那份吗?)。
7.2 配置项(Configuration Item, CI)
配置项:纳入受控管理的独立单元。典型清单:
| 类别 | 例子 |
|---|---|
| 文档 | SRS、架构文档、ADR、测试计划 |
| 源代码 | 所有.java/.ts/...、脚本 |
| 构建脚本 | Makefile、pom.xml、Dockerfile、CI 配置 |
| 数据 | 数据库 schema、迁移脚本、配置文件(模板) |
| 第三方依赖 | 锁定的依赖版本(lock 文件) |
| 制品 | 构建产物(带版本号的 jar/镜像) |
原则:凡是"改变它会改变系统行为或可复现性"的东西,都应是配置项。
配置项命名与编号规范(示例):
- 文档:
DOC-SRS-OOS-v1.2; - 代码:仓库名 + 分支 + commit;
- 制品:
oos-order-1.4.0-<commit短hash>(制品名内嵌源码指纹,可反向追溯); - 数据库迁移:
V2026.09.01__add_refund_status.sql(版本化 + 不可修改已发布迁移)。
常见遗漏配置项(自查清单):
- CI/CD 流水线配置本身(改了流水线等于改了过程);
- 环境配置文件(数据库连接、功能开关默认值);
- 数据库 schema 与种子数据;
- 容器基础镜像版本;
- 监控告警规则(告警规则也是"行为定义");
- 域名/证书/网络策略(基础设施即代码的一部分)。
7.3 基线(Baseline)与配置库
- 基线:经过正式评审与批准、作为后续工作起点的配置集合,标识为某版本(如
v1.2.0)。 - 常见基线:
- 功能基线:需求评审通过后的 SRS;
- 分配基线:设计评审通过后的架构/接口;
- 产品基线:发布版本(代码 + 文档 + 测试数据)。
- 配置库通常分三层:
- 开发库(个人/特性分支):自由修改;
- 受控库(主干/发布分支):受控合入;
- 产品库(发布版本):只读归档。
基线纪律:
- 基线建立 = 评审通过 + 版本号 + 决策人记录,三者缺一不成立;
- 对基线的任何修改走变更流程(7.6);
- 基线只增不删(历史基线永久可查,支撑审计);
- "从哪个基线拉出"必须显式记录(分支/标签即指针)。
7.4 版本控制:Git 工作流
分支模型选型
| 模型 | 结构 | 适合 |
|---|---|---|
| Git Flow | main + develop + feature + release + hotfix | 版本化发布、节奏较慢 |
| GitHub Flow | 仅 main + 短命特性分支,随时可发布 | 持续部署 Web 服务(OOS 采用) |
| Trunk-Based | 主干 + 极短分支(≤1 天)+ 特性开关 | 高频率 CI/CD |
OOS 选择:Trunk-Based + 特性开关(feature flag)——大促期间可按需开关功能,而不依赖分支合并时机。
三模型对比要点:
| 维度 | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| 发布频率 | 低(版本化) | 中 | 高(每日多次) |
| 集成风险 | 中(develop 长存) | 低 | 最低(分支≤1天) |
| 回滚方式 | revert 或 hotfix | revert | revert / 关开关 |
| 功能未就绪时 | release 分支持有 | 分支延后合入 | 特性开关(代码在主干,功能关闭) |
| 运维成本 | 分支管理复杂 | 中 | 低,但依赖开关治理 |
基本纪律
- 分支短命、命名规范(
feat/ORD-123-coupon); - 小步提交,commit message 说清"为什么"(关联需求/缺陷 ID);
- 保护主干:禁止直接 push,必须走 PR + 评审 + CI 门禁;
- 冲突尽早解:长时间不 rebase 的分支是合并噩梦;
- 标签即版本:
git tag v1.4.0指向发布 commit。
Commit Message 规范(示例):
<类型>(<范围>): <祈使句摘要> # feat(order): 支持部分发货状态 # 关联: FR-ORD-021 <正文:为什么改、影响面、风险> # 背景: 跨境订单存在拆单发货, # 需要 PARTIAL_SHIPPED 态,状态机见 ADR-015类型:feat / fix / refactor / test / docs / chore;范围对应模块。
特性开关(Feature Flag)治理
Trunk-Based 的生命线,失控则"主干里堆满僵尸代码":
- 每个开关有 Owner 与预期移除日期;
- 开关类型区分:发布开关(短期)、实验开关(A/B)、运维开关(降级)、权限开关;
- 月度"开关审计":无主开关强制下线;
- 关键路径禁止"永久开关"(长期双份逻辑 = 双倍维护)。
7.5 构建与制品管理
- 构建:从源代码 + 依赖 → 可部署制品(镜像/包)的过程;
- 关键原则:构建不可变(immutable)
- 同一 commit + 同一依赖锁 → 必须产出行为一致的制品;
- 制品一旦生成不再修改,只增版本号;
- 线上运行的必须是"被测试过的那个制品",而非"重新构建的近似制品"。
- 制品仓库(Nexus/Artifactory/Registry)存储制品,CI 从仓库取,不从源码临时编;
- 依赖锁定:lock 文件(package-lock.json / pom + BOM / go.sum)纳入版本控制,防止"昨天能跑今天拉了新版依赖就崩"。
不可变构建的三个必要环节(缺一即破):
- 源码可定址:构建永远由 commit 哈希触发,制品名内嵌哈希;
- 依赖可定址:锁定文件进版本库;基础镜像用 digest 而非 tag;
- 构建可审计:构建日志、输入清单(谁改了什么)与制品绑定存档。
制品晋级(Promotion)模型:
构建(一次) → 预发验证 → 生产金丝雀 → 全量 同一制品逐级晋级,绝不重新构建; 任一级失败 → 该制品"死亡",新修复重新构建新制品。7.6 变更控制流程
变更请求(CR) → 影响分析 → 风险评估 → 决策(CCB/PO) → 实施 → 验证 → 记录关闭- 谁有权变更:小团队 PO 可批;大项目设 CCB(变更控制委员会),成员含 PM、架构师、QA、业务方代表;
- 影响分析必查:受影响的配置项、需求/测试、发布计划、回滚方案;
- 紧急变更(hotfix):可走快速通道,但事后必须补记录——“先斩后奏可以,不奏不行”;
- 变更记录:CR 编号、原因、影响、决策人、时间——审计与复盘的基础。
与第 3 章的需求变更、第 6 章的缺陷修复是同一套控制框架的不同入口:一切对基线的修改都走变更流程。
CR 表单最小字段:CR 编号 / 提出人 / 描述 / 理由 / 影响分析(配置项+测试+工期)/ 风险 / 建议(接受/拒绝/延期)/ 决策与决策人 / 日期。
决策记录三态:接受(排期实施)/ 拒绝(记录理由)/ 延期(记录进入哪个版本)。拒绝必须留痕——否则会被反复提出。
7.7 发布管理(Release Management)
- 版本号语义化(SemVer):
MAJOR.MINOR.PATCH- MAJOR:不兼容变更;MINOR:向后兼容的新功能;PATCH:向后兼容的修复;
- 发布物清单(release note):本版本新增/修复/已知问题,关联 CR 与缺陷 ID;
- 发布检查单:构建通过、测试达标、文档更新、回滚预案就绪、监控告警就位;
- 回滚预案:发布前明确"什么信号触发回滚、回滚到哪一版、数据如何兼容"。
发布检查单(OOS 模板,可直接抄):
- 制品经预发全量回归,哈希与预发一致?
- 准出标准(第 6 章)全部达成或有 CCB 豁免记录?
- Release note 已生成并通知用户?
- 回滚:触发信号(错误率 >0.5% 持续 5 分钟)/ 目标版本 / 数据兼容说明 已确认?
- 数据库迁移向后兼容(旧版本代码可运行新 schema)?
- 监控:核心 SLI 看板 + 告警规则已上线?
- 特性开关:本版本开关清单与默认状态已确认?
- 发布窗口与值班人(开发 + SRE)已确认?
发布策略与本章的关系:蓝绿/金丝雀/特性开关(第 11 章)是"发布动作"的形态;本章的不可变制品 + 晋级模型是其前提——策略再高级,制品可变则一切归零。
数据库变更的特别纪律
- 迁移脚本只进不改(已发布的 V 文件永不修改,问题用新 V 文件修);
- 扩展-收缩模式:先加列(兼容)→ 双写 → 切读 → 删旧列,每步可回滚;
- 大表 DDL 用在线变更工具,发布窗口内完成;
- 迁移与代码部署的顺序必须写进发布计划(先 schema 后代码,或反之,视兼容设计)。
7.8 度量
- 构建成功率、平均构建时长;
- 变更前置时间(从提交到上线)、变更失败率;
- 回滚频率与 MTTR(平均恢复时间);
- 依赖更新滞后(最新可用 vs 实际使用)。
指标解读:
- 构建成功率 < 90% → 主干不健康,暂停新功能先修绿;
- 回滚频率上升 → 查准出执行与金丝雀观察指标;
- 依赖滞后 > 90 天且含已知高危 → 专项升级窗口。
7.9 CM 的反模式
| 反模式 | 症状 | 对策 |
|---|---|---|
| 分支考古 | 存在 3 个月无人 rebase 的分支 | 分支寿命纪律 + 定期清理 |
| 可变制品 | 生产"重新编译一下" | 不可变三环节(7.5) |
| 口头变更 | “先改上,文档回头补” | CR 留痕门禁 |
| 配置漂移 | 生产配置与仓库不一致 | 配置即代码 + 漂移检测 |
| 开关坟场 | 50 个开关无人认领 | 开关审计(7.4) |
| 迁移即事故 | 线上直接改表结构 | 扩展-收缩模式(7.7) |
7.10 本章 FAQ
Q1:个人项目需要配置管理吗?
需要最小集:Git 仓库 + 语义化标签 + 依赖锁定。三者成本极低,却能避免"哪个版本能跑"这一最常见灾难。
Q2:hotfix 和"快速 CR"的区别?
hotfix 是线上缺陷修复通道(速度优先,事后补全 CR 记录 + 回归验证);快速 CR 是计划内的加急变更(仍走完整影响分析)。两者都不豁免"事后验证 + 留痕"。
Q3:revert 还是 forward-fix?
线上事故:优先 revert(最快恢复),事后 forward-fix 重新走完整流程。"边修边发"是事故放大器。
Q4:配置管理会不会拖慢速度?
拖慢的是"无记录变更"(看似快,实则把成本转嫁给回滚与排查)。CM 的正确姿势是让"受控变更"比"野变更"更快(PR 10 分钟合入 vs 事故 2 小时定位)。
Q5:环境(测试/预发/生产)本身算配置项吗?
算。环境由 IaC 生成、代码化声明、状态入库,环境的创建/销毁/扩缩容同样留记录(第 11 章 IaC 检查单)。“手动开过服务器”= 配置项脱管 = 漂移之源。审计时,"生产有一台不在代码库里的机器"是标准扣分项。
Q6:版本库的权限模型怎么设计?
分层:开发者可推自己的分支、可建 PR;主干保护(评审 + CI 才能合入);标签(发布)仅发布角色或机器人可打;制品库对所有人只读,仅构建流水线可写。原则:能改"真相"的人越少,审计成本越低。权限变更本身也要留记录(谁在何时给谁开了什么权限)。
附录 A:Git 操作规范速查表
| 操作 | 规范 |
|---|---|
| 分支命名 | feat/fix/refactor/test + 需求/缺陷编号 |
| 分支寿命 | ≤1 周(Trunk-Based ≤1 天) |
| 提交 | 小步、祈使句、带关联 ID;重构与行为变更不混 |
| Rebase | 合入前保持最新;共享分支禁 force-push |
| 合并方式 | Squash(主干历史干净)或 rebase merge;避免 merge commit 堆积 |
| 标签 | 语义化 + 附注标签;tag = 发布 commit |
| 紧急回退 | revert(新增 commit 抵消),不用 reset 改写历史 |
附录 B:OOS 发布日流程走查(时间线)
D-3 发布冻结:不再接受新需求,仅 P1/P2 缺陷修复 D-1 发布检查单(7.7 八项)全部勾选;发布窗口与值班人确认 D-0 09:00 构建发布制品(记录哈希);预发全量回归通过 10:00 金丝雀 5% 流量;SLI 看板盯守 10:30 放量 25%;12:00 放量 50%;16:00 放量 100% 16:00 冒烟验证(黄金路径走通) 18:00 发布公告(用户 + 内部) D+1 24 小时观察:SLI 无劣化 → 发布关闭;旧版本热备撤除 任一环节失败: 回退上一阶段(目标 <15 分钟),启动复盘(第 11 章)纪律:发布日"双人组"(一操作一盯盘);发布日禁止对同一系统做其他变更(单变更原则)——一次发布只引入一个变量,出问题时归因才成立。
附录 C:变更请求(CR)完整走查(OOS 示例)
场景:v1.3 迭代中,业务方要求退款流程支持"部分退款"(CR-031)。
| 步骤 | 动作 | 产出 |
|---|---|---|
| 1 提出 | BA 提交 CR:场景、价值、期望 | CR-031 表单 |
| 2 影响分析 | 状态机 +2 迁移;退款接口契约变更;8 条测试受影响;工作量 3 人日 | 影响分析附件 |
| 3 风险评估 | 中(资金链路);无新增外部接口 | 风险等级:中 |
| 4 决策 | CCB(PO+架构+QA+PM):接受,排入 v1.3(容量有 2 人日余量) | 决策记录 |
| 5 实施 | 需求 FR-REF-008 → 设计(状态机更新)→ 编码 → 测试,CR 号贯穿 | 代码 + 工件更新 |
| 6 验证 | 回归(退款族全量)+ 金丝雀发布 | 测试报告 |
| 7 关闭 | CR 状态 → 关闭;复盘:历时 9 天,偏差 +1 人日,原因入库(估算校准) | 关闭记录 |
要点:同一个 CR 编号出现在"需求编号、commit message、测试用例、发布说明"中——追溯由此闭环,不需要另维护一张手工追溯表(与第 3 章呼应)。
附录 D:产品基线与配置审计示例(v1.0 发布)
- FCB(功能配置审计):发布清单 vs 功能列表核对——12 个功能全部在制品中,0 遗漏 → 通过;
- PCA(物理配置审计):生产制品哈希 vs 预发库制品哈希 → 一致;14 个依赖版本 vs lock 文件 → 一致 → 通过;
- 审计记录:审计人(QA-李)、日期、发现 2 项问题(1 条告警规则未入仓库 → 当日补录),结论:通过,记录归档。
节奏:每次发布做 FCB+PCA(半自动化脚本);每半年一次含文档基线的全量审计。使用提示:本附录示例可直接作为团队首次审计的模板,替换数据即可。
7.11 小结
- CM 四目标:可追溯、可复现、可回滚、受控并发;四子活动:标识/控制/状态/审计;
- 配置项 = 一切影响行为/可复现性的资产;基线 = 受批准的起点,只增不删;
- Git 分支模型按发布节奏选型,OOS 用 Trunk-Based + 特性开关(开关要治理);
- 构建不可变三环节:源码可定址、依赖可定址、构建可审计;制品只晋级不重造;
- 变更控制:影响分析 + 决策三态 + 记录,hotfix 可快但必须补账;
- 发布纪律:SemVer + 检查单(8 项)+ 数据库扩展-收缩;
- 度量:构建成功率、前置时间、失败率、MTTR、依赖滞后。
思考题
- 为什么"线上运行的必须是测试过的那个制品"?临时重新构建会出什么问题?
- Trunk-Based + 特性开关 相比 Git Flow,在"大促前紧急下线某功能"场景下的优势是什么?
- 设计一份 OOS v1.5 的发布检查单(至少 8 项)。
- 一次 hotfix 跳过了 CCB,事后应补哪些记录?谁负责?
- 列出你项目当前"漏网"的配置项(对照 7.2 自查清单),并制定纳管计划。
- 设计一次"数据库大表加列"的扩展-收缩方案:每一步的兼容性与回滚点是什么?
- 你的团队特性开关现状如何?做一次 7.4 的开关审计:无主开关占比、最老开关年龄、下线计划。
- “构建成功率 <90% 暂停新功能"会不会引发"为绿而绿”(弱化测试保成功率)?如何防?