公司做组织调整的时候,群里有人讨论“必须裁掉一个,你觉得该干掉哪个”。有人开玩笑说:“保洁吧,让这些当官的轮流干。”
这句话听起来是在吐槽岗位价值,但我在项目里遇到过类似问题:系统出现故障,团队第一反应也是“砍掉一个环节”。后来才发现,很多看似可以砍掉的东西,其实承担的是没人关注的稳定性职责。真正危险的是,某个能力长期没有进入系统设计,只靠某个人或者某个流程撑着。
有一次接手一个订单系统,线上经常出现订单状态异常。业务同学认为问题出在运营审核流程,开发同学认为是数据库慢,最后排查发现,真正的问题是订单状态流转完全依赖一个后台人员的操作经验。
当时的代码很简单。
public void updateOrderStatus(Long orderId, String status) {
Order order = orderMapper.findById(orderId);
order.setStatus(status);
orderMapper.update(order);
}
这个方法上线初期没有问题,因为订单量小,操作人员知道哪些状态可以修改。
比如待支付订单可以改成已取消,支付成功订单可以改成退款中,人工处理异常订单时也能直接修正。
但是业务增长以后,问题开始暴露。
一个新员工接手后台,不知道订单状态之间的限制;一个运营同学为了处理投诉,直接把订单改成完成;一次批量修复数据时,把几十个退款中的订单改回了支付成功。
代码没有报错,数据库也没有异常,但是业务数据已经错了。
这里其实有个坑。
很多系统刚开始设计时,会把业务规则放在人脑里。
开发认为:“这个流程大家都知道。”
运营认为:“以前都是这么操作的。”
测试认为:“正常流程没问题。”
但是几年以后,人员变化、业务增加,原来的隐性规则就会变成线上风险。
后来我们重新梳理订单状态,把修改入口收紧。
订单状态不能由外部直接传字符串,而是通过业务动作驱动。
public void cancelOrder(Long orderId, String operator) {
Order order = orderMapper.selectForUpdate(orderId);
if (!OrderStatus.PAYING.equals(order.getStatus())) {
throw new BusinessException("当前状态不能取消订单");
}
order.changeStatus(OrderStatus.CANCELED);
orderLogMapper.insert(
new OrderLog(
orderId,
"CANCEL",
operator
)
);
orderMapper.update(order);
}
这里改动最大的地方,不是加了一个判断。
而是把“谁都可以改状态”变成“只有符合业务规则的动作才能改变状态”。
数据库里的状态字段还是存在,但它不再代表全部逻辑,只是业务结果。
后面又遇到一个问题。
有些订单需要自动关闭。
以前运营每天定时筛选超时订单,然后手动处理。订单少的时候没有问题,但是一天几十万订单以后,人工处理已经无法保证准确。
当时有人提出直接删掉人工流程,全部改自动化。
这个方案听起来很彻底,但线上系统很少一步到位。
因为人工流程背后通常还有异常处理能力。
比如支付渠道延迟,订单已经支付但是支付通知晚到了;比如库存服务异常,订单关闭时库存没有释放。
所以我们没有简单删除人工入口,而是增加了自动处理和人工补偿两个路径。
public void closeTimeoutOrder() {
List<Order> orders = orderMapper.queryTimeoutOrders();
for (Order order : orders) {
try {
transactionTemplate.execute(status -> {
order.changeStatus(OrderStatus.CLOSED);
orderMapper.update(order);
stockService.release(order.getId());
return true;
});
} catch (Exception e) {
retryTask.save(order.getId(), e.getMessage());
}
}
}
自动任务负责处理大部分正常情况,人工只处理异常情况。
这样做以后,运营人员不需要每天检查几万条订单,但是系统也没有失去人工干预能力。
很多团队在做优化时,喜欢问:“这个岗位能不能不要?”
技术团队更应该问:“这个能力有没有进入系统?”
如果一个线上服务只能靠某个开发记住部署顺序,如果一个订单流程只能靠某个运营知道规则,如果一个数据库修复只能靠某个人执行脚本,那说明系统还没有真正承载业务。
后来我们又增加了状态机校验、操作日志和异常补偿。
public boolean changeStatus(Order order, OrderStatus target) {
Set<OrderStatus> allow =
statusRule.get(order.getStatus());
if (!allow.contains(target)) {
return false;
}
order.setStatus(target);
return true;
}
这段代码解决的是另一个问题:规则集中管理。
以前每个地方都可能修改订单状态,新增一种状态需要搜索整个项目。
现在状态变化关系集中维护,代码Review时也更容易发现问题。
很多线上事故不是因为系统没有人维护,而是系统把关键能力放在了人的经验里。
人员减少以后,暴露出来的不是岗位数量问题,而是软件设计问题。
真正需要被优化的,往往不是某个人,而是那些没有进入代码、没有自动化保障、没有留下约束的业务规则。