软件项目开发全流程 · 第 31 课 · 演进与版本规划
1、软件是"活"的:演进是常态
前面 30 课,我们讲完了从立项、上线到长期维护的全过程。这一课换个视角:
软件一旦上线,就进入了"持续演进"的长跑。
看看真实世界里每天都在发生的事:
- 用户提新需求:"能不能加个批量导出?"
- 市场在变:"竞品上了协同编辑,我们有吗?"
- 依赖在老化:"这个库两年没更新了,被标记为不维护。"
- 合规在收紧:"新法规要求用户能一键删除自己的数据。"
演进(Evolution)不是意外,而是常态。 一款不演进的软件,不用多久就会滑向"遗留系统"——上一个阶段讲的系统退役,起点往往就是某一刻停止了演进。
所以本课要回答一个问题:
面对源源不断的变更,如何让软件有节奏、有章法地"长大",而不是长歪、长崩?
版本规划(Release Planning),就是这门"让软件健康生长"的手艺。
2、需求变更:唯一不变的是变化
第 6~7 课我们讲过需求获取与管理。但那是"从 0 到 1"的项目视角;上线之后,需求以另一种方式袭来:变更。
需求的变更主要有四个来源:
| 来源 | 典型例子 | 特点 |
|---|---|---|
| 用户反馈 | "导出格式不对,我要 CSV 不是 Excel" | 具体、高频、真假混杂 |
| 市场/竞争 | "对手免费了,我们的定价要调整" | 影响大、紧急 |
| 内部战略 | "公司要转向 B 端,个人版淡化" | 方向性、牵一发动全身 |
| 外部约束 | 法规、平台规则、依赖停维护 | 不可协商、有硬截止 |
一个反直觉的真相:
需求不是"提得越晚越省事",而是"改得越晚越昂贵"。
在需求阶段改一句话,成本是 1;设计阶段改,是 10;开发完改,是 100;上线后改,可能是 1000(还要处理存量数据和用户)。这就是著名的变更成本递增曲线。
但另一面也别走极端——不是所有变更都要接。什么都接,产品会变成"什么都想吃、什么都不精"的四不像。所以关键不是"拒绝变更",而是管理变更。
3、变更管理:从"随口一提"到"有序吸收"
小团队最容易犯的错:老板在产品群里说一句"这个加一下",开发就顺手做了。三周后没人记得做过,也没人知道为什么做——这就是上一课说的技术债在需求层的表现。
变更管理(Change Management) 的核心就一句话:
任何变更都要先"落纸",再"评估",后"排期"。
一个轻量但有效的流程:
- 提出(Request):用一句话写清"谁、为什么要、期望什么",进统一的需求池(Issue 列表即可)。
评估(Assess):这一步最重要,问三个问题——
- 价值:不做会怎样?做了收益多大?(对齐产品目标)
- 成本:要多少人天?影响哪些模块?(对齐技术债)
- 风险:会不会破坏兼容性?要不要数据迁移?
- 决策(Decide):接 / 不接 / 换个更小的做法。不接也要记录理由(避免反复被提起)。
- 排期(Schedule):进入某个版本的路线图,透明可见。
一句话总结流程:
提出 → 评估(价值/成本/风险) → 决策(接/不接/缩小) → 排期 → 实现 → 复盘💡 "不接"不是消极。把它写进"暂不考虑"清单、并说明理由,比含糊拖着更专业——既尊重了提出者,也保护了团队节奏。
4、兼容性:向后兼容是演进的生命线
软件演进时,最怕的不是"加功能",而是"改坏了别人正在用的东西"。
一个具体场景:你的 API 返回字段从 userName 改成了 username(少了个大写 N)。你觉得只是"规范了命名",但所有已经集成的调用方——包括第三方开发者、手机 App 的旧版本——全部崩了。
这就是兼容性(Compatibility)问题。它分两种:
- 向后兼容(Backward Compatible):新版本能"读懂"旧版本的数据/请求。→ 升级了服务端,旧客户端还能用。
- 向前兼容(Forward Compatible):旧版本能"容忍"新版本产生的数据(选择忽略不认识的字段)。→ 降级回滚时不炸。
向后兼容是演进的生命线。 一条实用原则:
加字段是安全的大多数情况,删字段/改语义/改类型才是危险动作。
具体做法(呼应第 34 课"扩展-收缩"):
- 先加不删:新字段先加上,旧字段标记废弃但保留。
- 双写双读:过渡期新旧格式同时支持。
- 观察无依赖后再删:确认没有调用方再用旧字段,才真正移除。
这跟数据库迁移的思路一模一样——把"一次危险切换"拆成"几次安全的小步"。
5、语义化版本:把"影响范围"写进版本号
要让用户(和同事)一眼知道"这次升级危不危险",业界通用方案是语义化版本(Semantic Versioning,SemVer):
主版本.次版本.修订号 MAJOR.MINOR.PATCH
2 . 5 . 1| 位置 | 何时 +1 | 对使用者的含义 |
|---|---|---|
| MAJOR | 有不兼容的改动 | ⚠️ 升级可能要改代码 |
| MINOR | 新增功能,向后兼容 | ✅ 直接升级,更安全 |
| PATCH | 修 bug,向后兼容 | ✅ 放心升级 |
举例:2.5.1 → 3.0.0 意味着"有破坏性变更,升级前请阅读迁移指南";2.5.1 → 2.6.0 意味着"加了东西,老用法照旧"。
常见误区:把内部重构也当成 MAJOR 发布。版本号是给使用者的契约,不是给开发者的日记。 用户不关心的内部变化,不构成 MAJOR 升级的理由。
一个配套习惯:维护变更日志(CHANGELOG),按"新增 / 修复 / 破坏性变更"分块,比一句"优化若干"有用一万倍。
6、废弃策略:如何体面地"拿掉"一个功能
持续演进不只有"加",还有"减"。功能越积越多,产品会臃肿、维护成本会爆炸。但直接删功能等于突然打用户一巴掌。
专业做法叫 废弃(Deprecation)——不是"删掉",而是"先宣布过时,再给过渡期,最后才移除"。
一个标准的废弃三步:
- 标记(Announce):文档标注"已废弃",说明替代方案和截止日期。API 响应头可带
Deprecation/Sunset提示。 - 警告(Warn):运行时打日志或告警,让还在使用的人提前发现。
- 移除(Remove):到期(比如 6 个月后)再真正删除,并写进 MAJOR 版本。
标记废弃 → 给过渡期(观察使用量) → 到期移除(MAJOR 发布)⚠️ 最危险的废弃方式:嘴上说"这个不用了",代码照旧跑着,某天新同事顺手删了,线上炸。废弃必须有公告、有期限、有监控。
7、版本节奏与路线图:让演进有章法
零散地"想到就发",团队会疲于奔命;憋一年发个大版本,用户会以为你们跑路了。成熟团队用节奏(Cadence)来治。
两种主流节奏:
| 模式 | 特点 | 适合 |
|---|---|---|
| Release Train(火车时刻表) | 固定周期发车(如每 2 周),赶不上的等下趟 | 产品成熟、用户多、求稳定 |
| 持续交付(CD) | 随时可发,按需发布 | 团队强、自动化成熟 |
"火车时刻表"的精髓是:版本号按时间走,功能按能力上。今天的车到点就开,没做完的功能不硬塞——节奏本身就是一种质量保证。
有了节奏,还需要路线图(Roadmap)指明方向。路线图不是甘特图,而是回答:
- 近期(本月):做什么 → 具体、可交付
- 中期(本季):达成什么目标 → 目标导向
- 长期(今年):往哪个方向走 → 方向性、可调整
路线图的要义:近的写"事",远的写"方向"。把三年后做什么都写死,是自欺欺人。
8、常见误区
- "变更管理 = 审批盖章":把它做成官僚流程,团队就会绕过它偷偷改。好的变更管理是"轻量、透明、有记录"。
- "版本号随便定":把破坏性变更塞进 MINOR,用户升级后炸锅,从此不敢信你的版本号。契约精神,靠版本号体现。
- "废弃 = 直接删":不留过渡期,是拿用户信任换开发省事。
- "路线图 = 承诺书":对客户把远期路线图说成"一定会做",做不到就失信。近的兑现,远的探索。
- "兼容性可以以后补":等出了不兼容事故再想兼容方案,只能回滚,代价翻倍。
9、动手实验 🎯
任务:给一个「版本演进方案」画出来。
- 打开贯穿案例「Team Todo」,假设它已上线到 v1.0.0(提供 API)。
收到三条变更请求,分别写出评估表(价值 / 成本 / 风险):
- (a) 任务支持标签分类;
- (b) 把
created_at从时间戳改成 ISO 字符串; - (c) 移除"已完成"接口(被新接口取代)。
- 为这三条各自确定版本号该怎么涨(MAJOR/MINOR/PATCH),并说明理由。
- 针对 (b) 和 (c) 两个危险变更,写出向后兼容 / 废弃过渡方案(先加什么、警告什么、多久后移除)。
- 画一张三个月的路线图:近期写具体功能,中期写目标,长期写方向。
- 写一份最小 CHANGELOG(v1.1.0 一节,含"新增/修复/破坏性变更"三块)。
10、今天你带走的三句话
- 演进是常态,不变才是异常;需求要管理,不是来者不拒,也不是一律拒绝。
- 兼容性是演进的生命线——加字段安全,删字段危险;危险变更要靠"废弃过渡"分步走。
- 版本号是给用户的契约,路线图是给团队的方向;有节奏的演进,才是可持续的演进。