代码复用的伪命题

代码复用是个伪命题

大多数人认为代码复用是一种理所当然的好实践,能减少重复工作,提高效率。然而,真正的实践中,代码复用往往是一种代价高昂的幻觉。

复用代码比写新代码更难

过度抽象的陷阱

代码的复用性意味着它必须足够抽象,以适用于不同场景,而过度抽象又容易导致代码复杂且难以维护。

上下文的负担

复用代码需要考虑原代码的上下文、依赖、边界条件,往往要花大量时间去理解和适配,比直接写一份更简单的实现更麻烦。

代码复用的本质是「依赖」

当你复用一段代码,你就在引入它的历史、局限性和演化路径:

  • 你复用了某个库,就依赖了它的 API 设计、生命周期、潜在 bug,以及它的更新节奏。
  • 你复用了自己或他人写的组件,就要接受它的架构和设计,哪怕它并不完全适合你的需求。
  • 你复用了通用工具函数,可能意味着你要处理它未覆盖的边界情况,甚至要承担别人留下的技术债。

长远来看,「可复用」的代码会比「一次性」代码更容易拖累项目发展,因为它受限于最初的设计,难以适应新的需求。

代码复用的终极矛盾:稳定 vs 变化

我们之所以想要复用代码,是因为希望「写一次,处处可用」,但现实是代码的复用性取决于代码的稳定性,而软件的本质是变化。

稳定代码的悖论

如果代码足够稳定(几乎不变),它可能适合复用,但它也可能已经被技术演进淘汰。

变化代码的悖论

如果代码需要经常修改(以适应需求变化),那么复用它只会增加维护成本,甚至阻碍演进。

矛盾点:真正可复用的代码,通常已经不再需要被修改;而需要不断适配和修改的代码,往往最不适合被复用。

代码复用的本质是"共性"的幻觉

当我们试图让代码可复用时,我们通常会寻找不同场景中的"共性",但共性往往只是表象的相似,而非本质的相同:

你看到两个功能都需要日期格式化,于是你写了一个「通用的日期工具函数」,但很快,你发现不同的业务有不同的时区、格式、语言要求,最终工具函数越改越复杂,甚至变得难以维护。

你发现 A 和 B 页面都要显示用户信息,于是你提取了一个「通用的用户组件」,但后来 A 需要显示 VIP 图标,B 需要隐藏部分信息,这个组件的「复用性」变成了一种负担。

表面的相似,并不意味着长期的共性,而代码复用的代价是:你被过去的共性绑架,忽略了未来的变化。

代码复用是对「不确定性」的逃避

混沌中的秩序幻觉

人们追求代码复用,不是真的为了减少代码量,而是为了在不确定的世界里创造确定性。

错误的假设

  • 「如果我们复用代码,就能避免重复劳动。」
  • 「如果我们设计出可复用的架构,以后改动就会更轻松。」
  • 「如果我们有一个统一的组件库,就不会有人重复造轮子。」

这些想法基于一个错误的假设:未来是可预测的,变化是有限的。

代码复用的悖论:你写的代码越通用,它越难被复用

人们总是试图写「更通用的代码」,希望它能适用于更多场景,但真正的情况是:代码的通用性和实用性是对立的。

一个完全通用的 UI 组件,往往会变成一个庞大的、难以定制的怪物。

一个足够灵活的 API,最终会成为所有人都需要「hack」的工具,而不是直接可用的解决方案。

一个「能做所有事情」的库,往往谁都不愿意用,因为它的学习成本比自己写一个还高。

最好的代码,不是「通用」的,而是「恰好满足某个特定需求的」

代码复用的真正代价:演进的失控

当你复用代码时,你不是在减少工作量,而是在把未来的变化成本集中到一块代码上:

你以为你减少了代码量,但其实你增加了未来维护的难度。

你以为你创造了复用性,但其实你创造了「依赖」,依赖带来的锁定成本远高于「复制 + 修改」的成本。

你以为你让代码变得「抽象」了,但抽象的代价是:任何具体的需求变化,都会反向影响所有依赖者,最终导致整个系统僵化。

最典型的例子是:

React 组件库

你试图复用一个「通用的 Button 组件」,但不同的团队有不同的需求(样式、交互、动画、可访问性)。最终,这个「通用组件」被 patch 了一堆 props,变得比任何特定的实现都更难用。

微服务架构

你想让多个团队共享用户系统,于是把用户服务拆成一个独立的微服务。但后来你发现,每个团队对用户信息的需求都不一样,最终你不是在复用,而是在不断「修补」这个公共服务,让它既满足 A 的需求,也适应 B 的变化,最后变成了所有人的负担。

代码复用是对「不可复制性」的误解

代码的本质:每一行代码都属于它诞生的环境

代码从来都不是独立存在的,它诞生于特定的业务需求、团队文化、技术栈、历史背景,就像生物适应特定的生态环境一样。

你写的代码,隐含了当时的权衡和妥协。

你可能为了性能牺牲了可读性,为了快速交付牺牲了扩展性。

你可能为了性能牺牲了可读性,为了快速交付牺牲了扩展性。

这些权衡是无形的,但它们深深嵌入了代码的结构中。

当你试图复用一段代码时,你是在试图在一个不同的环境里强行适应它。

就像把热带雨林的植物搬到沙漠,它可能存活,但它的「最优适应性」已经消失了。

代码在新的环境下,不可避免地需要改动,最终变成了不是复用,而是迁移。

悖论:真正能够无痛复用的代码,往往已经失去了它的原生优势。

你从一个业务系统里提取了「通用的权限管理模块」,但在另一个系统里,它的「通用性」意味着它无法适应新的业务逻辑,最终不得不被大幅改动。

你复用了一个「通用的 API 封装层」,但新的团队用的技术栈不同,这些封装反而成了负担。

你复用了一个「前端组件库」,但它的设计理念、交互习惯、状态管理方式,都和你的新项目不兼容,最终你不得不改得面目全非。

代码的价值,不仅仅是代码本身,而是它所适应的上下文。而上下文是不可复用的。

代码复用是对「复制 vs 继承」的误解

在自然界,最有效的信息传递方式,不是克隆(Copy),而是进化(Evolve)。

复制(Copy)

直接拿来用,但环境不同,它未必适配。

继承(Inherit)

继承思想,然后在新环境下调整。

代码复用的最大误区

以为复制代码比重新写更有效率。

真正有价值的代码复用

不是直接用,而是「理解它为什么这么设计,然后重新实现一个适合当前环境的版本」。

真正的代码复用,不是「减少代码量」,而是「减少思考成本」。

真正应该复用的,是思维方式,而不是代码本身。

  • 你不应该复用某个 API 的实现,而应该复用「如何设计一个清晰的 API」的思考方式。
  • 你不应该复用某个组件,而应该复用「如何构建一个可维护的 UI 体系」的理念。
  • 你不应该复用某个工具,而应该复用「如何让代码更容易替换」的架构哲学。

代码复用的问题是,它让人们关注于「复制」,而忽略了「进化」。

代码本质上是一次性解决方案

代码的本质:所有代码都是「一次性的」

我们普遍认为代码是可以被「复用」的,就像是工厂生产的零件,能被不同的机器重复使用。但代码和物理世界的组件不同,它更像是一种「即兴演奏」,只能在特定的时空环境下奏响。

为什么?因为代码是对问题的映射,而问题本身从来都不会一模一样。

代码是问题的「临时表达」。
代码只是在特定时间、特定需求、特定团队背景下,对某个问题的一次性表达。它并不具备普适性。

需求在变,环境在变,代码本身的意义也在变。
你今天复用的代码,明天可能因为业务逻辑、架构调整、团队文化的变化,完全不再适用。

所以,代码的宿命从来不是「复用」,而是「过期」。

复用的悖论:真正被「复用」的代码,往往已经失去了灵魂

我们经常试图写「可复用的代码」,但真正的问题是:

代码越通用,表达力就越弱。

代码越抽象,可维护性就越差。

真正有价值的代码,只适用于它诞生的那一刻。

一个真正适用于所有场景的函数,往往无法精准描述任何一个具体场景。

你为了让它更灵活,增加了一堆参数,结果它变得无比复杂,适用性反而下降。

你以为你在构建一个「万能框架」,但实际上,你只是在创造一个「永远适配不了所有需求的桎梏」。

你以为你写了一段可以「长期复用」的代码,但现实是,它只在你当前的项目里有意义。

真正被复用的代码,往往已经被「削弱」到只剩下最通用的部分,丢失了它最初的价值。

代码复用的终极悖论:你在逃避什么?

所有代码复用的尝试,最终都指向一个深层次的心理动机:人们害怕面对变化。

  • 「我们应该写出可复用的代码,这样以后就不用改了。」

  • 「我们应该用一套通用架构,这样以后就不会出问题。」

  • 「我们应该设计一个完美的 API,这样所有人都能用它。」

但所有这些想法的背后,是对「变化」的恐惧。

代码复用的终极悖论是:复用不是为了减少工作量,而是为了减少对未来变化的焦虑。

但真正的问题是:软件开发的本质就是变化,所有代码迟早都会被推翻。

你以为你写的代码可以长期复用,但现实是,你只是延后了它的死亡时间。

终极结论

代码复用是个伪命题,真正的问题是:如何让代码更容易被替换?

最好的代码是什么样的?

  • 短命但高效

  • 容易被替换,而不是容易被复用

  • 复用的不是代码,而是思维方式

真正值得复用的是什么?

  • 思维模式,而不是代码本身

  • 架构哲学,而不是具体实现

  • 解决问题的方法,而不是代码片段

代码不是资产,而是负债。
最好的代码,不是可复用的,而是可抛弃的。