从一次生产事故中提升编码素养

Wangjie Lv2

前言

最近刚进项目组,正赶上项目组在总结生产事故,询问得知在一次双11上线过程中,由于新需求与现有接口入参相似度较高,考虑复用现有接口开发,最后上线完成后进行首单验证,触发故障,影响了线上业务。

故障代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public BaseResp<Void> xxxx(@RequestBody xxxx req) {
String xxx = req.getxxx();
String userName = req.getxxx();
String xxx = req.getxxx();
String xxx = req.getxxx();
String uid = req.getxxx();
// 根据userName去查uid 开卡新工卡数据库是否存在uid相同且是待开卡的数据,如果存在就走新工卡开卡流程,否则走老工卡开卡流程
Boolean isUsingNew = getUsingNew(uid, userName);
try {
if (isUsingNew) {
openNewCard(uid, userName, xxx, xxx);
} else {
openOldCard(req);
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
//修改状态为已领卡
return BaseResp.success("修改状态成功", null);
}

大家可以分析一下这段代码,细心的朋友很快便可以发现,在判断走新工卡开卡流程还是走老工卡开卡流程中,核心的参数为uiduserName,但是这两个参数都是通过前端传递的,我们都知道对于前端传递的参数,一定是不可信的,后端一定要做校验,特别还是这种会影响业务流转的参数。

引发的后果

最终,因为没有对参数进行校验,后续代码逻辑还有通用的查询还有删除逻辑,由于调用的通用方法,uid为空没有做条件拼接,导致大量数据被删除,引发灾难后果。开发团队连续3天没有休息,才完成对数据的恢复,上线时间也推迟了3周。

反思

对于这次的生产事故,表面上是对参数校验的疏忽,本质上我认为是编码素养的问题。方法复用的确能够显著减少代码冗余,提高代码的可维护性和可读性,但如果方法复用不当,尤其是在错误的地方复用,可能会引发一些问题,甚至导致程序的bug或者逻辑错误。

  • 接口设计

    • 接口单一职责原则

      假设有一个方法用于计算价格,后来在另一个场景中复用,结果导致了错误。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      // 原方法:计算商品价格
      public class PriceCalculator {
      public double calculatePrice(Order order) {
      double price = order.getBasePrice();
      if (order.isMember()) {
      price *= 0.9; // 会员折扣
      }
      return price;
      }
      }

      // 错误复用:计算运费时复用价格计算方法
      public class ShippingCalculator {
      public double calculateShipping(Order order) {
      double price = new PriceCalculator().calculatePrice(order);
      if (order.getWeight() > 10) {
      price += 20; // 加运费
      }
      return price; // 错误:将价格计算和运费计算混淆了
      }
      }

      在这个例子中,calculatePrice 方法是专门用来计算商品价格的,但是在 ShippingCalculator 中错误地复用了 calculatePrice 方法,导致运费的计算与价格计算混在一起。这违背了单一职责原则,导致逻辑混乱。

      正确的复用例子

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      // 原方法:计算商品价格
      public class PriceCalculator {
      public double calculatePrice(Order order) {
      double price = order.getBasePrice();
      if (order.isMember()) {
      price *= 0.9; // 会员折扣
      }
      return price;
      }
      }

      // 重新设计:正确分开运费计算和价格计算
      public class ShippingCalculator {
      public double calculateShipping(Order order) {
      double shippingCost = 0;
      if (order.getWeight() > 10) {
      shippingCost = 20; // 加运费
      }
      return shippingCost;
      }
      }

      public class OrderService {
      public double calculateTotal(Order order) {
      double price = new PriceCalculator().calculatePrice(order);
      double shipping = new ShippingCalculator().calculateShipping(order);
      return price + shipping; // 正确的复用
      }
      }
    • 最少入参/响应暴露原则

      最少入参暴露原则的目的是减少接口暴露的参数数量,从而降低接口的复杂度,提高系统的安全性、可维护性和可扩展性。

      最少响应暴露原则的目的是尽量减少接口返回的响应内容,确保返回的数据仅包含调用者所需要的信息,从而提高数据传输效率,并减少暴露不必要的敏感数据。

    • 对于入参做必要的参数校验

  • 核心(通用)接口设计

    • 核心能力/方法有必要参数校验
    • 方法定义应该有明确的入参和响应
  • Title: 从一次生产事故中提升编码素养
  • Author: Wangjie
  • Created at : 2024-12-13 14:28:28
  • Updated at : 2025-03-05 19:52:57
  • Link: https://wj0410.github.io/2024/12/13/从一次生产事故中提升编码素养/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
从一次生产事故中提升编码素养