第 10 章 · 项目管理与交付
收官章:代码写完只是开始,「按时交付、上线稳定、持续演进」才是项目的终点线。本章讲三件事:估算与进度管理(为什么延期、怎么估)、CI/CD 流水线(从提交到上线的自动化之路)、上线后的运维(监控、告警、事故响应)。
10.1 估算:软件最著名的谎言
「还要多久?」是软件工程最难回答的问题。估算系统性偏乐观的三个机制:遗漏的隐性工作(联调、测试、部署、改 review 意见占真实工时的 40%+,估算时常常归零)、打断与并行(每人每天有效编码 4 小时是常态)、未知与风险(没做过的事没有历史数据)。实用对策:
- 用相对估算:扑克牌规划(Planning Poker)给 backlog 打「故事点」(相对大小),跑两轮后用历史速度(每轮完成多少点)外推,比绝对工时诚实;
- 加缓冲要显式:估算 × 风险系数(新领域 1.5~2),把「不确定性」写成预算而不是藏进数字;
- 盯「完成定义」(Definition of Done):写了 ≠ 完成;完成 = 代码合并 + 测试通过 + 文档/配置齐 + 已部署。用 DoD 校准的「完成百分比」才可信。
** Brooks 定律(第 1 章)在估算环节的镜像:进度落后时,砍范围(scope)比加人诚实。**「功能做不完就砍 C 级需求」(第 3 章 MoSCoW)是健康项目的常规操作,「靠加班硬撑」是衰竭的前奏。
10.2 CI/CD:从提交到上天的自动扶梯
持续集成/持续交付把「代码合并 → 用户可用」的路径自动化:
push ──▶ CI 流水线
├─ 编译 + Lint(秒级,失败立刻反馈)
├─ 单元测试套件(第 8 章快测试,分钟级)
├─ 集成测试 + 构建 Docker 镜像(第 12 章 Dockerfile)
└─ 自动部署到测试环境 ──▶ 人工/自动验收
└─▶ 部署生产(灰度:先 5% 流量,观察,再全量)
CI 的价值在反馈速度:提交后 10 分钟内知道「我搞坏了什么」,越早修越便宜(第 1 章的缺陷成本曲线)。纪律:主干随时可发布(第 9 章 trunk-based 的配套),红了先修或 revert(第 9 章),「先合着回头修」是 CI 文化崩坏的开始。
CD 的关键设计是发布与部署解耦:代码部署上去不等于功能生效(特性开关控制),出问题不是「回滚版本」而是「关掉开关」,把事故恢复从分钟级降到秒级。灰度发布(先放小流量)、蓝绿部署(新旧两套环境切流量)是同一思想的不同实现,核心都是给「上线」装上可逆性。
10.3 上线之后:可观测性三支柱
系统在生产环境跑起来,黑盒状态不可接受。可观测性(Observability)三支柱:
- 日志(Logs):结构化优先(JSON 格式带 trace_id),集中收集(ELK/Loki);日志是事故后的「案发现场」,数据库课第 8 章 WAL 的「先写日志」思想在这里变成「先想清楚要记什么」;
- 指标(Metrics):数值的时间序列(QPS、延迟分位数 P95/P99、错误率、资源水位),Prometheus + Grafana 看板;告警规则挂在指标上(错误率 > 1% 持续 5 分钟);
- 追踪(Tracing):一次请求跨过 8 个服务,分布式追踪(trace_id 串联)还原完整链路,微服务(第 4 章)的排障刚需。
监控的成熟度标准:你在用户之前知道系统坏了。SRE 领域的 SLI/SLO(服务水平指标/目标)把「多稳定才算稳定」量化(如「月度可用性 99.9% ≈ 每月 43 分钟故障预算」),错误预算花完就暂停功能开发转修稳定性,这是「可靠性」从口号变成数字管理的路径。
10.4 事故响应:慌乱中的章法
生产事故(线上故障)的响应有标准节奏:
- 止血优先于找因:回滚(第 9 章 revert 的运行时版)、关开关、限流,先恢复服务;
- 同步信息:指定一人对外通报(「故障现象、影响面、当前动作」),避免全员各查各的;
- 复盘不追责:事后写 postmortem(时间线、根因、行动项),只查系统不查个人(blameless),否则下次大家忙着藏问题;
- 行动项落地:每个复盘产出的改进项进 backlog 跟踪,不然事故必然复读。
「没有事故报告的团队,只是还没攒够事故」;高可用系统的高可用,一半是技术(第 8 章冗余),一半是这种组织学习机制。
常见错误
- 估算直接乘 1 加系数(拍脑袋的绝对工时);用历史速度(故事点吞吐)是唯一有数据支撑的外推。
- CI 失败常态化。「红了几天了,大家绕着走」;CI 的全部价值依赖「绿是默认状态」,红即最高优先级。
- 直接部署生产。没有灰度、没有回滚预案;「上线」应该和「下线」一样容易(第 9 章 revert 思想)。
- 监控只看平均值。平均延迟 200ms 可能掩盖 P99 = 8s(少数用户灾难);分位数才是用户体感。
练习
- 动手:给你的课设搭一条最小 CI(GitHub Actions):push 触发 → 编译 → 跑测试 → 打印结果,故意推一个破坏测试的提交,体验「红」的反馈速度。
- 手算:某服务月可用性目标 99.9%,本月已耗故障预算 30 分钟,还剩多久?一个新功能的上线上风险 vs 剩余预算,给「能不能现在上线」一个数字化的判断。
- 动手:为你的项目写一份「上线检查单」:部署步骤、回滚命令、验证方法(哪些接口 curl 一下)、告警联系人,打印出来贴桌上。
- 思考:回顾全课程:从软件危机(第 1 章)到本章的可观测性,这门课的所有实践都在回答「复杂度与变化」。挑出你认为最不依赖工具、纯靠人的习惯的三条实践,解释为什么它们不会过时。