重复造轮子在编程中是一个比喻,指在已有经过验证的替代方案的情况下创建自己的解决方案。根据Tidelift(2024)的研究,超过80%的商业应用程序包含至少一个「轮子」——对标准库或流行包中已有功能的自实现。这种做法增加了开发和维护成本,也提高了引入错误的风险。
要点
重复造轮子是来自开发者社区的一个术语,指对已以现成库、框架或服务形式存在的功能创建自己的实现。在英语环境中使用表达reinventing the wheel——重新发明轮子。在中文中也有「轮子」、「自实现」、「自己造轮子」等变体。
这个比喻的起源与以下事实有关:轮子是人类最古老的发明之一。在21世纪试图重新创造它是毫无意义的。在编程中这个类比更加准确:现成的库是经过数千名工程师多年优化的「轮子」。创造自己质量更差的轮子——浪费资源。
RedMonk在一份分析报告(2023)中计算出,一个普通的商业应用程序使用大约500个外部依赖。如果开发人员自己编写每一个,项目成本将增加数十倍,上市时间将增加数年。包管理器生态系统(npm、Maven、PyPI、NuGet)正是为了避免重新发明轮子而存在的。
代码如果是重复造轮子,可以通过几个特征识别:以非标准方式解决标准任务,没有测试或文档,不支持现成库中早已考虑的边界情况。这样的代码通常是为了项目的「独特需求」而编写的,但实际上这些需求与典型需求没有任何区别。
定制解决方案在现成库因架构或许可限制而不适合时是合理的。重复造轮子是在没有客观原因的情况下产生的——出于「玩玩」的愿望、对他人的代码不信任或对现有工具的不了解。区别是根本性的:定制是有意识的选择,重复造轮子是错误。
第一个也是最常见的原因——不了解现有解决方案。初级开发人员可能不知道标准库中有用于解析JSON的内置函数。相反,他会手动编写解析器。这个问题对刚刚进入语言生态系统的新手尤其相关。
第二个原因——控制错觉。有经验的开发人员有时确信他们能「写得更好」,超过流行库的作者。统计数据说明相反:在数百万项目使用的库中出错的可能性显著低于新编写的代码。根据Synopsys(2024),开源代码平均每千行包含0.1个错误,而企业代码为1–2个。
第三个原因——缺乏重用文化。在不习惯开始工作前研究现成解决方案的公司中,每个开发人员都会创建「自己的轮子」。这导致代码碎片化:在一个项目中可能有三个由不同员工编写的不同HTTP客户端实现。
| 原因 | 典型开发者 | 后果 |
|---|---|---|
| 不了解 | 初级 | 标准任务未能以最优方式解决 |
| 控制错觉 | 高级 | 时间浪费在已有的代码上 |
| 缺乏文化 | 团队 | 代码库增长、重复 |
| 学习愿望 | 任何人 | 对学习有益,对生产有害 |
| 害怕依赖 | 技术主管 | 拒绝数百个经验证的解决方案 |
宜家效应——一种心理现象,人们对自己创造的东西评价高于客观上更好的现成物品。在编程中这表现为对「自己的轮子」的自豪感,即使现成库有明显优势也不愿替换它。
经济后果最为明显。根据Stripe(2022)的估计,开发人员将多达35%的工作时间用于创建已经以现成解决方案形式存在的代码。按10人团队的工资计算,每年大约浪费20万美元用于重新发明轮子。
技术后果包括代码库增长、测试覆盖率降低(自写代码通常测试更差)、错误和漏洞数量增加。此外,每个自写组件都是需要监控和维护的另一个故障点。
Google在其研究「Why Google Stores Billions of Lines of Code」(2023)中指出,即使是在最大的科技公司,也有关于添加新依赖或编写自己实现的严格决策流程。大多数内部团队首先在统一代码仓库中寻找现成解决方案。
重复造轮子会造成信息异步:当一个开发人员离开时,他的自写组件会留下没有文档和支持。新团队成员必须处理非标准代码,浪费本可用于高效工作的时间。
最常见的示例——手动解析JSON或XML,尽管几乎所有现代语言都有内置工具。开发人员编写递归函数遍历对象树,却不知道JSON.parse()一行就能解决问题。
第二个示例——自己实现HTTP客户端。标准库(fetch、axios、OkHttp、URLSession)支持缓存、重连、超时和安全性。自写客户端通常至少忽略其中一个要求,导致生产中出现错误。
第三个示例——自写日志系统而不是使用SLF4J、Winston或Log4j。开发人员花费数周时间编写现成库开箱即用就能完成的工作,支持轮转、日志级别、异步写入和与监控系统的集成。
# 轮子 — 手动CSV解析
def parse_csv(line):
result = []
current = ""
for ch in line:
if ch == ",":
result.append(current)
current = ""
else:
current += ch
return result
# 改为使用标准库
import csv
with open("data.csv") as f:
reader = csv.reader(f)
编写自己的ORM(对象关系映射)——可能是最昂贵的轮子。像Hibernate、Entity Framework或SQLAlchemy这样的现成ORM经过多年开发,支持缓存、延迟加载、迁移和数十种数据库系统。自写ORM通常局限于一个数据库,并在连接管理方面包含严重错误。
学习——唯一的情况,重复造轮子不仅是合理的,而且是有益的。为教育目的编写自己的解析器、HTTP服务器或ORM有助于理解这些工具在底层是如何工作的。重要的是不要将教育项目与生产代码混淆:对个人项目好的东西在商业开发中是不可接受的。
独特需求确实可能需要自己的实现。如果没有库支持特定的协议、数据格式或硬件平台——创建定制解决方案是合理的。但首先要确保任务确实是独特的,而不仅仅是研究不充分。
许可限制——另一个正当理由。某些开源许可证(GPL、AGPL)可能与公司的商业模式不兼容。在这种情况下,使用更宽松的许可证开发自己的实现是合理的。
有一个实用规则:在编写自己的实现之前,尝试找到并测试三个不同的现成解决方案。如果都不适合——创建自己的,但要记录为什么现有方案被拒绝。这样可以防止无意识地重新发明轮子。
第一步——养成在任何典型任务开始之前搜索现成解决方案的习惯。使用包管理器、GitHub、Stack Overflow进行搜索。花在研究上的时间通过不编写自己的代码而多次回报。
第二步——实施专注于发现重复造轮子的代码审查。审查时问:「为什么我们不为此任务使用现成库?」如果答案不包含客观原因——这就是轮子。在大公司(Google、Meta),代码审查包括强制性的重新发明轮子检查。
第三步——创建内部知识库。记录项目中使用了哪些库和工具,它们解决哪些任务。新开发人员应该能够访问这些信息,以免因为不了解而重复造轮子。维护已做出的架构决策列表(ADR)并附上选择理由。
NIH综合症(Not Invented Here——「非我发明」)——一种反对使用外部解决方案的组织偏见。患有NIH综合症的公司更喜欢自己开发一切,拒绝开源库,即使它们优于自己的开发。这个综合症是轮子的企业版。
经典例子——Netscape在1990年代末,当时公司花费数年从头重写浏览器,而不是开发现有代码库。结果——市场份额损失并被AOL收购。相反,Android基于Linux内核构建,使用数千个开源组件——这使得产品在创纪录的时间内推向市场。
哈佛商业评论(2023)的研究表明,NIH综合症水平低的公司产品上市速度快40%,开发支出少30%。代码重用文化是现代软件开发中的竞争优势。
// 轮子 — 自定义排序实现
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
// 内置排序 — 标准解决方案
arr.sort((a, b) => a - b);
常见问题
定制解决方案是在现成库因客观原因不合适时创建的:许可、性能、兼容性。重复造轮子是现有方案的复制,没有客观原因。主要标准:你能用三个具体论据证明拒绝现成库的合理性吗?如果不能——这就是轮子。
最好的论据——数字:计算自写代码的维护成本(测试、文档、修复错误的工时)并与使用现成库进行比较。通常开发人员根本不知道库的存在。现场展示替代方案:导入库并调用方法对比数百行自己的代码。
非常罕见。在生产中,可靠性、安全性和可维护性很重要——只有通过社区多年的测试才能获得的品质。即使你的轮子现在能工作,它也没有经过数千种使用场景、边界情况和攻击的测试。例外——当任务确实没有现成解决方案时。
不。重复造轮子不是劣质库的唯一替代方案。寻找其他库,检查GitHub星标、更新频率、开放问题数量。如果所有库质量都很低——只有那时才考虑编写自己的实现。但从评估开始:也许你只是找错了库。
学习语言的生态系统:标准库、流行包、框架。阅读开源项目的代码——你会看到有经验的开发人员如何解决标准任务。在每个任务之前问自己:「这个问题在其他项目中是如何解决的?」更有经验的同事的代码审查是发现自己的轮子的最佳方式。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。