本系列用来收集一些工作中常见的丑陋代码。
实例分析
起因
初始实现: 经验丰富的A同学封装了一个经典的安全除法函数:
func SafeDivideFloat64(dividend, divisor float64) float64 { if divisor == 0 { return 0 } return dividend / divisor }
这个实现简洁明了,功能符合函数名称。
需求变更: B同事为满足特定需求,修改了函数实现:
func SafeDivideFloat64(dividend, divisor float64) float64 { if divisor == 0 { return 0 } return math.Round(dividend / 1000 * 1000 / divisor) }
修改引入了舍入操作,改变了函数的基本行为。
命名问题: 虽然功能发生了变化,但函数名未作相应调整。理想情况下,应改名为如SafeDivideFloat64R3。
影响
- 广泛的使用范围
- SafeDivideFloat64 函数在60多个文件中被使用,影响范围广泛。
- 精度问题
- 单次使用:在接口返回时,影响相对可控。
- 多次使用:在复杂计算中多次调用,可能导致精度累积误差。
- 潜在风险:在大额计算或比例转换时,微小的精度差异可能引发严重的结果偏差。
- 认知和使用风险
- 新同事认知盲点:
- 容易忽视函数的隐含逻辑。
- 可能误认为是简单的安全除法,忽略舍入操作。
- 熟悉代码同事的潜在错误:
- 在疲劳或压力下可能疏忽,误解函数行为。
- 望文生义陷阱:
- 函数名暗示简单除法,易导致错误假设。
- 即使经验丰富的开发者也可能在快速浏览时误解。
- 新同事认知盲点:
- 长期维护挑战
- 知识传递断层:随时间推移,了解修改原因的人员可能离开或遗忘细节。
- 使用风险增加:由于知识传递不足,错误使用的可能性随时间增加。
处理方式
- 函数重命名与新函数实现
- 将现有的
SafeDivideFloat64
重命名为SafeDivideFloat64R3
- 实现新的
SafeDivideFloat64
函数,保持原始的安全除法逻辑
- 将现有的
- 废弃旧函数
- 标记
SafeDivideFloat64R3
为废弃(deprecated) - 添加注释说明潜在风险和使用注意事项
- 使用编译器警告或代码分析工具标记,便于识别需要替换的调用
- 标记
- 新代码规范
- 在新编写的代码中,严格使用
SafeDivideFloat64
- 禁止在新代码中使用
SafeDivideFloat64R3
- 在新编写的代码中,严格使用
- 渐进式替换策略
- 制定计划,逐步替换现有代码中的
SafeDivideFloat64R3
调用 - 优先处理关键业务逻辑和高频调用的部分
- 制定计划,逐步替换现有代码中的
- 文档和培训
- 更新技术文档,清晰说明两个函数的区别和使用场景
- 对团队进行培训,确保所有成员了解这一变更及其重要性