为什么“复制和粘贴”代码是危险的?

有时,我的老板会向我们抱怨:

为什么我们需要这么长的时间来实现一个特性?

实际上,这个特性已经在另一个应用程序中实现过了,您只需要从那里复制和粘贴代码。成本应该很低。

这确实是一个很难回答的问题,因为在我看来,复制和粘贴代码并不是一件简单的事情。

你有什么好的理由向你的非技术老板解释这一点吗?

19175 次浏览

复制和粘贴是一个等待发生的灾难。你的老板应该评估的价格运送早期相对于已经破碎的代码运送到最终用户很快的价格。

如果您在复制粘贴代码中发现了一个 bug,那么您将需要在每个地方修复它,并希望能够记住所有的 bug (这也适用于已更改的需求)。

如果将逻辑放在一个地方,那么在需要时更改就会更容易(因此,如果决定应用程序需要更新,那么只需在一个地方进行更新)。

让你的老板读一读 干燥原理(不要重复自己)。

您所描述的听起来像是 图书馆的完美用法,您可以在其中共享代码并只将其保存在一个位置。

我只会复制粘贴代码,如果我打算 重构它很快之后-确保我以后提取的公共代码,这样我可以重用尽可能多的逻辑。不久之后,我指的是几分钟几小时之后,而不是几天几周之后。

通过构建一个库,而不是通过复制和粘贴 收到的代码,你会远远优于 分享的代码。

相对于重写(查找 DRY) ,您仍然可以获得速度上的优势,但是只有一个地方可以维护代码。

如果您已经实现了这些特性,并且通过复制和粘贴 需要来重用它们,那么听起来您似乎做错了什么。你不能把这些特性放在一个库里,这样你就可以不用复制/粘贴就可以重用它们了吗?

显而易见的原因是你为将来背上了“债务”: 你需要在代码中做的任何改变(不仅仅是修正错误,任何改变)现在都要花费两倍的代价,因为你必须更新两个地方——而且风险更大,因为你最终会忘记其中一个地方。换句话说,现在让它工作得更快,将来会让你的工作更慢,而 可以是良好的商业意识,但通常不是。

但更重要的原因是,“这两者一样”的假设往往是错误的。无论何时,只要代码依赖于未说出的假设是正确的,将其复制到另一个位置就会导致错误,除非这些假设也适用于新的位置。因此,粘贴的代码通常从一开始就是错误的,而不仅仅是在下一次更改之后。

复制和粘贴代码通常会导致 巧合编程

我在一家类似的公司工作。作为一个实习生,我当时并不了解情况,所以当我开始一个新项目时,我的老板也建议我把代码粘贴到其他地方。好吧,正如您可能认为的,整个软件是相当混乱,直到一点,当您试图修复一个错误,两个新的错误出现。

从设计的角度来看,复制粘贴的代码无疑是一场灾难,可能会在未来造成很多问题。但是你问为什么它需要你大量的工作 就现在,答案是: 因为它从来不只是复制和粘贴。

如果原始代码是为了重用而编写的,作为一个相当独立的库,考虑到灵活性和客户端使用——那很好,但那不是复制粘贴,那是使用代码库。真正的代码复制粘贴通常是这样的:

  • “当然,我已经有完全可以做到这一点的代码了!”
  • “等等,这五个代码版本中哪个是我想用作源代码的版本?”
  • “嗯,所有这些‘ util _ func _ 023’函数是做什么的? 我不是记录了它们吗? 我现在需要它们中的哪一个?”
  • “哦,是的,这段代码使用代码基础 Y。我想我需要[ 选一个:将所有代码基础 Y 复制到我的新项目中/花一天时间从代码基础 Y 中解出我想要的一个函数/花一周时间从代码基础 Y 中解出我想要的一个函数]。”
  • “我复制了所有东西,耶!”
  • “为什么不起作用?”
  • 这就是您花费数小时/数天/数周调试与您想要的类似的现有代码的时候,而不是编写您实际想要开始的代码。

总之,不能直接使用的现有代码充其量只能作为编写类似代码的参考。它当然不能被整体提升,并期望在一个完全不同的系统中工作。一般来说,这是一个安全的假设,任何已经编写和完成的代码都应该尽可能少地被篡改——即使它是一个副本,而不是原始代码本身。

如果您希望将项目建立在复制粘贴的基础上,那么您必须以一种能够实现易于重用的方式编码 首先,即 没有复制原始代码并对其进行处理。这是值得做的,如果这是你的老板所期望的,那么你们两个首先都需要确保这是你们设计和工作的方式。

即使其他应用程序已经具有您需要的特性,如果不进行重大改写,该特性的代码可能根本不适合您当前的应用程序。这就像把福特的发动机装进丰田车里。一般来说,有一个经验法则,如果您必须修改超过25% 的代码复制,它是更好的(更便宜)从头开始重写。

将有问题的代码提取到库中听起来很有说服力,但是它可能比听起来更困难,这取决于其他系统是如何构建的。例如,该特性的代码可能很难提取,因为它以不干净的方式接口了许多其他代码(例如访问许多全局变量等)

你确定你的老板想听关于 DRY 原理,漏洞和其他技术方面的东西吗?

当你的老板或公司低估了完成某个项目所需的时间时,你经常会听到这样的评论。根据错误的估计,签订了一份合同,等等。在大多数情况下,程序员不参与评估。

为什么会这样?有时项目发起人的预算太少。也许您正在使用软件自动化的业务流程不值得您的团队努力。在这种情况下,管理者通常对坏消息非常封闭。在这个项目的开始有一种一厢情愿的想法。然后经理们试图责备程序员。在您的情况下间接通过复制和粘贴。在极端情况下,这被称为 死亡行军

告诉你的老板,每个变量名的部分都包含了旧项目的名称,现在你必须手动更改它们。如果你的老板不知道(或者想知道)为什么复制/粘贴是不好的,他/她可能会相信:)

我认为“ 另一份申请”是这里的关键,如果其他应用程序已经测试和使用,它应该 不会改变使用一个公共库,因此你不能与它共享代码。

同样的申请中,“复制和粘贴”是不好的,但是在不同团队开发的代码库之间,或者在不同的发布周期之间,“复制和粘贴”可能是最好的选择。

在我看来,你那不懂技术的老板最大的误解就是,你的工作主要是打字。他们认为减少打字可以节省很多时间。

我认为你能给这个人的最好的教育就是指出你所做的所有工作不是打字。即使大部分的工作通常是无形的,在你的头脑中,在你打字的同时。

当然,消除键入将节省一些时间。但是,那些大得多的、非打字的工作部分会变得更大,并且会消耗掉任何节省下来的时间和更多的时间。

他说得对,如果团队以前实现过类似的功能,那么第二次重复使用 很多会更容易。

但是,您可能应该解释每个应用程序是不同的。仅仅因为你在一个房子里安装了一扇门并不意味着你可以在 没有固定时间的另一个房子里安装另一扇门——你会因为安装门的经验而更快,但是你仍然需要时间来获得你的设备,安装门,确保它是铅垂的,然后把它拧进框架里。

是的,最大的问题是它不仅仅是复制和粘贴-它的复制然后粘贴然后稍微修改。

然后,当其中一个粘贴的变体出现问题时,就会对其进行更改。然后,另一个变体被改变。

然后,您会发现所有的变体都必须改变,因为原始副本有 bug。现在你是很好的,真正的螺丝,因为所有的粘贴区域现在是不一样的。

你难道不知道吗,这种蹩脚的编码通常几乎完全没有注释。

对我来说,不同之处在于,当您有多个代码副本执行相同的操作时,您所拥有的是一堆代码。当您只有一段代码来完成每个特定的任务时,那么您就有了一个系统。

系统的行为可以很容易地通过单点修改来改变——改变一组代码的行为需要一组代码。

我喜欢系统,而不是一堆代码。

在我的公司,我们总是使用类和方法,并为它们制作技术文档。我认为,如果您可以使用您自己的具有良好键的 svn 搜索应用程序来查找之前使用的方法类,那么这将是最佳实践:)

在您眼前即时功能的开发速度(特别是当应用程序很小时)和随着应用程序增长的长期维护成本之间存在权衡。

对于即时功能,复制和粘贴更快,但是随着应用程序规模的增长,在修复错误、进行系统范围的更改以及维护应用程序不同组件之间的工作流方面,复制和粘贴将使您付出高昂的代价。

这正是企业所有者需要听到的论点。它类似于维护一个车队的公认成本,然而,在软件方面,软件体系结构的破碎方面通常隐藏在业务方面,并且只能被开发人员看到。