您如何安全地删除看起来从未输入过的一段代码?


125

您已经找到了一些看起来多余的代码,而编译器没有注意到这一点。您要做什么以确保(或尽可能接近地确保)删除此代码不会导致回归。

想到两个主意。

  1. 根据代码是否看起来应该执行来“简单地”使用推论。但是,有时这可能是一项复杂,耗时的工作,并且风险很小(您的评估容易出错),并且没有实质性的业务回报。

  2. 将日志记录在该代码部分中,并查看实际输入的频率。执行完足够的代码后,您应该有足够的信心删除代码是安全的。

是否有更好的主意或类似的规范方法?


55
查看版本控制历史记录可能会有所帮助:什么时候创建了“死代码”?它创建时看起来死了吗?是否在创建代码的同时检查了使用它的其他代码(此后已被修改或删除)?
ChrisW

9
如果正在进行任何形式的元编程,例如反射,则编译器将不受信任。基本目录的正确grep是必不可少的做法。多个应用程序共享代码也是很常见的,因此请确保在正确的目录层进行grep。
亚当·卡维尼斯

16
我会问-“为什么要删除它?”。如果用在某些被称为蓝月亮的边缘情况下,则说明您已经弄坏了东西。如果从未使用过而您将其保留在那里,则会造成可执行文件的损坏。除非这是针对某些嵌入式设备的,否则这有什么大不了的?在上面留下评论,然后继续有效地利用您的时间。
mickeyf

40
@mickeyf损害在于某人必须维护该代码。这意味着开发人员必须了解其功能。如果情况发生变化,开发人员必须对其进行修改,并且围绕它的代码现在必须做一些不同的事情。相信我,当您无所事事时,这对于以后尝试解决问题的任何人来说都是一个头疼的问题。
17

9
@MichaelJ。我的观点是,它本来不应该在那里。如果我不这样做,情况会更糟:现在,其他一些开发人员也必须对其进行调查。它停留的时间越长,花在理解它上的时间就越多。这就是多余代码的成本。
jpmc26,2017年

Answers:


112

在我拥有100%单元测试覆盖率的完美幻想世界中,我只是删除它,运行我的单元测试,并且当没有任何测试变为红色时,我将其提交。

但是不幸的是,我必须每天早晨醒来,面对这样一个残酷的现实:许多代码要么没有单元测试,要么没有代码可以覆盖所有可能的边缘情况。因此,我将考虑风险/回报,并得出结论,这样做根本不值得:

  • 奖励:将来代码更容易维护。
  • 风险:在我没想到的模糊边缘情况下破坏代码,在我最不希望的情况下引发事件,并为此承担责任,因为我必须满足我的代码质量OCD并进行更改,而没有可察觉的商业价值给任何不是编码员的利益相关者。

249
在观察大型软件项目的状态时,工程师在十到十二年间大多遵循“不值得”的方法来删除可能多余的代码,因此,我推荐这种方法。
njuffa

125
而且,单元测试不会告诉您有关此代码是否实际用于生产中的任何信息。该代码可以进行完美的测试,但仍未在生产中使用,因此是多余的。
knub

8
以我的经验,此类项目的状态从来都不是由于事后使用“不值得”的方法来修复代码,而总是因为首先编写了错误的代码。编写错误的代码是一个错误,但是认为清理代码总是值得的,这也是一个错误(更高级的错误)。
Weyland Yutani

42
@Weyland Yutani根本不符合我的经验。考虑到十年前已经存在的软件规格,硬件平台或操作系统版本,以及解决工具链中的错误或局限性或较早可用的硬件等,我看到的大多数可能多余的代码都编写得足够好,并且完全合理。 。
njuffa

20
答案错过了继续进行变革的最关键和最重要的好处之一:学习一件事。了解了它之后,您可以添加结构(注释,测试)以帮助以后的维护开发人员。因为您不知道后果而选择不更改某些内容是“迷恋”编程,即使该选择不是删除某些内容。
kojiro

84

此过程分为两半。第一个是确认代码确实已死。第二个是理解错误的代价,并确保它们得到了适当的减轻。

这里的许多答案对前半部分都有很好的解决方案。诸如静态分析器之类的工具非常适合识别死代码。 grep在某些情况下可以成为您的朋友。我经常采取的一个不寻常的步骤是尝试确定代码的原始用途。争论“ X不再是我们产品的功能,并且代码段Y旨在支持功能X”要比说“我看不到代码段Y有任何目的”要容易得多。

后半部分是打破是否应该删除代码的僵局的关键步骤。您需要了解错误答案的含义。如果您因错误的答案而要死掉,请注意!也许是时候接受代码随时间推移而发展的好时机,而不是尝试自己不要编写更多的东西。如果人们不会死,请问问自己自己用户的宽容程度。如果您破坏了某些东西并保持了客户关系,是否可以向他们发送修复程序?您是否有付费的问答团队来查找此类问题?这些类型的问题对于理解在按下Delete键之前必须具有的确定性至关重要。

在评论中,rmun指出了删除代码之前理解代码原始目的这一概念的绝佳措辞。现在的报价为切斯特顿篱笆。虽然太大了,无法在评论中直接引用,但我认为应该在此处正确引用:

在改造事物时,不同于使事物变形,有一个简单明了的原则;可能被称为悖论的原理 在这种情况下,存在某种制度或法律;为了简单起见,我们可以说是在道路上竖起的栅栏或大门。较现代的重整器对此轻描淡写地说道:“我看不到有这种用途。让我们清除它。”哪一种更聪明的重整器将很好地回答:“如果您看不到它的用途,我当然不会让您清除它。走开思考。然后,当您可以回来告诉我您确实看到它的用途时,我可以允许您销毁它。


14
切斯特顿的栅栏假定发现纯粹是通过思考而来的,并且不提供任何追索权,而是一种古希腊哲学。科学方法更现代-设计受控实验来确定移除结构的后果。可以肯定的是,我不会毫不犹豫地建议在生产中进行此实验。但是,如果没有其他选择,并且在生产中进行试用的成本很高,那么,我宁愿很快离开一个工作场所,该场所并没有提供安全的方法来学习为什么存在某些法规-这样工作的个人风险过高。
kojiro

15
@kojiro这是真的。我想认为切斯特顿会没事的,如果我作为“现代改革家”来过,并说:“我不知道为什么这篱笆在这里,我承认。但是我想将其涂成红色以查看是否为我提供了任何新信息,”切斯特顿可能会对此感到满意。
Cort Ammon

41
VMS OS内核模式时钟解码方法SYS $ ASCTIM包含一行代码,可帮助校正春分的恒星漂移。DEC的任何单元测试都不执行此代码。它在2000-02-28 23:59:59:999上运行了一次-正确。直到2400年它才会再次运行。请不要删除它。
AI Breveleri

13
@AIBreveleri我希望代码本身有注释来解释这一点,而不仅仅是在StackExchange:D
Kyle Strand

11
@KyleStrand您在说什么?这文档。您能想到比StackExchange更好的放置重要信息以便其他人找到的地方吗?;-)
Cort Ammon

43

我也倾向于grep使用代码中的函数/类名称,这可以提供代码分析器可能没有的其他好处,例如,如果在注释,文档文件或脚本中提到了该名称。我在源代码树中的文件上运行grep并将结果存储在文件中;通常,结果会给出一个简短的信息:文件名/路径,行号以及遇到该名称的行,这可以提供线索,指出在没有任何语义的情况下调用或提及函数/类的位置(与代码分析器相反) ),并且不考虑文件扩展名。绝对不是最终的解决方案,而是对分析的一个很好的补充。


11
我不是一个拒绝者,但是如果您怀疑从未运行的代码段不是完整的函数或类,而是方法/函数的一部分,该怎么办?
图兰斯·科尔多瓦

9
取决于语言,这可能并不总是可靠的-有些语言允许动态地进行方法调用。例如php:$object->$variableMethod($arg1, $arg2);
Dezza

9
@TulainsCórdova显然,这可能不是一个好的解决方案。对于每个潜在的解决方案,如果在给定的上下文中有意义,请使用它。但是也许grep。包含代码部分的函数可以提供有关如何使用函数以及其内容的线索?
piwi

7
“就像在注释,文档文件或脚本中提到名称一样”-或有人正在使用反射来调用该方法(如果使用高级/ OO语言)。尽管仍不一定100%准确,但由于某些语言支持某些非常钝的查找和调用方法的方式。
aroth

如果您可以访问所有可能使用该功能的代码,这将很有帮助。但是,如果代码是库的一部分,您将如何知道其他无法搜索的内容不会中断?
凯文·谢伊

30
  • 识别看起来无效的代码(静态分析等)。
  • 对于据称无效代码的每次调用都添加一条日志消息。函数/方法很容易;对于像常量这样的静态成员,则比较棘手。有时您可以仅将代码标记为已弃用,运行时将自动为您记录一条消息。保留其他代码不变。
    • 在加载死模块时记录一条消息:大多数语言都有一种在加载时运行静态初始化的方式,该方式可以背负。
    • 确保你的日志消息包括合理的堆栈跟踪,让你明白什么叫据称死代码。
  • 在所有测试套件中运行更改后的代码。您的测试套件还应该测试特殊的时间,例如穿越午夜,四分之一圈,一年之类的时间。查看日志,更新对已死者的了解。请注意,单元测试可以专门测试无效代码,而其他任何单元测试和集成测试都不能。
  • 在生产环境中运行更改后的代码几周。确保在此期间运行每个定期过程,例如每月一次的ETL cron作业。
  • 查看日志。记录的任何内容实际上并没有死亡。即使没有被调用,调用图在其余无效代码上的传递性关闭可能不会失效。分析一下。也许有些分支已经安全地死掉了(例如,使用现已灭绝的服务的API),而有些分支还没有死。也许只为其中定义的常量加载整个模块/类,您就可以轻松使用它。
  • 剩下的一切都安全地死了,可以将其删除。

一切都是第一次,所以仅仅因为代码之前没有运行并不意味着它永远不会运行,特别是如果有执行路径的话。也就是说,决定删除可以执行的操作与确定它是否可以运行是不同的过程。

7
@nocomprende确切地讲,如果此代码仅用于年末功能并且您运行测试的时间是2月-11月。即使将其剖析了将近整整一年,您也找不到用处。
Erik

1
@ 9000我在理论上同意,但是旧系统充满了隐藏的依赖关系,这些依赖关系可以挫败这种测试。为了使测试示例正常工作,您需要知道存在某种时间耦合。如果时间耦合在您要测试的代码外部,那么您就不会在代码中看到它并且知道时间耦合是一个因素。例如,筒仓A的代码已安装在GAC中。多年前,筒仓B要求筒仓A为他们需要针对筒仓A的数据运行的报告添加代码路径。续
艾瑞克(Erik)

1
@ 9000续 现在,筒仓B的代码在时间上与筒仓A的代码中的“死”代码路径相关,而仅通过查看筒仓A的代码就看不出来。由于时间耦合仅在筒仓B的代码中而不知道与筒仓B对话,您将如何对该时间耦合进行单元测试?即使这样,时间也不会成为您的代码(Silo A)中的因素,那么时间测试会是什么样?
艾瑞克(Erik)

2
有人记得Y2k恐慌吗?有些代码在2000年是正确的,因为2100年是错误的!我们有一个潜在问题,可能每100年发生一次,甚至每400年发生一次。
史蒂夫·巴恩斯

16

除了提到的现有答案外,您还可以迭代地删除多个版本的代码。

在初始版本中,您可以在代码块仍起作用的情况下发出弃用警告。在此后的版本中,您可以删除代码块,但会留下一条错误消息,使用户不赞成使用该功能,并且该功能不再可用。在最终版本中,您将删除代码块和所有消息。

这可能有助于识别不可预见的功能,而不会警告最终用户。在最好的情况下,该代码实际上什么也不做,并且所发生的一切只是在删除之前不需要的代码保留了多个版本。


7
+1。我发现,如果开发人员(或其管理人员)愿意将一个项目分为两个或三个安全阶段,而不是试图一次投入所有费用来满足一个问题,那么可以避免许多错误和面向客户的问题。在进入下一个项目之前的任意截止日期。
ruakh

这可以回答标题中的问题,但是如果您问我,问题的标题是错误的。标题询问如何删除代码,但是正文是关于如何首先识别是否可以安全删除代码的内容。您肯定会回答前者,但我不确定这是OP真正要问的。
underscore_d

7

许多人建议“安全”的事情是在无法证明未使用代码的情况下保留代码。

但是代码不是资产,而是责任。

除非您能解释为什么它很重要并指出其测试,否则“更安全”的选择可能就是删除它。

如果仍然不确定,则至少确保添加测试以执行僵尸代码。


步骤1:删除所有代码。步骤2:放回所需的内容。步骤3:重复。

1
@Adrian我可以引起您对Knight Capital的注意吗?en.wikipedia.org/wiki/…-以防万一您想引用它来支持您的观点。他们之所以发生内爆,是因为一些旧的代码重获新生,并为他们损失了近5亿美元。随后的询问集中在他们的发布程序上,但核心问题是“未删除失效的功能”。
SusanW

6

您可以使用功能切换来更改软件的执行路径,以完全忽略相关代码。

这样,您可以在不使用代码的情况下安全地部署您的更改,然后将其关闭。如果您发现一些与代码有关的主要错误,请重新打开切换开关,并调查可能的代码路径。

如果您在长时间内未发现任何问题,并且可以通过实时部署将其重新启用,则此方法应该使您充满信心。但是,更好的方法是在相关区域周围应用额外的日志记录和测试覆盖范围,这将为是否使用该区域提供更多证据。

在此处阅读有关切换的更多信息:https : //martinfowler.com/articles/feature-toggles.html


6

当然可以进行静态分析……而且,好处是,您不需要任何新工具。您的编译器具有所需的所有静态分析工具。

只需更改方法的名称(例如更改DoSomethingDoSomethingX),然后运行构建即可。

如果构建成功,则该方法显然不会被任何人使用。安全删除。

如果构建失败,请隔离调用它的代码行,并检查if该调用周围有哪些语句以确定如何触发它。如果找不到任何触发它的数据用例,则可以安全地将其删除。

如果您真的担心删除它,请考虑保留代码,但使用ObsoleteAttribute(或与您的语言等效的标记)对其进行标记。像这样发布一个版本,然后在没有任何问题的情况下删除代码。


8
对于没有完全动态/后期绑定的编程语言,这是不错的建议。但是,对于那些拥有此功能的用户,则比较棘手。而且,当然-即使从未在生产中使用过,代码也可能正在使用该方法。
eis

这个问题的前提是编译时符号解析(因此,您发现了一些看起来多余的代码。编译器没有注意到这一点。)。甚至后期绑定的函数或对象通常都在两个位置定义,例如接口或映射文件,因此构建仍将失败。
约翰·吴

1
嗯...我很感兴趣,我不认为你说的是​​真的。例如,它不适用于香草javascript(未预编译),ruby,python或smalltalk,对吗?如果您引用的内容不存在,则这些错误仅在运行时出现。
eis

也许我们不同意“绑定”的含义,对我而言,“绑定”表示逻辑表示对物理表示的解析,例如地址的符号。Javascript函数以标记/值对的形式存储在包含的对象中,因此任何绑定概念似乎都不是必需的。
约翰·吴

1
是的,我们真的不同意。该术语在没有完全后期绑定的语言上具有不同的含义(如我在第一条评论中所述)。当您在运行时绑定方法时(使用可以在运行时添加和删除方法的语言进行即时绑定),您将按照我理解的方式使用后期绑定。这在ruby / python / smalltalk风格的语言中是正确的,并且那里没有单独的地图文件。由于特别是ruby和python的使用非常广泛,因此我不同意您的观点,即“通常”是什么意思。
eis

4
  • 我将开始使用代码分析器来检查这是否为无效代码
  • 我将开始检查测试并尝试获取代码。如果我无法在测试用例中访问代码,则可能是无效代码
  • 我将删除代码并引发异常。对于一个版本,异常被激活,而在第二版本中,异常可以被删除。为了安全起见,当客户注意到异常时,放置一个紧急标志(在Java中是系统属性),以便能够激活原始代码。因此可以禁用该异常,并可以在生产环境中激活原始代码。

6
-1。绝对不建议在生产性代码或安全标志中进行这些更改。仅当确定不使用所讨论的代码时,才应将其从生产性代码中删除。正是这种混乱,是为什么总要有不同的生产和测试环境的原因。
约翰内斯·哈恩

3
当测试覆盖率接近90%时,这将在幻想中起作用,但是如果测试覆盖率接近90%,那么您的项目有100.000行代码。那就是为什么我描述了尝试测试代码的原因,如果没有测试能够达到该代码……它可能已经死了。
马库斯·劳斯贝格

恕我直言,如果您不确定是否要完全删除代码-您不应该在生产中引发异常-OP建议的日志记录是更好的选择,可以实现,并且可以进行诊断,而不必依靠客户抱怨
Sebi

@JohannesHahn我想您的意思是“生产代码”,它是在生产环境中运行的代码。
17

@ jpmc26是的,当然。
约翰内斯·哈恩

3

删除无法访问的代码

在有原则的静态类型语言中,您应该始终知道代码实际上是否可以访问:将其删除,编译,如果没有错误,则无法访问。

不幸的是,并非所有的语言都是静态类型的,并且并非所有的静态类型语言都是有原则的。可能出错的事情包括(1)反射和(2)无原则的重载。

如果您使用动态语言或具有足够强大的反射能力的语言,使得可以在运行时通过反射来访问受审查的代码段,那么您就不能依赖编译器。这些语言包括Python,Ruby或Java。

如果您使用的语言没有意想不到的重载,那么仅删除重载就可以简单地将重载分辨率切换为另一个重载。某些这样的语言允许您编写与代码使用相关的编译时警告/错误,否则您将不能依赖编译器。这样的语言包括Java(使用@Deprecated)或C ++(使用[[deprecated]]= delete)。

因此,除非您非常幸运能够使用严格的语言(想到Rust),否则您可能真的会信任编译器,从而陷入困境。不幸的是,测试套件通常不完整,因此也没有太多帮助。

提示下一节...


删除可能未使用的代码

实际上,该代码实际上是被引用的,但是您怀疑实际上引用该代码的分支从未被采用。

在这种情况下,无论使用哪种语言,都可以证明代码是可访问的,并且只能使用运行时工具。

过去,我已经成功地使用了3个阶段的方法来删除此类代码:

  1. 在怀疑不采取的每个分支上,记录警告。
  2. 一个周期后,在输入特定代码段时引发异常/返回错误。
  3. 在另一个周期之后,删除代码。

什么周期?这是代码使用的周期。例如,对于财务应用程序,我希望每月周期较短(月底支付工资),而每年周期较长。在这种情况下,您必须等待至少一年,以验证没有发出任何警告,因为年终清单可以使用否则不会使用的代码路径。

希望大多数应用程序的周期较短。

我建议在TODO注释中加上日期,以建议何时进行下一步。以及日历中的提醒。


1
实际上,在C ++中,您可以标记可疑的超载,[[deprecated]]以标识调用该版本的站点。那么您可以检查它们,看看它们的行为是否会改变。或者,将重载定义为= delete-在这种情况下,您将需要显式转换参数以使用其他版本之一。
Toby Speight

@TobySpeight:是的,这也是一个很好的选择。让我编辑英寸
马修M.

“医生,我这样做会很痛。” “ 不要这样做! ”不要使用会使代码难以管理的方法。

2

从生产中删除代码就像打扫房子。当您从阁楼上扔掉一个物体时,您的妻子第二天就会杀死您,原因是她从1923年去世的邻居那里丢掉了曾祖母的第三位侄子的返乡礼物。

认真地说,在使用每个人都提到过的各种工具进行粗略分析之后,再使用已经提到的逐步弃用方法之后,请记住,在实际删除工作时,您可能会离开公司。让代码保留下来并记录其执行,并确保将有关其执行的任何警报明确地传达给您(或您的后继者和分配者)是至关重要的。

如果您不遵循这种方法,很可能会被妻子杀死。如果您保留代码(就像每一份纪念品一样),那么您可以放心,因为摩尔定律对您有所帮助,并且代码所使用的垃圾磁盘空间的成本感觉就像“一块石头砸了我”。年,您不必冒险成为多个饮水机八卦的中心,而在走廊上看起来也很怪异。

PS:为回应评论而澄清。.最初的问题是“您如何安全删除..”当然,我的回答中假设使用了版本控制。就像在垃圾桶中寻找有价值的东西一样。任何机构都不应抛弃代码,而版本控制也不应该成为每个开发人员的严格要求。

问题在于可能多余的代码。除非我们可以保证100%的执行路径都不会到达,否则我们永远无法知道一段代码是多余的。并且假定这是一个足够大的软件,因此几乎无法保证。除非我读错了这个问题,否则整个对话的整个过程只有在生产期间可能会调用被删除的代码的情况下才有意义,因此我们遇到了运行时/生产问题。版本控制不会因生产故障而挽救任何人,因此关于“版本控制”的评论与问题或我的初衷无关,即,如果确实需要,则在非常长的时间内适当弃用,否则不要

恕我直言,该评论是多余的,可以删除。


12
如果我拥有等效的版本控制功能,我的妻子的反对意见将很容易被撤销。删除代码也是如此:这不是致命的罪行。该存储库永远不会被丢弃。
JDługosz

我认为面向对象的编程应该缩小可以调用方法的范围。因此,它应该导致更易于理解和管理的代码,对吗?否则,我们为什么要这样做呢?

@nocomprende在该线程中没有任何地方表示该讨论仅限于OOP设计/语言。我不确定您为什么认为这很重要。
underscore_d

1

也许编译器确实注意到了,只是没有大惊小怪。

使用针对大小的完整编译器优化来运行构建。然后删除可疑代码,然后再次运行构建。

比较二进制文件。如果它们相同,则编译器已经注意到并默默地删除了代码。您可以安全地从源中删除它。

如果二进制文件不同...则没有定论。可能还有其他变化。一些编译器甚至在二进制文件中包含了编译日期和时间(也许可以将其配置掉!)


1
并非所有语言都经过编译,并非所有编译器都具有优化功能,并且并非所有优化都相等,许多语言不会删除未调用的代码,以防万一有某种原因存在该代码。如果代码位于共享库/ DLL中,则不会删除它。
史蒂夫·巴恩斯

1
@SteveBarnes好吧,如果您不执行LTO并静态链接所有内容,那么您会有些痛苦。那是给定的。
纳文

1

实际上,我最近使用一种称为“ deleteFoo”的方法遇到了这种确切情况。在整个代码库中,除了方法声明之外,找不到该字符串,但是我明智地在方法顶部写了一条日志行。

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

原来,代码已被使用!某些AJAX方法调用“ method:delete,elem = Foo”,然后将其连接并使用call_user_func_array()进行调用。

如有疑问,请登录!如果花了足够的时间而看不到日志已满,请考虑删除代码。但是,即使您删除了代码,也请保留日志和带日期的注释,以使需要维护它的人更容易在Git中查找提交!


有一些PHP和Perl的库,称为Tombstone(php:github.com/scheb/tombstone),在这里可能会有用。
Alister Bulman'3

0

使用grep或首先使用可用的任何工具,您可能会找到对代码的引用。(最初不是我的指示/建议。)

然后决定是否要冒删除代码的风险,或者是否可以使用我的建议:

您可以修改功能/代码块以将其用于日志文件。如果在日志文件中没有“记录过”这样的消息,那么您可以删除日志记录代码。

两个问题:

  1. 从其他用户获取日志。 也许您可以设置一个全局标志,该标志将在退出时使程序崩溃,并请用户向您发送错误日志。

  2. 跟踪呼叫者。 在某些高级语言(例如Python)中,您可以轻松地获得追溯。但是,在编译语言中,您必须将返回地址保存到日志中,并在退出时强制进行核心转储(第1点)。
    在C语言中,这应该非常简单:使用条件块(例如#ifdef ... #endif)仅在知道它会工作(并已经测试过)时才使用它,并只需读取返回地址从堆栈中(可能需要也可能不需要内联汇编语言)。

将参数保存在日志文件中可能有用也可能没有用。


0

如果您知道它周围的代码是做什么的,请对其进行测试以查看其是否损坏。这是重构您正在处理的代码的最简单,最具成本效益的方法,无论如何,首先应该对正在处理的代码进行任何更改,都应该这样做。

另一方面,如果您在不知道代码做什么的情况下重构了一段代码,请不要删除它。首先了解它,然后在您确定它安全的前提下对其进行重构(并且仅当您可以确定重构将适当地利用您的时间时)。

现在,在某些情况下(我知道我自己也遇到了这种情况),“死”的代码实际上没有连接任何东西。例如由承包商开发的代码库,这些代码库实际上从未在任何地方实现,因为它们在应用交付后立即变得过时了(为什么是的,我确实有一个具体的例子,您怎么知道?)。

我本人确实删除了所有这些代码,但是我不建议轻描淡写,这是一个巨大的风险-取决于此更改可能影响多少代码(因为您始终有可能忽略了某些东西)应该格外小心,并进行积极的单元测试,以确定删除此古老的旧代码是否会破坏您的应用程序。

现在所有这些都说,尝试删除这样的代码可能值得您花费时间或理智。除非它已经成为您的应用程序中的一个严重问题(例如,由于应用程序膨胀导致无法完成安全扫描...为什么是,我仍然想着一个例子),否则,除非您达到目标,否则我不建议您这样做这种情况下,代码确实开始以一种有影响的方式腐烂了。

简而言之-如果您知道代码的作用,请先对其进行测试。如果您不这样做,则可以不理会它。如果您知道自己不能孤单,请花时间在实施更改之前积极测试更改。


0

我的方法在这种情况下一直被认为是行业标准,但迄今为止却没有被提及:

再换一双眼睛。

有不同类型的“未使用的代码”,在某些情况下,删除是适当的,在其他情况下,您应该对其进行重构,而在其他情况下,则尽可能地逃避并且永远不要回头。您需要弄清楚是哪一个,并且这样做最好与经验丰富的第二个配对!-开发人员,非常熟悉代码库。这显着降低了发生不必要错误的风险,并允许您进行必要的改进以将代码库保持在可维护的状态。而且,如果您不这样做,那么在某些时候您将耗尽对代码库非常熟悉的开发人员。

我也已经看到了日志记录方法-更确切地说,我已经看到了日志记录代码:还有更多死代码被保留了10年。如果您正在谈论删除整个功能,那么日志记录方法是相当不错的,但是如果您只是删除一小部分无效代码,则不是。


0

您问题的简单答案是,您无法安全删除不了解的代码,包括不了解如何调用它。会有风险。

您将不得不判断如何最好地减轻这种不可避免的风险。其他人提到了日志记录。日志记录当然可以证明它已被使用,但不能证明它没有被使用。由于无效代码的主要问题在于它得到了维护,因此您也许可以添加注释说它是可疑的无效代码,而不做任何维护。

如果代码在源代码控制下,则始终可以查明何时添加了代码,然后确定如何调用它。

最终,您要么了解它,要么不理会它,否则就抓住机会。


0

如果相关代码的开发人员仍然可用,请与他一起检查删除代码。另外,请阅读代码中的注释

您将得到正确的解释,为什么还要添加此代码以及该代码的作用。它可以轻松地支持特定的用例,或者您甚至可能没有足够的专业知识来判断是否真的不需要它。在极端情况下,可能会从C程序中删除所有空闲语句,而不会发现任何错误(可能需要几分钟才能用完内存)。

当代码的作者坐在您旁边时,这确实是一种糟糕的文化,但是您看不出要说话的原因,因为您确定他只是编写了不好的代码,而且您的代码是如此完美。而且,当然,他只是在评论中随意写一些单词,而无需浪费时间阅读。

在代码中写注释的最重要原因之一就是解释为什么它是通过这种方式实现的。您可能不同意该解决方案,但是您需要考虑该问题。

与作者的讨论也可能表明该代码是已删除或从未完成的内容的历史遗留物,因此可以安全删除。


-1

我非常同意Markus Lausberg的第一点:绝对应该借助工具来分析代码。猿人的大脑不能被这种任务所信赖!

大多数标准编程语言都可以在IDE中使用,并且值得调用的每个IDE都具有“针对所有用途的查找”功能,而这正是这种情况的正确工具。(例如,在Eclipse中为Ctrl + Shift + G)

当然:静态分析器工具并不完美。必须意识到,在更复杂的情况下,仅在运行时才决定调用该方法的决定,而不是更快(通过反射调用,以脚本语言调用“ eval”函数等)。


5
也许猿脑不应该编写代码?“ 不受人为干预 ”曾经是营销短语。我一直想像大猩猩的手在做这项工作。

3
(猿人也写了这些工具)(嘘!您会毁了这个故事!)

1
我想你们都错了我的意思。我明确地说过,不能保证静态代码分析将找到方法或类的每次调用。就像锡上所说的那样,它是静态分析。不能也不应该期望通过反射,eval()等来找到动态调用。我的观点是,静态代码分析应被视为删除一段代码的必要先决条件。而且项目越大,人脑甚至根本无法进行分析的可能性就越大,更不用说正确地进行分析了……
Johannes Hahn

1
...静态代码分析是繁琐且重复的工作,最好由计算机完成。正是可以由计算机可靠地执行的任务,但是当由猿类大脑执行时却出了名的不可靠。后者仅应用于计算机无法完成的任务,例如查找那些棘手的反射和评估调用。
约翰内斯·哈恩

1
再次,遗漏了重点。即使这些工具不是完美的,因为它们是由猿人编写的(我什至不确定我是否应该同意!计算静态调用层次结构也不是那么困难),但它们仍然远远优于手动任务执行。这不是一种全有或全无的情况。99%可靠的工具仍然比大脑更可取,后者可能以99%的百分比开始,但是在前几千行代码之后迅速下降到更低的比率。
约翰内斯·哈恩

-1

两种可能性:

  1. 您的测试覆盖率达到99%以上:删除代码并完成操作。
  2. 您没有值得信赖的测试范围:那么答案是添加弃用警告。即,您向该地方添加了一些合理的代码(例如,将警告记录到与应用程序的日常使用不冲突的某个容易看到的地方)。然后让它煮几个月/几年。如果您从未发现过这种情况,请使用引发异常的方法来代替不赞成使用的东西。让它站立一会儿。最后,完全删除所有内容。

2
在先前的17个答案中,这似乎并没有提供任何实质性的解释。覆盖面和折旧警告都在那里讨论已经
蚊蚋

@gnat,你是对的。当我初次查看答案时,出于某种原因,我认为我看到了几个顶部并没有很快简洁地到达这两个点的答案。在答案的中间有一些类似的东西。
AnoE
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.