当代码远离“干净代码”实践时,如何维护庞大的开源库?


80

我仍然缺乏编写高质量代码的经验,因此我阅读了解决诸如Robert C. Martin的Clean Code之类的问题的书籍,并不断检查著名库的代码以提高我的技能。

尽管许多开源库已经维护了多年,这意味着它们不太可能走在正确的道路上,但我发现其中许多代码与编写干净代码的原理相去甚远,例如包含数百行代码。

因此,我的问题是:干净代码的原则是否太受限制,我们可以在许多此类库中不使用它们吗?如果没有,如何在不考虑许多这些原则的情况下维护庞大的库?

任何简短的说明,我将不胜感激。如果这个问题对一个新手来说似乎很愚蠢,我深表歉意。

编辑

Butterknife库中查看此示例-Butterknife库-Android社区中最知名的库之一。


71
您正遭受样本偏见的困扰。您说您检查“知名”库的代码。好吧,由于没有遵循最佳实践而倒闭的图书馆并不是“众所周知的”,而是消失了。
约尔格W¯¯米塔格

3
您是否检查过Linux资源?
MartinSchröder18年

55
软件价值的主要衡量标准不是代码的“干净程度”,而是代码完​​成某些特定任务的程度。尽管有些人只是为了编写东西而喜欢编写软件,但是对于大多数人来说,代码只是达到目的的一种手段。
whatsisname

3
没有人不同意你。问题是如何保持不良代码多年?为什么在经过这么多次迭代后仍未清除它?
伊斯兰萨拉赫

13
这个问题的前提(长期维护的开源项目必须固有地遵循一个特定书作者的最佳实践概念)是完全错误的,而且我不知道您从哪里得到的。请在您提出问题的前提下进行扩展吗?
Lightness Races in Orbit比赛'18

Answers:


84

在这里已经有了很好的答案,但是让我说说您的牛刀示例:尽管我不知道代码的作用,但乍一看,它对我来说似乎并不是真正不可维护的。变量和方法名称似乎是有意选择的,代码经过适当缩进和格式化,具有一些注释,长方法至少显示了某些块结构。

是的,它绝不会遵循Bob叔叔的“干净代码”规则,并且某些方法肯定太长(可能是整个类)。但是看代码,我仍然看到足够的结构,这样就可以通过将这些块自己提取到方法中来轻松地“清理”它们(使用重构工具时引入错误的风险很小)。

这种代码的真正问题是,添加一个块和另一个块,而另一个块在某种程度上可以工作,有时需要几年时间。但是每天代码都变得有点难以开发,并且修改和测试它需要更长的时间。而且,当您真的不得不更改某些无法通过“添加另一个块”解决但需要重组的问题时,您希望某人已开始更早地清理代码。


评论不作进一步讨论;此对话已转移至聊天
扬尼斯

158

“清洁代码”中所述的原则并非总是得到普遍同意。多数是常识,但作者的某些观点颇具争议,并不为所有人所共有。

特别是,并非所有人都同意使用简短方法。如果较长方法中的代码未在其他地方重复,则将其中一些提取到单独的方法中(这样您将获得多个较短的方法)会增加总体复杂性,因为这些方法现在对于其他不需要关心它们的方法可见。因此,这是一个折衷,而不是客观的改进。

本书中的建议(与所有建议一样)也适用于特定类型的软件:企业应用程序。其他类型的软件(例如游戏或操作系统)与企业软件相比具有不同的约束,因此发挥着不同的模式和设计原则。

语言也是一个因素:Clean Code假定使用Java或类似语言-如果您使用C或Lisp,则很多建议均不适用。

简而言之,这本书是关于特定软件类别的个人观点。它不会在所有地方都适用。

对于开放源代码项目,代码质量从糟糕到辉煌。毕竟,任何人都可以将其代码发布为开源。但是,如果您看到一个由多个贡献者组成的成熟且成功的开源项目,则可以肯定地确定他们有意识地选择了适合他们的样式。如果这种风格与某些意见或准则相抵触,那么(说白了)就是准则是错误的或无关紧要的,因为工作代码胜过意见。


18
为“面向特定类型的软件” +1。这可以扩展到有关此主题和类似主题的大多数书籍。一丁点读完您所阅读的所有内容,可能会因撰写时间,目标环境,开发方法以及所有其他因素而有所偏差。
Reginald Blue

16
紧随这本书之后,我们会严格遵守许多人所说的“垃圾代码”。
Frank Hileman '18

16
@FrankHileman:完全不遵循该书的建议。
布朗

5
@ jpmc26-您的链接答案属于我非常熟悉的科学编程领域。最近,我获得了一个愿望清单项目,该项目旨在使约翰逊航天中心多次模拟中使用的重力模型相对论正确。计算注释和空白行时,我编写的计算相对于牛顿重力的相对论扰动的代码长145行,并且全部集成在一个函数中。通常,我会畏缩地看到自己写了一个45行长的函数,更不用说145行了。但是在这种情况下不是。...
David Hammen '18

12
...所讨论的函数实现了单个方程,即日记纸Y中的方程X,因此它肯定遵循单个目的规则。(详细信息在等式中占据了四分之一的页面。)没有有意义的地方可以将此功能分为几个部分,也没有有意义的理由。评论,鲍勃叔叔鄙视吗?在这种情况下,它们是绝对必要的,这在科学编程中很常见。虽然可以在模型的TeX文档中看到相关的期刊参考文献,但也可以在实现中看到它们。
David Hammen '18

34

摘要

正如雅克·B(JacquesB)所写,并非所有人都同意罗伯特·C·马丁(Robert C. Martin)的“清洁代码”。

您发现发现的开源项目“违反”了您所期望的原则,很可能仅包含其他原则。

我的观点

我碰巧监督了几个非常符合Robert C. Martin原理的代码库。但是,我并不是真的声称它们是正确的,只能说它们对我们有效 -而且“我们”实际上是至少

  • 我们产品的范围和架构,
  • 目标市场/客户期望,
  • 产品要维持多长时间,
  • 我们使用的开发方法,
  • 公司的组织结构和
  • 我们开发人员的习惯,意见和过去的经验。

基本上,这可以归结为:每个团队(无论是公司,部门还是开放源代码项目)都是唯一的。他们将有不同的优先次序和不同的观点,当然他们将做出不同的权衡。这些折衷及其产生的代码风格很大程度上取决于品味,不能被证明是“错误”或“正确”的。团队只能说“我们这样做是因为它对我们有用”或“我们应该更改它,因为它对我们不起作用”。

就是说,我相信,为了能够在多年内成功维护大型代码库,每个团队都应就他们认为适合上述方面的一组代码约定达成一致。这可能意味着采用罗伯特·C·马丁(Robert C. Martin),另一位作者的做法,或者发明自己的做法。这可能意味着正式记录下来或“以示例方式”记录下来。但是它们应该存在。

考虑“将代码从一个长方法拆分为几个私有方法”的实践。

罗伯特C.马丁说,这种风格的允许限制每个方法的内容,以抽象的层次上-作为一个简单的例子,一个公共方法很可能只包含调用私有方法一样的verifyInput(...)loadDataFromHardDisk(...)transformDataToJson(...)最后sendJsonToClient(...),这些方法将有实施细节。

  • 有些人喜欢这样,是因为读者可以快速了解高层步骤,并可以选择要阅读的详细信息。
  • 有些人不喜欢它,因为当您想了解所有细节时,您必须在类中四处走动以遵循执行流程(这是JacquesB在撰写有关增加复杂性的文章时可能指的)。

教训是:所有人都正确,因为他们有权发表意见。


13

实际上,许多开源库确实存在客观上较差的编码习惯,并且由一小部分长期贡献者来维护,这些贡献者可以解决可读性差的问题,因为他们非常熟悉最常维护的代码部分。事实发生之后,重构代码以提高可读性通常是费力的,因为每个人都需要在同一个页面上,这并不有趣,并且由于没有实现新功能而没有用处。

就像其他人所说的,任何有关干净代码的书都必须说明所有建议,但这些建议并未得到普遍认可。特别是,几乎所有规则都可以遵循过度的热情,用另一个规则取代可读性问题。

就个人而言,如果我没有一个好的名字,我避免创建它们。一个好名字必须简短,并忠实地描述功能对外部世界的作用。这也与尝试使函数参数尽可能少并且没有全局可写数据有关。当一个函数真正复杂时,试图将一个非常复杂的函数分解为较小的函数通常会导致参数列表过长。创建和维护可读代码是在相互冲突的常识规则之间取得平衡的一种练习。阅读书籍虽然不错,但是只有经验会教您如何找到错误的复杂性,这才是真正获得可读性的地方。


2
我要补充一点:仅仅因为某些东西是“开源的”,并不意味着任何人都是贡献者。通常,许多开源项目都是由集团维护的,不管是好是坏,他们会将其项目与其他贡献者隔离开来-除非得到分叉,否则没有其他贡献。如果它没有被分叉,无论是因为没有人需要修改它还是因为没有人知道如何做,那么传统的代码风格可能会保持不变。
can-ned_food

7

大多数开源项目管理不善。显然有例外,但是在开源世界中您会发现很多垃圾。

这并不是对我正在谈论其项目的所有项目所有者/经理的批评,这只是时间问题。这些人与自己的时间有更好的关系,例如实际的报酬工作。

在一开始,代码是一个人的工作,可能很小。小代码不需要干净。或者更确切地说,使代码更干净所需的工作多于收益。

随着时间的流逝,许多人将代码堆成一堆补丁。补丁编写者感觉没有代码的所有权,他们只是想以最简单的方式添加此功能或修复此错误。

所有者没有时间清理东西,没有人在乎。

而且代码越来越大。真丑

随着越来越难找到代码,人们开始在错误的地方添加功能。他们没有修复错误,而是在代码的其他位置添加了解决方法。

在这一点上,不仅仅是人们不在乎,他们不再清理,因为他们害怕破坏事物。

我曾经有人将代码库描述为“残酷和不寻常的惩罚”。

我的个人经历还不错,但是我看到了一些非常奇怪的事情。


23
如果您在此答案中删除了“开放”和“来源”一词,它将继续保持原样。
史蒂芬·韦伯

我要说的是,对于封闭源代码软件来说同样如此。
Mark Rotteveel

3

在我看来,您是在问,如果没有人在做他们应该做的事情,那么这些东西将如何工作如果它确实起作用,那为什么我们应该做这些事情呢?

答案是恕我直言,它的工作原理是“足够好”,也被称为“ 越糟 越好 ”的哲学。基本上,尽管开源和Bill Gates之间有着艰难的历史,但他们实际上都采用了相同的想法,即大多数人关心功能而不是bug

当然,这也导致我们“ 偏差规范化 ”,从而导致出现Heartbleed之类的情况,在这种情况下,就好像在回答您的问题一样,大量,过长的 意大利面条堆称为OpenSSL的开源代码被“ 清理 ” 了大约十年之久。最后出现了严重的 安全漏洞,影响了数亿人

解决方案是发明被称为一个全新的系统LibreSSL,这是要使用干净上下的代码,当然,几乎 没有人 使用它

那么,如何维护编码错误的巨大开源项目呢?答案就在问题上。他们中的许多人都没有保持清洁状态。它们由数千名不同人员随机修补,以涵盖各种奇怪机器上的用例以及开发人员将永远无法进行测试的情况。该代码“足够好”,直到每个人都惊慌并决定花钱解决这个问题之前,一直有效。

那么,如果没有其他人,为什么还要烦恼“ 正确地做某事” 呢?

答案是你不应该。您要么,要么不做,无论世界如何转动,因为人的本性不会随着人类 一生的规模而改变。就我个人而言,我只尝试编写简洁的代码,因为我喜欢这样做的感觉。


1
如此多的链接……乍一看,我认为此答案可能与悬停广告有关,或者它是Wikipedia页面。
强尼·亨利

2

构成好代码的要素取决于上下文,而指导您使用的经典书籍,即使不是太老而无法讨论开源,也至少是对不良内部代码库进行无休止战争的传统的一部分。因此,很容易忽略这样一个事实,即库具有完全不同的目标,并且它们是相应编写的。以下列顺序考虑以下问题:

  • 当我导入一个库或从一个库导入时,我可能对其内部结构没有足够的专家来确切地知道我正在处理的工作所需要的工具包的哪一部分,除非我要复制一个Stack Exchange的答案告诉我要做。因此,我开始输入from A import(例如使用Python),然后看看会发生什么。但这意味着我看到的内容需要反映我需要借用的逻辑任务,而这正是代码库中的内容。无数种使它变得更短的辅助方法只会让我感到困惑。
  • 库是为最不熟练的程序员准备的,他们尝试使用大多数人才隐约听到的算法。他们需要外部文档,并且需要精确地镜像代码,如果我们不断重构所有内容以使短方法和一臂之力的支持者满意,那么它将无法做到。
  • 如果人们借用的每一种图书馆方法都可能被破坏甚至被重命名,那么它可能会破坏世界范围的代码,并带来灾难性的后果。当然,我希望sklearn可以纠正Calinski- Harabasz 的错字,但这可能会导致另一起事件。实际上,以我的经验,库演化的最大问题是当他们过于努力地采用一些好的代码新的“改进”来构造所有内容时。
  • 在内部,出于种种原因我不必反驳(尽管这些观点确实有些夸大了),评论最多充其量是必要的。一个好的注释说明了代码为什么起作用,而不是如何起作用。但是图书馆知道他们的读者是有能力的程序员,比如说他们不能从纸袋里写线性代数。换句话说,一切都需要评论:为什么起作用!(好的,这又是一个夸张。)所以这就是为什么您看到签名行,100行注释块,实际上可能已经在签名行上出现的1行代码(当然,允许语言)。
  • 假设您在Github上进行了一些更新,然后等着看是否接受您的代码。必须清楚为什么更改代码有效。我从经验中知道,重构以使营地更清洁,使其成为功能提交的一部分,这通常意味着节省大量的行,重新布置和重命名,这使您的无薪审查员的工作更加困难,并导致其他上述问题。

我敢肯定,比我更有经验的人可以提及其他几点。


关于第一个要点。这就是为什么您有公共/私有方法。您可以公开内部调用私有或内部方法的公共api。第二个要点也是不准确的。我认为没有理由为什么您不能使用简短的公共方法编写文档,然后调用许多小型方法。
FCin

@FCin,这是一种可行的方法,只要维护人员记得每次来来去去时始终在每个方法前都使用正确的关键字。或者他们可以做些更简单,更不易出错的事情。
JG

在诸如C#,Java之类的语言(鲍伯叔叔通常谈论的语言)中,访问修饰符是用于真正编写任何代码的最基本工具。使用正确的关键字是编写任何代码的一部分。
FCin

@FCin在其他一些语言中很少将它们显式显示,但是我甚至在内部C#代码库上工作,在这些代码库中,人们不一定使用应有的修饰符。
JG

这就是为什么他们应该阅读Bob叔叔的书:)
FCin

2

已经有很多好的答案-我想给出一个开源维护者的观点。

我的观点

我是许多这样的项目的维护者,而他们的代码却不尽人意。有时出于兼容性方面的考虑,我什至无法改进此类代码,因为这些库每周被下载数百万次。

它确实使维护变得更加困难-作为Node.js的核心成员,我害怕触及部分代码,但是无论如何,还有很多工作要做,人们会成功使用该平台并享受它。最重要的是它可以工作。

可读代码

当你说:

我发现其中许多代码与编写干净代码所涉及的原理相去甚远,例如,包含数百行代码的方法。

代码行并不能很好地衡量代码的可读性。在这项研究中,我分析了与linux内核相关的链接,并对程序员进行了调查,发现“常规”代码(人们基本期望的代码)和一致的代码在可理解性方面要优于“干净”的代码。这也符合我的个人经验。

一些开源项目不太受欢迎

Linus “著名”地表示,Linux不应该具有内置的调试器,因为使用调试器的人还不足以在Linux上工作,他也不想吸引更多的人。

就我个人而言,我绝对不同意他在那儿的立场-但这也是人们所做的。


1

开源软件不一定意味着要涉及多个作者。当一个软件(或软件单元)由单个作者编写时,长功能经常出现。

这来自于开发过程的本质。一个简单的方法会随着时间的流逝而扩展,添加新功能并修复错误。

冗长的方法严重降低了新作者对功能的理解。但是,只有一位作者,这很少有问题,而且这个问题往往被忽略。开源的另一个特性是,许多软件没有得到积极开发,因此没有重构工作,例如,将复杂的方法拆分为多个简单的方法。

您没有显示任何示例,但据我了解,这通常也与开发语言有关。一些语言从一开始就执行严格的掉毛规则,并进行大量的单元测试(甚至TDD)。棉绒测试和单元测试通常都可以防止该问题(很难对复杂/长方法进行单元测试)。

通常,如果软件是由一位作者开发的,而其他贡献者仅解决了一些小问题,那么使代码变得更加干净就很难了。

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.