第二章:技术债务识别与评估——代码坏味道识别与量化分析
各位同学,今天我们来聊聊技术债务的识别与评估。说白了,就是怎么发现代码里的“坑”,以及怎么衡量这些坑有多深。
我在项目中见过太多团队,代码跑得动就万事大吉。直到某天改一个bug,发现牵一发动全身,改一行代码要花三天。嗯,这就是技术债务在讨债了。
2.1 22种经典代码坏味道
代码坏味道,这个词最早出自《重构》这本书。它不是bug,但暗示着更深层的问题。我个人习惯把坏味道分成三大类:
2.1.1 类与对象层面的坏味道
- 过大的类(Large Class)——一个类做了太多事。我曾经接手过一个3000行的“上帝类”,改一个功能要翻半小时代码。
- 过长的参数列表(Long Parameter List)——方法参数超过3个就该警惕了。超过5个?基本可以断定设计有问题。
- 基本类型偏执(Primitive Obsession)——用String表示电话号码,用int表示金额。你想想看,这多容易出错?
- 数据泥团(Data Clump)——三个字段总是一起出现,比如地址的省、市、区。为什么不封装成一个对象?
- 临时字段(Temporary Field)——某个字段只在特定场景下才有值。这种代码读起来很困惑。
- 过度耦合的消息链(Message Chains)——a.getB().getC().getD()。改一个地方,全链都得改。
- 中间人(Middle Man)——一个类的主要工作就是委托给另一个类。那它存在的意义是什么?
2.1.2 方法层面的坏味道
- 过长方法(Long Method)——方法超过20行就该考虑拆分了。我见过一个方法写了400行,里面还有三层嵌套循环。
- 重复代码(Duplicated Code)——这是最臭的坏味道。复制粘贴一时爽,维护起来火葬场。
- 过大的条件表达式(Large Conditional)——if-else超过三层,或者switch分支超过5个,就该用多态重构了。
- 复杂的三元表达式(Ternary Operator Abuse)——a ? b ? c : d : e 这种代码,你自己看得懂吗?
- 死代码(Dead Code)——注释掉的代码、永远不会执行的分支。留着干嘛?过年吗?
- 异曲同工的类(Alternative Classes with Different Interfaces)——两个类做同样的事,但方法名不一样。
2.1.3 继承与设计层面的坏味道
- 拒绝继承(Refused Bequest)——子类不需要父类的某些方法。这说明继承关系错了。
- 继承过深(Deep Inheritance)——继承层级超过3层,代码就很难追踪了。
- 发散式变化(Divergent Change)——改一个需求,要改多个类。这说明职责没分清楚。
- 霰弹式修改(Shotgun Surgery)——改一个功能,要改十几个文件。这比发散式变化更可怕。
- 依恋情结(Feature Envy)——一个方法频繁访问另一个类的数据。它是不是放错地方了?
- 不恰当的亲密(Inappropriate Intimacy)——两个类互相访问对方的私有成员。这关系太“亲密”了。
- 纯数据类(Data Class)——只有getter/setter,没有行为。这跟C语言的结构体有什么区别?
- 注释过多(Comments Overuse)——代码本身应该自解释。注释多往往意味着代码烂。
- 循环依赖(Circular Dependency)——A依赖B,B依赖A。这种代码迟早出问题。
核心观点:坏味道不是bug,但它是bug的温床。我建议团队每周花一小时做“坏味道巡检”,就像打扫房间一样,定期清理。
2.2 SonarQube静态分析实战
光靠人眼识别坏味道,效率太低。我们需要工具。SonarQube是我最常用的静态分析工具,没有之一。
2.2.1 快速搭建SonarQube
用Docker启动,三分钟搞定:
docker run -d --name sonarqube \
-p 9000:9000 \
-e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true \
sonarqube:latest
启动后访问 http://localhost:9000,默认账号密码都是 admin。
2.2.2 配置质量规则
SonarQube内置了多种规则集。我个人习惯这样配置:
| 规则类型 | 严重级别 | 建议配置 |
|---|---|---|
| 代码坏味道 | INFO / MINOR | 全部开启 |
| 潜在bug | MAJOR | 全部开启,阻断CI |
| 安全漏洞 | CRITICAL / BLOCKER | 必须修复才能上线 |
| 重复代码 | INFO | 重复率超过3%报警 |
2.2.3 集成到CI流水线
以Maven项目为例,在pom.xml中添加:
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
然后执行:
mvn clean verify sonar:sonar \
-Dsonar.projectKey=my-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=my-token
避坑指南:我曾经遇到过SonarQube分析结果和本地不一致的情况。后来发现是JDK版本问题。SonarQube 9.x需要JDK 11以上,切记。
2.3 技术债务量化评估——SQALE方法
坏味道找到了,怎么量化?SQALE(Software Quality Assessment based on Lifecycle Expectations)是目前最成熟的方法。
2.3.1 SQALE的核心思想
说白了,SQALE就是把技术债务换算成“修复时间”。一个坏味道需要多少小时修复,累加起来就是总债务。
公式很简单:
技术债务 = Σ(每个违规项的修复时间)
修复时间 = 基础修复时间 × 严重级别系数 × 上下文系数
2.3.2 严重级别系数
| 严重级别 | 系数 | 说明 |
|---|---|---|
| BLOCKER | 10 | 必须立即修复 |
| CRITICAL | 5 | 严重影响可维护性 |
| MAJOR | 2 | 一般性问题 |
| MINOR | 1 | 轻微问题 |
| INFO | 0.5 | 建议性改进 |
2.3.3 实际案例
假设一个项目有:
- 5个BLOCKER级别的循环依赖,每个修复需2小时
- 20个MAJOR级别的过长方法,每个修复需0.5小时
- 100个MINOR级别的命名不规范,每个修复需0.1小时
那么技术债务 = 5×2×10 + 20×0.5×2 + 100×0.1×1 = 100 + 20 + 10 = 130小时
130小时,相当于一个开发人员三周的工作量。这就是你欠下的技术债。
注意:SQALE的系数需要根据团队实际情况调整。我见过有些团队把BLOCKER系数设为20,因为他们修复一个阻塞问题平均要花4小时。不要死板套用公式,要结合自己的数据。
2.4 知识体系总览
下面这张图,是我梳理的本章知识结构。你可以把它当作一张地图,随时回来查阅。
2.5 本章小结
识别技术债务,就像医生看病。先通过“望闻问切”(坏味道识别)找到症状,再用“仪器检查”(SonarQube)确认问题,最后用“化验指标”(SQALE)量化严重程度。
我个人建议,每个团队都应该建立自己的技术债务清单。每两周回顾一次,把债务控制在可接受的范围内。别等到项目快交付了才发现代码已经烂到没法改——那时候就真的晚了。
一句话总结:坏味道是症状,SonarQube是听诊器,SQALE是化验单。三者结合,才能准确评估你的技术债务有多深。
公众号:蓝海资料掘金营,微信 deep3321