垃圾代码 — 是对低质量源代码的俗称:难以阅读、结构糊乱、难以维护。根据 Stripe(2022)的报告,开发人员将达到40%的工作时间花在阅读和理解糟糕的代码上。在俄语社区中,这个术语非常普及,町至有一个专门的网站 govnokod.ru,开发人员在上面发布尤其典型的案例。
主要内容
垃圾代码 — 是对不符合最低质量标准的代码的主观但广为接受的征性形容。Robert Martin在《嵌洁代码》(2008)一书中将糟糕的代码定义为“阻碍人们理解它在做什么”的代码。垃圾代码可能在语法上是正确的甚至可以运行,但其维护对团队来说成为了梦魆。
垃圾代码这个术语主要在俄语社区中流行。在英语中,使用更正式的术语:spaghetti code、dirty code、technical debt code。但是,“垃圾代码”的情感色彩更准确地传达了开发人员对这类代码的态度——焦躁、厌恶和职业愤慨的混合。
根据 McKinsey(2023)的研究,具有高技术债水平的公司——而垃圾代码是其主要组成部分——在开发新功能时要多花费20–40%的资源。代码质量直接影响业务指标,这不是比喻,而是已被证实的事实。
没有客观的指标,但有实用的标准:如果开发人员花费超过5分钟来理解一个20行的函数——这就是垃圾代码。如果修改一行代码破坏三个不相关的模块——这就是垃圾代码。如果代码无法在不全部重写的情况下被测试覆盖——这就是垃圾代码。
复制粘贴(copy-paste programming)— 最明显且最容易检测的特征之一。当同一段代码在多个地方以极小的变化重复出现时,这不仅是垃圾代码,还是未来错误的源头。在一个地方修复而在另一个地方遗漏——这是典型情况。
无意义的变量名称——经典问题。名称为`a`、`b`、`x`、`data`、`temp`、`tmp`、`result`、`list`、`obj`的变量不传递任何关于其用途的信息。代码阅读者必须分析整个函数才能理解变量中存储的是什么。Robert Martin称这为“名称中的谎言”——名称承诺信息,但不提供。
深层嵌套——当条件、循环和错误处理创建出具有5+级缩进的结构时。这样的代码无法不用水平滚动条或精神跟踪所有层级就能阅读。这是导致错误的直接原因:逻辑运算符很容易混淆,闭合括号很容易被忽略。
| 特征 | 垃圾代码示例 | 干净代码 |
|---|---|---|
| 复制粘贴 | 同一段代码被复制5次 | 提取为函数 |
| 名称 | `var a = getData()` | `var userList = getData()` |
| 嵌套 | 6层 if/for | 2–3层,提前返回 |
| 函数 | 300行的函数 | 拆分为3–5个方法 |
| 注释 | `i++ // increment i` | 不需要注释的清晰代码 |
Dead code — 从不被使用的函数、变量、类。这增加了代码量,分散开发人员的注意力,并制造关于系统能力的假象。Magic numbers — 没有上下文的数字。God-类 — 同时处理所有事情的类,违反单一职责原则(SOLID: S)。
时间不足——最常见的原因。当截止日期逼近时,开发人员为了速度而牺牲质量。战术上这可能是合理的,但战略上这是技术债的积累。问题在于,“临时”的垃圾代码很少被回头修复。
缺乏代码审查——第二个重要原因。当代码是单独编写而没有同事的审查时,不良模式会得到崽固和蔓延。代码审查不仅是质量控制,还是团队内部知识传播的方式。没有审查的项目不可避免地滑向垃圾代码。
开发人员技能低下或缺乏师徒制导。疙于监督的初级开发人员自然而然地编写垃圾代码——这是学习过程的一部分。问题出现在这些代码没有经过审查和重构就进入生产环境。
在以“能用就行”为座右铣的团队中,垃圾代码茂盛生长。缺乏编码标准、测试要求和审查流程创造了一个代码质量无人关心的环境。这样的项目很快就变成“遗留系统”——没人敢碰的代码。
主要后果是开发速度的降低。糟糕代码的矛盾在于它可以快速编写第一个版本,但每次后续修改所需要的时间越来越多。开发速度与代码质量的关系图是指数级的——超过某个阈值后,添加新功能变得几乎不可能。
人员流失——间接但严重的后果。开发人员,尤其是经验丰富的,不愿意与垃圾代码打交道。根据 Stack Overflow Developer Survey 2024,47%的开发人员称代码库质量是选择工作地点的关键因素之一。拥有糟糕代码的项目会丢失最优秀的员工。
安全——垃圾代码的另一个受害者。糟糕的代码包含更多漏洞:未处理的异常、SQL注入、XSS、内存泄漏。吻合unit测试和代码审查的高质量代码能在上线前捕获大部分这些问题。
SonarQube和类似工具可以以人工小时或天数来评估技术债。例如,500个关于复制粘贴的警告、200个关于魔术数字的警告和50个关于深层嵌套的警告意味着30天的技术债。这些数据可以也应该展示给管理层,以证明重构的必要性。
DRY(Don't Repeat Yourself)原则——第一个应该实施的原则。每一个逻辑片段应该只存在一份。替代复制粘贴——将重复的代码提取到单独的函数、类或模块中。替代魔术数字——使用有名称的常量。替代长函数——使用多个小函数。
KISS(Keep It Simple, Stupid)原则防止过度复杂。如果一个任务可以用10行解决——不要写50行。如果循环比流更简单——使用循环。如果普通函数比装饰器更容易理解——编写函数。简单性是易于维护的代码的主要特征。
Boy Scout Rule原则——“让代码比你发现它时更好”。即使每次修改中的小改善随着时间推移也能将垃圾代码变成体面的代码。重命名变量、拆分大函数、添加测试——每一个改善都很重要。
// 糟糕的代码 — 复制粘贴、魔术数字、糟糕的名称
function calc(a, b, c) {
let x = a * 0.85;
if (b > 1000) { x = x * 0.9; }
let y = c * 0.85;
if (b > 1000) { y = y * 0.9; }
return x + y;
}
// 干净的代码 — 清晰的名称、DRY、常量
const DISCOUNT_RATE = 0.85;
const BULK_THRESHOLD = 1000;
const BULK_DISCOUNT = 0.9;
function applyDiscount(amount, quantity) {
let price = amount * DISCOUNT_RATE;
if (quantity > BULK_THRESHOLD) {
price = price * BULK_DISCOUNT;
}
return price;
}
function calculateTotal(items, quantity) {
return items.reduce((sum, item) => {
return sum + applyDiscount(item, quantity);
}, 0);
}
让我们看一个Python中的典型例子。该函数处理订单,但处理得很糟:80行代码、深层嵌套、魔术数字、重复代码。重构后,代码变得可读、可测试且易于维护。
# 糟糕的代码 — 一个函数处理所有事情
def process_order(order):
if order.get("type") == "premium":
if order["amount"] > 100:
discount = 0.8
else:
discount = 0.9
else:
discount = 1.0
total = order["amount"] * discount
return total
# 干净的代码 — 提取的函数和常量
class OrderProcessor:
PREMIUM_DISCOUNT_HIGH = 0.8
PREMIUM_DISCOUNT_LOW = 0.9
PREMIUM_THRESHOLD = 100
def get_discount(self, order):
if order.type == "premium" and order.amount > self.PREMIUM_THRESHOLD:
return self.PREMIUM_DISCOUNT_HIGH
return self.PREMIUM_DISCOUNT_LOW
def calculate_total(self, order):
return order.amount * self.get_discount(order)
一个好的函数只做一件事情并且做得很好。如果一个函数执行三个不同的操作——拆分它。如果一个函数包含超过20行——大概率可以拆分。如果一个函数有超过两层的缩进——需要重构。
静态代码分析器——对抗垃圾代码的第一道防线。ESLint(JavaScript)、Pylint(Python)、SonarQube(多语言)、Checkstyle(Java)自动检测复制粘贴、魔术数字、空 catch 块、过长的函数以及数百种其他反模式。
Code style和代码格式化工具——第二层保护。Prettier、Black、gofmt自动格式化代码,消除空格、缩进和括号的问题。团队中统一的编码风格让代码无论是谁编写的都可读。关于格式化的争论应该自动化。
代码审查——第三个也是最重要的阶段。没有任何分析器能够替代人类发现解决方案架构的错误或开发人员选择了错误的方法。有效的审查需要时间,但通过显著减少垃圾代码的数量而得到回报。
常见问题
极为罕见。在原型开发或黑客马拉松中,速度比质量更重要,但这类代码应该被标记为临时性的,且不应该在未经重构的情况下进入生产环境。在生产环境中,垃圾代码没有任何理由——现在每一次时间的节省都将在未来变成多倍的损失。
新手的代码是经验不足但往往真诚的代码,随着技能的提升而改善。垃圾代码是对质量的故意或无所谓的忽视。新手可能编写不是最优的但可读的代码。而垃圾代码本质上是不可读的——其作者不在乎别人是否理解它。
重写是最后的手段。逐步重构更安全:分离模块,用测试覆盖它,逐部分重写。完全重写风险很高——您可能丢失老代码中积累的业务逻辑,包括没有人文档记录的边界情况处理。
使用指标:SonarQube会以小时为单位显示技术债。向管理层展示老代码中的bug浪费了多少时间。比较项目中“干净”和“脏乱”部分新功能的开发速度。用商业语言解释:时间就是金钱,而垃圾代码是在浪费金钱。
《嵌洁代码》 Robert Martin(2008)— 高质量编程的圣经。它描述了命名、格式化、错误处理和测试的原则。附加:《完美代码》Steve McConnell、《重构》Martin Fowler、《设计模式》Gang of Four。每位开发人员都应该阅读这些书籍。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。