第二章:技术债务识别与评估——代码坏味道识别与量化分析

各位同学,今天我们来聊聊技术债务的识别与评估。说白了,就是怎么发现代码里的“坑”,以及怎么衡量这些坑有多深。

我在项目中见过太多团队,代码跑得动就万事大吉。直到某天改一个bug,发现牵一发动全身,改一行代码要花三天。嗯,这就是技术债务在讨债了。

2.1 22种经典代码坏味道

代码坏味道,这个词最早出自《重构》这本书。它不是bug,但暗示着更深层的问题。我个人习惯把坏味道分成三大类:

2.1.1 类与对象层面的坏味道

  1. 过大的类(Large Class)——一个类做了太多事。我曾经接手过一个3000行的“上帝类”,改一个功能要翻半小时代码。
  2. 过长的参数列表(Long Parameter List)——方法参数超过3个就该警惕了。超过5个?基本可以断定设计有问题。
  3. 基本类型偏执(Primitive Obsession)——用String表示电话号码,用int表示金额。你想想看,这多容易出错?
  4. 数据泥团(Data Clump)——三个字段总是一起出现,比如地址的省、市、区。为什么不封装成一个对象?
  5. 临时字段(Temporary Field)——某个字段只在特定场景下才有值。这种代码读起来很困惑。
  6. 过度耦合的消息链(Message Chains)——a.getB().getC().getD()。改一个地方,全链都得改。
  7. 中间人(Middle Man)——一个类的主要工作就是委托给另一个类。那它存在的意义是什么?

2.1.2 方法层面的坏味道

  1. 过长方法(Long Method)——方法超过20行就该考虑拆分了。我见过一个方法写了400行,里面还有三层嵌套循环。
  2. 重复代码(Duplicated Code)——这是最臭的坏味道。复制粘贴一时爽,维护起来火葬场。
  3. 过大的条件表达式(Large Conditional)——if-else超过三层,或者switch分支超过5个,就该用多态重构了。
  4. 复杂的三元表达式(Ternary Operator Abuse)——a ? b ? c : d : e 这种代码,你自己看得懂吗?
  5. 死代码(Dead Code)——注释掉的代码、永远不会执行的分支。留着干嘛?过年吗?
  6. 异曲同工的类(Alternative Classes with Different Interfaces)——两个类做同样的事,但方法名不一样。

2.1.3 继承与设计层面的坏味道

  1. 拒绝继承(Refused Bequest)——子类不需要父类的某些方法。这说明继承关系错了。
  2. 继承过深(Deep Inheritance)——继承层级超过3层,代码就很难追踪了。
  3. 发散式变化(Divergent Change)——改一个需求,要改多个类。这说明职责没分清楚。
  4. 霰弹式修改(Shotgun Surgery)——改一个功能,要改十几个文件。这比发散式变化更可怕。
  5. 依恋情结(Feature Envy)——一个方法频繁访问另一个类的数据。它是不是放错地方了?
  6. 不恰当的亲密(Inappropriate Intimacy)——两个类互相访问对方的私有成员。这关系太“亲密”了。
  7. 纯数据类(Data Class)——只有getter/setter,没有行为。这跟C语言的结构体有什么区别?
  8. 注释过多(Comments Overuse)——代码本身应该自解释。注释多往往意味着代码烂。
  9. 循环依赖(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 知识体系总览

下面这张图,是我梳理的本章知识结构。你可以把它当作一张地图,随时回来查阅。

技术债务识别与评估知识体系 22种代码坏味道 类与对象层面(7种) 方法层面(6种) 继承与设计层面(9种) SonarQube静态分析 环境搭建(Docker) 质量规则配置 CI流水线集成 SQALE量化评估 修复时间换算 严重级别系数 实际案例计算 核心目标:将隐性技术债务转化为可量化的修复计划 识别 → 分析 → 量化 → 排期 → 修复

2.5 本章小结

识别技术债务,就像医生看病。先通过“望闻问切”(坏味道识别)找到症状,再用“仪器检查”(SonarQube)确认问题,最后用“化验指标”(SQALE)量化严重程度。

我个人建议,每个团队都应该建立自己的技术债务清单。每两周回顾一次,把债务控制在可接受的范围内。别等到项目快交付了才发现代码已经烂到没法改——那时候就真的晚了。

一句话总结:坏味道是症状,SonarQube是听诊器,SQALE是化验单。三者结合,才能准确评估你的技术债务有多深。


公众号:蓝海资料掘金营,微信 deep3321