第十六章 独立负责
一
赵北辰走的那天,把一个文件夹拷给了王志远。
文件夹里有三样东西:一份系统架构图、一份模块负责人清单、一份技术债务清单。
架构图上,"订单履约模块"旁边的负责人从"赵北辰"改成了"王志远"。
"这个模块以后你负责。"赵北辰说。"日常开发、技术方案、线上问题,都是你的。"
王志远盯着屏幕上自己的名字,下意识地把手从键盘上挪开了——怕手汗把键盘弄湿。
订单履约模块是整个系统的核心之一——从用户下单到商品发货,中间涉及库存扣减、支付确认、物流对接、状态流转,逻辑复杂,上下游依赖多。
"我能行吗?"他问。
"你觉得呢?"
王志远想了想。半年前他会说"不行"。现在他说:"我试试。"
赵北辰笑了:"'试试'比'不行'好,但不如'我来'。"
王志远深吸一口气:"我来。"
"这就对了。"
二
独立负责的第一周,王志远就遇到了挑战。
产品经理李想提了一个需求:给订单履约流程加一个"拆单"功能——当一个订单包含多个仓库的商品时,自动拆成多个子订单,分别发货。
这个需求涉及订单模型的改造、库存扣减逻辑的调整、物流对接接口的扩展,影响面很大。
以前这种需求,赵北辰会先出一个大致的方案,然后让王志远去细化和实现。现在赵北辰不在了,从方案到实现,全是他一个人的事。
他花了两天时间做调研和设计,写了一份技术方案文档。然后他约了一个技术方案评审会。
参加评审的有苏薇、陈锐、周旋经理,还有仓储组的一个同事老孙。
三
评审会上,王志远打开了他的设计文档,开始讲解。
他讲了拆单的触发条件、拆单算法、子订单的状态流转、与库存和物流的交互方式。他画了时序图、类图、状态机图,还准备了一份数据库变更方案。
讲完之后,提问环节开始了。
苏薇第一个问:"拆单之后,如果其中一个子订单发货失败,主订单的状态怎么处理?"
王志远回答:"主订单的状态由所有子订单的状态决定。如果所有子订单都发货成功,主订单状态变为'已发货'。如果有子订单失败,主订单状态变为'部分发货'。"
"那用户看到'部分发货'之后,能不能取消未发货的子订单?"
王志远愣了一下。他没有考虑过这个场景。
"这个……我回去补充一下。"
苏薇点了点头,没有追问。
老孙问了一个更尖锐的问题:"拆单的时候,库存扣减是在拆单前还是拆单后?如果拆单前扣减,拆单失败了怎么回滚?如果拆单后扣减,拆单成功但库存不足怎么办?"
王志远的额头开始冒汗。这个问题他想过,但没有想透。
"我的方案是拆单后扣减。"他说。"拆单成功后,每个子订单独立扣减对应仓库的库存。如果某个仓库库存不足——"
他停了一下,脑子飞速运转。
"如果某个仓库库存不足,那个子订单标记为'待补货',不影响其他子订单的发货。同时通知用户,给出预计补货时间。"
老孙想了想:"可以。但你需要考虑一个边界情况——如果用户在下单时看到的是'有货',拆单后变成'待补货',用户体验不好。建议在下单前就做库存预校验,按仓库维度检查。"
"好的,我补充这个逻辑。"
陈锐问了一个技术细节:"拆单算法的时间复杂度是多少?如果一个订单有一百个商品,分布在十个仓库,拆单的性能能保证吗?"
这个问题王志远有准备:"拆单算法的核心是按仓库分组,时间复杂度是 O(n),n 是商品数量。一百个商品、十个仓库的场景,我做了性能测试,拆单耗时不到 5 毫秒。"
陈锐没有再追问。
周旋经理全程没有提技术问题,但在最后说了一句:"方案整体可行,但有几个细节需要补充。志远,给你两天时间完善,完善后再发一版给大家确认。"
四
评审会结束后,王志远回到工位上,后背全是汗。
他数了一下——评审会上一共被问了十一个问题,他当场回答了七个,有四个需要回去补充。
四个没答上来的问题,每一个都是他设计时的盲点。
他想起赵北辰说过的话:"设计评审不是来展示你的方案有多好的,是来发现你的方案有什么漏洞的。被问倒不丢人,被问倒了还不改才丢人。"
他花了两天时间,把四个问题全部想清楚,补充到了设计文档里。他还加了一节"风险与应对",把评审会上提到的所有边界情况都列了出来。
修改后的文档发出去,苏薇回复了一个字:"可。"
这个"可"字,比任何长篇大论都让他安心。
五
拆单功能的开发用了三周。
过程中遇到了不少问题,但王志远发现,因为设计阶段想得足够充分,编码阶段的问题大多是实现层面的,不涉及架构调整。
最大的挑战是状态机。
订单的状态流转本来就复杂——待支付、已支付、待发货、已发货、已签收、已取消、已退款。加上拆单之后,主订单和子订单各有一套状态,而且子订单的状态变化会影响主订单的状态。
他画了一张状态机图:
主订单状态:
待支付 → 已支付 → 待拆单 → 已拆单 → 部分发货 → 全部发货 → 已完成
↓
部分取消 → 全部取消
子订单状态:
待发货 → 已发货 → 已签收
↓ ↓
待补货 退货中 → 已退货
↓
已取消
状态之间的转换规则有二十多条。他用了一个状态机框架来管理,每条转换规则都写了单元测试。
测试写了四十多个用例,覆盖了正常流程、边界条件、异常情况。他特别注意了并发场景——如果两个子订单同时更新状态,主订单的状态计算会不会出错?
他用乐观锁解决了这个问题,并写了一个并发测试来验证。
六
三周后,拆单功能上线了。
上线前,他做了一件赵北辰教他的事——写了一份上线检查清单:
## 上线检查清单
### 代码
- [x] 所有代码已通过 Code Review
- [x] 单元测试覆盖率 > 85%
- [x] 集成测试全部通过
- [x] 无 P0/P1 级别的 SonarQube 告警
### 数据库
- [x] 数据库变更脚本已 Review
- [x] 变更脚本在测试环境执行成功
- [x] 有回滚脚本
### 配置
- [x] 新增配置项已添加到配置中心
- [x] 配置项有默认值和说明
### 监控
- [x] 关键指标已添加监控(拆单成功率、拆单耗时)
- [x] 告警规则已配置
### 灰度
- [x] 灰度发布方案已确认(先 10% 流量,观察 1 小时)
- [x] 回滚方案已确认
### 通知
- [x] 已通知上下游团队(仓储组、物流组)
- [x] 已通知客服团队(新功能说明)
上线过程很顺利。灰度 10% 流量,观察一小时,各项指标正常。全量放开后,拆单成功率 99.8%,平均耗时 3 毫秒。
李想验收后说了一句:"这个功能做得很扎实。"
王志远笑了笑。他知道"扎实"这个词的分量——它意味着不只是"能用",而是"靠谱"。
七
上线后的第三天,出了一个小问题。
一个用户的订单拆成了三个子订单,其中一个子订单的物流信息没有同步到主订单页面。用户投诉说"我的订单显示未发货,但其实已经发了"。
客服把问题转到了王志远这里。
他查了一下,发现是物流信息同步的定时任务有一个 bug——当子订单数量超过两个时,第三个子订单的物流信息会被跳过。原因是一个循环的边界条件写错了。
// bug 代码
for (int i = 0; i < subOrders.size() - 1; i++) {
syncLogistics(subOrders.get(i));
}
// 修复后
for (int i = 0; i < subOrders.size(); i++) {
syncLogistics(subOrders.get(i));
}
size() - 1 应该是 size()。一个经典的 off-by-one 错误。
他修复了 bug,补了一个测试用例(三个子订单的场景),然后写了一份简短的故障报告:
## 故障报告
**现象**:子订单数量 > 2 时,最后一个子订单的物流信息未同步
**根本原因**:循环边界条件错误(off-by-one)
**影响**:约 50 个订单受影响
**修复**:修正循环条件,补充测试用例
**改进**:在测试用例中增加"多子订单"场景的覆盖
他把故障报告发到了群里,没有等别人来问。
苏薇看了之后回复:"发现快、修复快、报告清晰。不错。"
这是王志远第一次独立处理线上问题——从发现到修复到报告,全程没有找任何人帮忙。
他突然意识到,自己真的可以独立负责了。
八
那天晚上,他给赵北辰发了一条消息:
"赵哥,拆单功能上线了,运行稳定。今天出了一个小 bug,我自己修了。"
赵北辰回复了一个大拇指的表情,然后说:"看到了。故障报告写得不错。有一个建议——以后故障报告里加一条'如何防止类似问题再次发生',不只是修复当前的 bug,还要想想怎么从流程上避免。"
王志远想了想,在故障报告里加了一条:
预防措施:在 Code Review 检查清单中增加"循环边界条件检查"项。所有涉及集合遍历的代码,Review 时重点检查边界条件。
他把更新后的报告重新发了一遍。
赵北辰回复:"这就对了。修 bug 是治标,改流程是治本。"
九
那天晚上,王志远在笔记本上写下:
Day 195
第一次独立负责核心模块。第一次独立做方案评审。第一次独立处理线上问题。三个"第一次",一天之内。
苏薇说"可"的时候,我知道方案过关了。她的"可"比别人的"太棒了"更有分量——因为她从来不说客气话。
赵哥不在了,但他教我的东西都在。我发现自己开始用他的方式思考问题了——遇到事情先画图,先想影响范围,先问"最坏情况是什么"。
"我试试"不如"我来"。三个字的区别,是信心的区别。
他合上笔记本,看了一眼桌上的《程序员修炼之道》。书签夹在第七章——"石头汤与煮青蛙"。
书里说:做一个催化剂,而不是等待别人来推动变化。
他觉得自己正在从"被推动的人"变成"推动变化的人"。
这种感觉,很好。
:::{admonition} 小胖的笔记 :class: tip
本章知识卡片:独立负责模块
1. 技术方案评审流程
1. 写设计文档(架构、数据模型、接口、技术方案、风险)
2. 发出评审邀请(相关方:上下游、Tech Lead、产品)
3. 评审会(讲解 → 提问 → 记录问题)
4. 修改文档(补充评审中发现的问题)
5. 二次确认(邮件/群消息确认修改后的方案)
6. 开始编码
评审会的心态: - 被问倒 ≠ 方案不好,= 发现了盲点 - 记录所有问题,会后逐一解决 - 不要在会上争论细节,会后单独讨论
2. 上线检查清单
| 类别 | 检查项 |
|---|---|
| 代码 | Code Review 通过、测试覆盖率达标、无高级告警 |
| 数据库 | 变更脚本 Review、测试环境验证、有回滚脚本 |
| 配置 | 新配置项已添加、有默认值和说明 |
| 监控 | 关键指标有监控、告警规则已配置 |
| 灰度 | 灰度方案确认、回滚方案确认 |
| 通知 | 上下游团队已通知、客服已通知 |
3. 故障报告模板
## 故障报告
- 现象:用户看到了什么?
- 根本原因:代码/配置/数据层面的原因
- 影响:影响了多少用户/订单?
- 修复:做了什么修复?
- 预防:如何防止类似问题再次发生?
4. 状态机设计
适用场景:订单、工单、审批等有明确状态流转的业务
设计要点: - 画出所有状态和转换关系 - 每条转换规则都要有单元测试 - 考虑并发场景(多个状态同时变化) - 使用状态机框架管理,避免 if-else 地狱
:::