代码一致性和代码改进之间的正确平衡是什么?


53

最近,我与一位同事讨论了代码风格。他在争辩说,您对API的使用以及所使用的一般模式应与周围的代码尽可能地相似,如果与整个代码库无关,则应与代码外观(括号位置,大写字母等)相同。 。例如,如果我要在C#中将方法添加到DAO类中,那么即使该类中的其他任何方法都没有使用LINQ,我也会尝试在适当的地方使用LINQ来帮助使我的代码干净并易于维护。但是,我的同事会争辩说我不应该在这种情况下使用它,因为它会违背该类的现有风格,因此更难以理解。

起初我发现他的立场相当极端,但是经过一会儿的思考,我开始明白他的观点。在假设的LINQ示例中,也许该类不包含它,因为我的同事们不熟悉LINQ?如果是这样的话,如果我不使用我的代码,对我的开发人员来说,它的代码维护起来会更好吗?另一方面,如果我真的相信使用这种技术可以使代码更简洁,那么即使它与周围的代码有很大的不同,我也不应该使用它吗?

我认为,我同事观点的症结在于,如果我们都打算以不同的方式在代码库中实现相似的功能,而我们每个人都认为我们的方式是“最佳”的,那么最终整个代码将变得更加困难了解。但是此刻,我仍然认为,如果我们盲目地遵循现有代码,那么质量将随着时间的流逝而缓慢恶化。

那么,模式在何种程度上成为代码风格的一部分,我们应该在保持一致性和进行改进之间划清界限吗?


14
那么,如果每个人都将自己限制在“坏”的方式上,那么代码质量将如何提高?
2013年


当您编写LINQ时,不是说LINQ to SQL吗?如果可以,我可以同意你的朋友。如果您在谈论LINQ,那么我不同意他的观点。这些天,每个人都必须熟悉LINQ。这不是他们的选择。他们付钱以熟悉。
2013年

@Peri我的意思只是基本的LINQ-我想我的示例应该是关于IEnumerables而不是DAO的。
罗伯·约翰逊

2
ICYMI-Dilbert关于这个主题的漫画:dilbert.com/strips/2013-09-21
Jim Bethancourt,

Answers:


53

给出更一般的答案:

在这种情况下,您有两个相互对立的编程“最佳实践”:代码一致性很重要,但是选择最佳的方法来完成任务也是如此。对于这一困境,没有一个正确的答案。这取决于几个因素:

  • “正确”的方式有多有益?

    • 有时,经过改进的新最佳实践将极大地提高性能,消除错误,更容易编程等。在这种情况下,我会倾向于使用新方法。另一方面,“正确的方式”可能仅仅是句法糖,或者是约定俗成的做某事实际上并不优越的方法。在这种情况下,代码一致性可能更重要。
  • 不一致会造成多大的问题?

    • 新代码与旧代码之间如何相互联系?您的新代码是库的一部分吗?它会创建一个对象并传递给程序的许多部分吗?在这种情况下,一致性非常重要。使用新的API或通常采用新的处理方式,可能会产生细微不同的结果,从而破坏程序其他地方的假设。 另一方面,如果您编写的是相当孤立的代码,则不一致的可能性较小。
    • 您的代码库有多大?有多成熟?有多少开发人员需要了解它并进行研究?商定的,一致的标准对于大型项目而言更为重要。
    • 代码是否需要在可能不支持最新功能的较旧环境中运行?

基于这些问题的平衡,您必须正确选择采用哪种路线。我个人认为一致性方面的价值不大,因此宁愿使用最新的最佳方法,除非这样做会花费大量成本。

当然,还有第三个选择:重写现有代码,以便它使用最佳方法保持一致。有时候这是必要的,但是成本很高。


7
很好且平衡的答案(+1)。以我的经验,如果每个人都同意相对较少的一组易于理解的习惯用法,那么一个团队可以提高工作效率,而不是每个团队成员都可以自由使用其他团队成员可能难以理解的新的不同习语。关于“重写现有代码,使其使用最新实践并保持一致”的声明,有一点要点:“最新”并不自动意味着“更好”。必须始终视情况决定。
乔治

1
@Giorgio,感谢您的反馈;我调整了最后一点的语言。

6
Giorgio给出了很好的答案和好的评论。也许值得考虑的是,在此示例中,根据团队/文化的不同,有可能采用协调的团队采用方式,而不是单独开始。这样可以减少维护方面的风险(因为每个人都在船上)。(即,目前更强调团队的实际技能,而不是他们过去编写的代码。)
Matt

+1。同意的,彻底且平衡的答案-很好地考虑了各种情况。再次,乔治(Giorgio)的好评。
罗伯·约翰逊

1
关于采用新的习语,技术:我认为新项目的开始是介绍新事物的好时机。此后,在整个项目期间,应保持代码样式尽可能一致。最近,我不得不挖掘出一些已有5年历史的代码,我很高兴我仍然可以通过该代码找到自己的方式,因为整个团队都遵循我们所同意的通用体系结构和成语(并非没有奋斗! )在项目开始时。
Giorgio 2013年

26

在我看来,保持一致毫无价值。持续改进是必须的。

您同事的职位确实阻碍了创新。一致性参数使您陷入只有在将所有代码迁移为使用LINQ 的情况下才可以使用LINQ的情况。而且,我们没有时间这样做,对吗?

我宁愿有些地方仍然foreach在ArrayLists上执行代码而其他部分在LINQ on上使用LINQ IEnumerable<T>,而不是坚持使用最古老的方法直到结束。

保持相关性并学习新的做事方式是您同事的责任。


3
“保持相关性并学习新的工作方式是您同事的责任。”:一种可能是在新代码(新模块)中使用新的习惯用法和库,并在旧代码中保持一致的样式。
乔治

8

对于公共和内部API而言,API一致性非常重要。

代码格式化的一致性很重要,并且理想情况下,应该由自动格式化工具对每个人使用相同的格式化规则来实施。使共享版本控制的代码库更容易使用。

命名约定也应保持一致,例如局部变量等也应保持一致。

应该使用静态分析工具来强制执行某些其他约定和良好实践(但不要盲目地遵循任何此类工具的默认设置,默认设置有时可能会发疯),尽管如果有需要,不要害怕禁用某些功能临时检查(通常在注释中使用指令),如果可以的话请进行检查。

除了上面列出的内容之外,函数/方法内部发生的事情无需以任何方式保持一致,只要它本身就是好的代码即可。好的,可理解的,注释良好的代码非常重要,但是在那之后,如果编译器和静态分析工具认为它是一致的代码,那就足够一致了。


关于诸如LINQ之类的新语言功能(嗯,这并不是完全新的),唯一需要考虑的是,将在使用代码的地方使用足够新版本的语言/库吗?如有疑问,请使用已知兼容的功能。当然,这并不妨碍您在整个系统中进行版本升级,因此您可以开始使用新功能。

每个使用代码的开发人员都应该保持最新状态,因此任何.NET开发人员都应该了解LINQ,如果他们不了解,则应该出于自身的利益而被迫学习(您永远不知道什么时候会寻找新的。因某种原因而从事这项业务)。


6

答案很简单。

一致性至关重要。

但它有一个警告...

您和您的同事可能会迷恋于错误的一致性

实施是一次性的。根据测试套件的质量和全面性,可以轻松地对它们进行彻底检修。担心诸如“这应该是一个属性吗?”,“稀疏代码不应该使用LINQ而不是较低级的构造吗?”之类的东西。具有可疑的价值。很难将任何度量与实现级别的一致性值联系起来。在这个级别上要问的一个更好的问题是“此代码是否按宣传的方式工作?”。TL; DR实施的一致性是“小脑子”开始研究霍布林的地方。

为什么一致性在这里不那么重要?实现通常只有很少的贡献者。大多数方法都是书面的,永远不会被触及。在剩下的代码中,几乎可以肯定有两个贡献者的方法的数量是大多数。这种模式是无限的。在这种情况下,一致性并不是那么重要。如果代码的保存期限非常短(几年),那么从积极的一致性中获得收益可能就不是一个重要因素。

这并不是说您应该在实现中发疯。与其说是一种好,干净,简单的设计,不如说一种愚蠢的锅炉板一致性方法,对您的假设未来维护者而言将有价值几个数量级。这将我们带到了真正的意义...

API不是一次性的。

这是所有API代码级别,Web服务,SDK等。这些必须,必须,必须保持一致。由于多种原因,这种一致性带来的生产力提高是巨大的:

集成测试:

如果您保持API的一致性,则可以创建集成测试套件。这些使开发人员可以自由交换实现细节并立即进行验证。想要将您的同事废话换成LINQ吗?集成测试是否运行?在准备投入生产时,它还提供了验证。由于计算机速度很快,因此一台笔记本电脑可以执行数千名执行日常任务的测试人员的工作。它等于大大增加了组织的员工人数。

生产率

当API保持一致时,您只需遵循已了解的使用API​​其他部分的知识,就可以猜测如何使用API​​。这是因为API提供了自然,一致的“外观”。这意味着您的客户花费在筛选文档上的时间更少。入职更容易,更便宜。向开发API的人员询问的问题更少。一致性使每个人都是赢家

为什么一致性在这种情况下很重要?因为API具有与实现完全相反的问题。使用它们的人数通常远远大于为其实现做出贡献的人数。由于保持一致性而产生的小收益将成倍增加,并且要摊销保持这种一致性的成本。

结论

一致性很昂贵。从表面上看,它降低了生产率。它限制了开发人员,使他们的生活更加艰难。它限制了他们解决问题的方式,有时会迫使他们以非最佳方式解决问题。这通常是由于他们不了解,构思不当或他们不了解合同(较大的组织或组织间政策)的原因。

Raymond Hettinger在他的Pycon 2015演讲中谈到了为Python程序员团队使用PEP8样式指南的观点。他表明,对一段代码的风格一致性的痴迷使代码审阅者错过了严重的逻辑和设计缺陷。他的假设可以概括为发现风格上的矛盾很容易。确定一段代码的真实质量很难

这一点很关键。确定一致性的重要位置并积极保护它。不重要的地方不要浪费时间。如果您不能提供一种客观的方法来衡量一致性的价值(在上述情况下为“有效员工数”,成本是生产力的函数),并且您无法证明回报是可观的,那么您可能会损害您的组织。


4

不熟悉LINQ?如果是这样的话,如果我不使用我的代码,对我的开发人员来说,它的代码维护起来会更好吗?

C#语言仍在不断发展。如果人们没有从C#1中学到更改,他们将在以下方面丢失:

  • 泛型
  • 部分
  • 匿名方法
  • 迭代器
  • 可空类型
  • 自动属性
  • 匿名类型
  • 扩展方式
  • Lambdas
  • 异步方法

这只是在Wikipedia文章中找到的一些常用功能的一小部分。我要说的是,如果开发人员不学习,那么代码库将处于真空状态。希望您的开发人员在不断完善的基础上,不断完善自己的代码,并不断完善其代码库。您还记得使用.NET 1手动实现属性有多糟吗?

Linq使生活更轻松。尽可能使用它。您可能会激励团队成员。

那么,模式在何种程度上成为代码风格的一部分,我们应该在保持一致性和进行改进之间划清界限吗?

改进是逐步的。对我来说,保留旧代码的可读性和维护性较差没有任何意义。您的团队成员至少应该能够弄清楚正在发生的事情,即使他们无法编写。


2
我完全同意您的意思,但是我的问题不是关于新的语言功能和开发人员学习它们的责任,而是一个更普遍的问题,即在相对较小的代码库区域内以不同的方式解决类似的问题。我之所以提到LINQ,是因为它是使用其他方法解决问题的一个很好的例子。我的同事很高兴使用新的语言功能,但前提是这些功能不违背他正在开发的代码。
罗伯·约翰逊

@RobertJohnson我要向您的同事指出,可维护性比一致性要重要,因此,即使某些东西“违背”了现有代码,如果它更易于维护,您也应该这样做。我怀疑使用旧的DAO比Linq更具可维护性。
安迪

3

您应该保持一致,但不一定要与旧代码保持一致。也就是说,您的团队应该就正确的操作方式达成共识,并在编写新代码或进行实质性更改时使用该方式。

这样,您将获得一致性的大部分好处(特别是,谁编写代码都无关紧要),但是如果团队通过另一种方式看到明显的好处,仍然可以改善。

在理想的情况下,我们将重写所有旧代码以符合新的正确方法。尽管这在经济上不可行,但应该具有技术意义(否则,新方法不是更好的方法),并且您的长期计划应该是对其进行升级。如果这样做不值得,请不要理会新方法。换句话说,考虑将其声明为正确方法的新方法必须具有明显的优势。


1

我认为在项目中遵循代码风格很重要。命名约定,缩进等

我不同意仅仅因为您的同事不理解它们,或者他们以前没有在项目中使用过它们,就应该限制您使用新的语言结构或设计模式。

当然,这些东西应该有充分的理由使用它们。性能或简洁(如果适用)。


减是什么意思?
ozz 2013年

0

我会非常小心地盲目遵循当前的设计模式。当然,当没有明显的不利影响时,应该始终尝试在整个代码库中遵循类似的模式。

但是,要进入大多数开发人员认为遵循现有不良模式是“最佳实践”的情况,这太容易了,因为这种情况会导致良好的开发人员编写不良的,无法维护的代码。

另一方面,一致性很重要。当处理庞大的代码库时,尝试遵循类似的模式非常有用,这样即使开发人员没有阅读代码库的每一寸,他们也知道会发生什么。

因此,我认为最好建立和更新有关开发人员应遵循的模式的准则。这样,任何新代码都与其他新代码一致。


-1

我们有定期的,相当非正式的技术会议,我们任何人都可以举行。如果我们发现一种新技术,我们将在会议上进行介绍。通常,您会对同事接受和理解该想法的感觉很好。这可以帮助您确定将其包含在代码中是否明智;如果您这样做,则可以帮助其他人对其进行解密;如果其他人需要使用该样式的帮助,则可以帮助您确定“去”的人。如果没有人重新发明轮子,我们仍然会在原木上拖拉东西。


反对票是干什么的?
2013年

我不是拒绝投票的人,但这似乎并不是针对这个问题的。这是一个可以应用于任何事物的团队流程,而不是讨论编码风格问题。另外,您的意思不是“发明轮子”而不是“发明轮子”吗?

我想说无论圆木是否是轮子,但它都会旋转,减少摩擦,表面与地面连续接触且是圆形,这是它的语义。我敢肯定,我的意思有更好的例子-我会将其留给读者练习。
2014年
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.