您已经发现一些看起来多余的代码,而编译器没有注意到这一点。为确保(或尽可能接近)删除该代码不会导致回归,您应该怎么做。

想到两个主意。


“简单地”根据代码看起来是否应该执行来使用推论。但是,有时这可能是一项复杂,耗时的工作,风险很小(您的评估容易出错),而没有实质性的业务回报。
将日志记录在该代码部分中,并查看它在实践中的输入频率。经过足够的执行后,您应该有足够的信心删除代码是安全的。

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

评论

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

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

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

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

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

#1 楼

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

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


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


评论


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

– njuffa
17 Mar 7 '17 at 15:48

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

– knub
17年7月7日在16:15

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

– Weyland Yutani
17 Mar 7 '17 at 17:46

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

– njuffa
17 Mar 7 '17 at 18:15

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

– kojiro
17 Mar 8 '17 at 11:15

#2 楼

此过程分为两半。第一个是确认代码确实已死。第二个是理解错误的代价,并确保适当地降低了错误的代价。

这里的许多答案对前半部分都有很好的解决方案。诸如静态分析器之类的工具非常适合识别死代码。在某些情况下,grep可以成为您的朋友。我经常采取的一个不寻常的步骤是尝试确定代码的原始用途。争论“ X不再是我们产品的功能,并且代码段Y旨在支持功能X”要比说“我看不到代码段Y有任何目的”要容易得多。 >
后半部分是打破是否应该删除代码的僵局的关键步骤。您需要了解错误答案的含义。如果您因错误的答案而要死掉,请注意!也许是时候接受代码随时间推移而发展的好时机,而不是尝试自己不要编写更多的东西。如果人们不会丧命,问问自己自己的用户有多宽容。如果您破坏了某些东西并保持了客户关系,是否可以向他们发送修复程序?您是否有付费的问答团队来查找此类问题?这些类型的问题对于理解按下删除键之前必须具有的确定性至关重要。

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


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


评论


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

– kojiro
17 Mar 8 '17 at 12:37

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

–Cort Ammon
17 Mar 8 '17 at 14:13

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

–。A. I. Breveleri
17 Mar 8 '17 at 21:40



@ A.I.Breveleri我应该希望代码本身有注释来解释这一点,而不仅仅是在StackExchange:D上

–凯尔·斯特兰德(Kyle Strand)
17 Mar 9 '17 at 23:44

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

–Cort Ammon
17 Mar 9 '17 at 23:46

#3 楼

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

评论


我不是一个低俗的人,但是如果您怀疑从未运行的代码段不是完整的函数或类,而是方法/函数的一部分,该怎么办?

–图兰斯·科尔多瓦(TulainsCórdova)
17年7月7日在17:01

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

– Dezza
17 Mar 7 '17 at 17:20

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

– Piwi
17 Mar 7 '17 at 21:44



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

–aroth
17年8月8日在5:16

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

–凯文·谢伊(Kevin Shea)
17 Mar 8 '17 at 14:54

#4 楼


识别出看起来无效的代码(静态分析等)。
每次调用所谓的无效代码都添加一条日志消息。函数/方法很容易;对于像常量这样的静态成员,则比较棘手。有时您可以仅将代码标记为已弃用,运行时将自动为您记录一条消息。保留完整的代码。


在加载死模块时记录一条消息:大多数语言都有一种在加载时运行静态初始化的方式,可以背负式进行。
确保您的日志消息中包含合理的堆栈跟踪,以便您理解所谓的无效代码。


在所有测试套件中运行更改后的代码。您的测试套件还应该测试特殊的时间,例如穿越午夜,四分之一圈,一年之类的时间。查看日志,更新对已死者的了解。请注意,单元测试可以专门测试无效代码,而其他任何单元测试和集成测试都无法测试。
将更改后的代码在生产环境中运行几周。确保在此期间运行每个定期过程(例如每月一次的ETL cron作业)。
查看日志。记录的所有内容实际上并没有死亡。即使没有被调用,调用图在其余无效代码上的传递性关闭也可能不会失效。分析一下。也许有些分支已经安全地死掉了(例如,使用现已灭绝的服务的API),而有些分支还没有死。也许只为其中定义的常量加载整个模块/类,然后您就可以轻松地使用它。
剩下的所有东西都是安全死掉的,可以删除。


评论


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

–user251748
17 Mar 7 '17 at 17:20

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

– Erik
17 Mar 8 '17 at 20:42

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

– Erik
17 Mar 8 '17 at 21:56

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

– Erik
17 Mar 8 '17 at 22:03

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

–史蒂夫·巴恩斯(Steve Barnes)
17年3月11日在6:43

#5 楼

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

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

这可能有助于识别意外功能,而不会警告最终用户。在最佳情况下,代码实际上什么也不做,而发生的所有事情就是在删除之前不需要的代码保留了多个版本。

评论


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

–ruakh
17年8月8日在18:15

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

– underscore_d
17 Mar 12 '17 at 13:57

#6 楼

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

但是代码不是资产,这是一种责任。
/>
除非您能解释为什么它很重要并指出它的测试,否则“安全”的选择可能就是删除它。

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

评论


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

–user251748
17 Mar 9 '17 at 14:26

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

– SusanW
17年3月13日在13:02

#7 楼

您可以使用功能切换来更改软件的执行路径,以完全忽略所讨论的代码。如果您发现与代码相关的一些重大错误,请重新打开开关并研究其可能的路径。

如果您长时间未发现任何问题,这种方法应该可以使您充满信心。可以通过实时部署将其重新启用。但是,更好的方法是在有问题的区域周围应用额外的日志记录和测试覆盖范围,这将为是否使用它提供更多的证据。 martinfowler.com/articles/feature-toggles.html

#8 楼

当然可以进行静态分析……而且,好处是,您不需要任何新工具。您的编译器具有所需的所有静态分析工具。只需更改方法的名称(例如,将DoSomething更改为DoSomethingX),然后运行构建即可。

成功的方法显然没有被任何人使用。安全删除。

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

如果您真的担心删除它,请考虑保留代码,但将其标记为ObsoleteAttribute(或相当于您的语言)。像这样发布一个版本,然后在没有任何问题的情况下删除代码。

评论


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

–eis
17 Mar 8 '17在7:30



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

–吴宗宪
17 Mar 8 '17 at 9:48



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

–eis
17 Mar 8 '17在10:31



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

–吴宗宪
17 Mar 8 '17 at 20:46

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

–eis
17 Mar 8 '17 at 21:44



#9 楼


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


评论


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

–约翰尼斯·哈恩(Johannes Hahn)
17年7月7日在16:18

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

–马库斯·劳斯伯格(Markus Lausberg)
17 Mar 8 '17 at 6:40



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

– Sebi
17 Mar 8 '17 at 19:15

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

– jpmc26
17 Mar 9 '17 at 2:18

@ jpmc26是的,当然。

–约翰尼斯·哈恩(Johannes Hahn)
17 Mar 9 '17 at 8:18

#10 楼

删除无法访问的代码

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

不幸的是,并非所有的语言都是静态类型的,并且并非所有的静态类型语言都是有原则的。可能出错的事情包括(1)反射和(2)无原则的重载。在运行时通过反射,那么您就不能依赖编译器。这样的语言包括Python,Ruby或Java。

如果您使用的是无原则的重载语言,那么只需删除重载就可以将重载解决方案简单地切换为其他重载。某些此类语言允许您编写与代码使用相关的编译时警告/错误,否则您将不能依赖编译器。这样的语言包括Java(使用@Deprecated)或C ++(使用[[deprecated]]= delete)。

因此,除非您非常幸运地使用严格的语言(想到铁锈),否则您可能真的会开枪通过信任编译器。不幸的是,测试套件通常是不完整的,因此也没有太多帮助。

提示下一节...
更有可能实际引用了该代码,但是实际上您怀疑从未引用该代码的分支。

在这种情况下,无论使用哪种语言,该代码都是显然可以实现,并且只能使用运行时检测工具。

过去,我已经成功地采用了三阶段方法来删除此类代码:


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

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

希望大多数应用程序请缩短周期。

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

评论


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

– Toby Speight
17 Mar 8 '17在11:15



@TobySpeight:是的,这也是一个很好的选择。我来编辑一下。

– Matthieu M.
17 Mar 8 '17 at 11:34

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

–user251748
17 Mar 9 '17 at 14:23

#11 楼

从生产中删除代码就像打扫房子。当您从阁楼上丢下一个物件时,您的妻子第二天就会杀死您,因为她丢掉了曾在1923年去世的邻居的曾祖母的第三位侄子返乡回国的礼物。使用大家已经提到的各种工具进行分析,并使用已经提到的分步折旧方法之后,请记住,在实际删除工作时,您可能会离开公司。让代码保留下来并记录其执行,并确保将有关其执行的任何警报明确地传达给您(或您的后继者和分配者)是至关重要的。

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

PS:为回应评论进行澄清。原始问题是“您如何安全删除。”,当然是我的答案中的版本控制。就像挖垃圾一样,找到有价值的东西。任何意义上的机构都不应丢掉代码,而版本控制也不应该成为每个开发人员的严格要求。

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

恕我直言,注释是多余的,可以删除。

评论


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

–JDługosz
17 Mar 8 '17 at 8:04

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

–user251748
17 Mar 9 '17 at 14:25

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

– underscore_d
17 Mar 12 '17 at 14:07



#12 楼

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

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

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

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

评论


并非所有语言都被编译,并非所有编译器都具有优化功能,并且并非所有优化都相等,许多驱动程序不会删除未调用的代码,以防万一有某种原因存在该代码。如果代码位于共享库/ DLL中,则不会删除它。

–史蒂夫·巴恩斯(Steve Barnes)
17年3月11日在6:50

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

– Navin
17年3月13日在11:21



#13 楼

实际上,我最近使用一种称为“ 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
17 Mar 9 '17 at 12:48

#14 楼

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

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

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

两个问题:


从中获取日志其他用户。也许您可以设置一个全局标志,该标志将在退出时使程序崩溃,并请用户向您发送错误日志。
跟踪调用者。在某些高级语言(例如Python)中,您可以轻松获得回溯。但是在编译语言中,您必须将返回地址保存到日志中,并在退出时强制进行核心转储(第1点)。
在C语言中,这应该非常简单:使用条件块(例如#ifdef ... #endif)仅在知道它会工作(并且已经测试过)时才使用它,并仅从堆栈中读取返回地址(可能需要内联汇编语言)。

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

#15 楼

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

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

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

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

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

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

#16 楼

我的方法(在这种情况下我一直认为是行业标准)到目前为止还没有被怪异地提及:

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

我也看到了日志记录方法-更确切地说,我是我已经看到了日志代码:还有更多的死代码被留在那里了10年。如果您要删除全部功能,那么日志记录方法是相当不错的选择,但是如果您只删除一小段无效代码,那么记录方法就不太合适。

#17 楼

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

您将不得不判断如何最好地减轻这种不可避免的风险。其他人提到了日志记录。日志记录当然可以证明它已被使用,但不能证明它没有被使用。由于无效代码的主要问题是它得到了维护,因此您可以添加注释说它是可疑的无效代码,而不用对其进行任何维护。该代码处于源代码控制之下,您总是可以找出添加它的时间,然后确定它的调用方式。

最终,您要么理解它,要么不理会它,否则便会碰巧。
/>

#18 楼

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

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

当代码的作者坐在您旁边时,这确实是一种糟糕的文化,但是您看不到有任何理由要谈论,因为您确定他只是编写了不好的代码,而且您的代码是如此完美。而且,当然,他只是在注释中写随机的单词,而无需浪费时间阅读。

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

与作者讨论可能还会发现该代码是已删除或从未完成的内容的历史遗留物,因此可以安全地进行删除。

#19 楼

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

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

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

评论


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

–user251748
17 Mar 7 '17 at 16:44



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

–user251748
17年7月7日在16:46

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

–约翰尼斯·哈恩(Johannes Hahn)
17 Mar 9 '17 at 14:23



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

–约翰尼斯·哈恩(Johannes Hahn)
17 Mar 9 '17 at 14:26

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

–约翰尼斯·哈恩(Johannes Hahn)
17 Mar 9 '17 at 14:34

#20 楼

两种可能:


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


评论


在先前的17个答案中,这似乎并没有提供任何实质性的解释。覆盖和弃用警告都已经在那边讨论过了

– gna
17 Mar 9 '17 at 20:53

@gnat,你是对的。当我第一次浏览答案时,出于某种原因,我认为我看到了几个在顶部并没有很快简洁地到达这两个点的答案。在答案的中间有一些类似的东西。

– AnoE
17 Mar 10 '17 at 7:49