程序员抱怨可读性,谈论糟糕的代码和不干净的代码,以及他们试图理解和维护这些代码时遇到的困难。我们所说的可读性是什么意思?是什么让代码变得不可读?
我听程序员说有些代码“不可读”。我阅读关于可读性和可维护性的文章和书籍。那是什么意思?程序员通常将可读性(或者更多情况下,缺乏可读性)归因于代码本身。但是,就像美一样,可读性取决于观察者的眼睛。当我们说某些代码“不可读”时,我们实际上是指以下一个或多个:
我不能阅读代码,因为我没有足够的经验或专业知识(语言或领域)。
我没有花足够的时间来尝试阅读和理解代码(“它不明显”或“它不直观”)。
我对理解这段代码不是很感兴趣,我更喜欢用我自己的风格重写它。
将“此代码不可读”这句话简单地重写为“我无法读此代码”,就能正确地看待这个问题。
以此类推,很多人觉得读荷马、莎士比亚或纳博科夫很难,也很有挑战性,但我们不会说“麦克白是不可读的”。我们理解问题出在读者身上。我们可能在语言和习语方面没有足够的经验。我们可能没有足够的历史和文化背景(类似于在查看软件时缺乏领域专业知识)。我们可能没有耐心或欲望去花时间去学习如何阅读一本具有挑战性的书。维基百科文章和克里夫笔记的存在是为了给那些不能或不想读原著的人提供tl;dr版本的书籍。当我们在其他(非编程)环境中观察到这种趋势时,我们可能会将其解释为懒惰或注意力持续时间短。当我们以这种方式对代码做出反应时,我们会责怪代码和原始程序员。
当我作为一个十几岁的业余程序员第一次阅读Knuth的“计算机编程的艺术”时,我发现Knuth对算法的大量数学分析很难理解。我不认为Knuth不知道怎么写,或者他的例子和解释需要重构和简化。我想我需要学习更多的数学和分析技巧,这样我才能熟练地理解这些书。随着我对数学的适应程度有所提高,努斯以前无法阅读的书籍获得了很多很好的信息。
我最近告诉一位程序员朋友,根据我在遗留系统上做大量维护工作的经验,所有代码都需要经过充分研究才能进行维护。我更关心理解代码所需的时间,以及更改代码时的风险。好的代码应该易于理解,并且在进行更改时风险较小。这些品质来自原始程序员的技能,以及他们编写代码时施加的约束。最初的程序员可能试图预测未来的维护,并以他们认为清晰和可读的方式编写代码,但要做到这一点,需要对未知的未来需求和未知的未来维护程序员做出假设和预测。程序员很容易做出这些假设,增加复杂性和通用性,远离需求,而实际上并没有使代码更具可读性或可维护性。
程序员通常认为他们应该专注于编写代码。阅读代码,特别是其他人的代码,似乎是一项繁琐的工作,是一种必要的邪恶,经常被归类到担任维护角色的初级程序员手中。阅读代码并弄清楚它并不像是一项创造性的活动,甚至不是一项富有成效的活动。
我亲眼看到(不止几次)专业程序员在看了几分钟之后,就认为工作和生产代码“不可读”和“不可维护”。他们的反对通常归结为美学:“我讨厌PHP。”“代码使用制表符而不是空格,在我的编辑器中看起来很糟糕。”“代码不是用类和对象编写的。”他们没有花足够的时间来理解系统,或者没有学习足够的业务领域知识来阅读上下文中的代码(就像不了解古希腊历史的人对“伊利亚特”不屑一顾)。
程序员在查看他们不理解或不喜欢的代码时,会发现违反神圣原则和所谓最佳实践的例子:“这打破了单一责任原则。”“这看起来像是干巴巴的违规行为。”“全球是一种代码味。”然后,他们建议重写系统,或者可能用他们喜欢的语言、习惯用法和风格重写系统的各个部分(通常被错误地称为“重构”,因为对客户或老板来说,这听起来像是一件技术性的事情)。
每当我必须思考才能理解代码所做的事情时,我都会问自己是否可以重构代码,以使这种理解更直接地显现出来。-马丁·福勒(Martin Fowler)。
任何傻瓜都能写出计算机能理解的代码。优秀的程序员编写人类可以理解的代码。--马丁·福勒(Martin Fowler)。
所有人都尊重马丁·福勒,但这些引述说明了我的观点。我的经验使我预计我将“必须思考”才能理解不熟悉的代码。我认为思考有多难很重要,但我并不指望能立即一目了然地理解重要的代码,即使我有几十年的经验也是如此。在我理解了代码之后,我可能认为我可以更清楚地编写它并对其进行重构,或者我可能会觉得我从代码中学到了一些新东西,而不去管它。我认为代码不必一目了然,特别是考虑到程序员在技能和经验方面的广泛差异。如果我不能立即理解其他程序员的代码,我不会称他们为傻瓜。“其他人”,甚至其他程序员,包括了太多拥有广泛技能和经验的人,使“编写人类可以理解的代码”成为一个有意义的目标。如果优秀的作家写的书每个人都能立刻理解,我们的书架上就只会有“饥饿的毛毛虫”和“小狗偷窥”了。
仅仅因为人们告诉你这件事做不到,并不一定意味着它做不到。这只意味着他们做不到。--安德斯·海尔斯堡(Anders Hejlsberg)
这在读取代码时和编写代码时一样适用。请记住,下次您或与您一起工作的人声明某些代码“不可读”和“无法维护”时。
智力的真正考验不是我们知道怎么做,而是当我们不知道该做什么的时候如何表现。--约翰·霍尔特(John Holt)。
好的代码很简单。代码审查是训练团队编写简单代码的好方法。不要害怕说“这很难理解。”--埃里克·埃利奥特。
“好的代码很简单”实际上什么也没说。我多年的编程经验和业务领域专业知识给了我一个非常不同的“简单”概念,而不是那些经验较少、没有领域专业知识的人花几分钟看一些代码。我们所谓的“简单”取决于我们的经验、技能、兴趣、耐心和好奇心。程序员在不理解代码时应该说点什么,而不是说“这段代码糟透了”,而应该说“我还不能理解这段代码。”这就把重点放在了难以理解的人身上,而不是代码上。我同意代码审查可以提高代码质量和团队凝聚力,但这是否会转化为“简单”的代码取决于程序员。编程团队通常集中在共同的习惯用法和风格上,这使得一起编程变得更容易,但是这种集中并不意味着代码在六个月后对于外人来说就会看起来是可读的。
这是否意味着所有程序员都应该简化他们的代码,以便即使是没有领域专业知识的初学者也能一目了然?我们应该努力满足莎士比亚对哑巴的需求吗?当面对我不理解的代码时,我会首先质疑自己的技能和耐心,如果我有足够的动力(就像付费客户一样),我会花时间学习代码,以提高我理解它的能力。我可能需要查看语言或框架文档,或者试验代码来弄清楚它是如何工作的。当我理解代码时,我可能会认为我知道一种更简单、更清晰的方式来表达它,或者我可能认为代码对我来说只是一个挑战,因为我没有技能、知识或正确的心态。根据我的经验,弄清楚代码需要大量的时间和精力,但当我弄清楚这一点时,我通常不会认为代码有致命的可读性缺陷,或者原始程序员不知道如何编写代码。
编程的复杂性发生在许多级别,从总体系统架构到变量名的选择和流控制习惯用法。有了经验和反思,程序员应该培养一种对不必要的复杂性的感觉,并学会如何在自己的工作中控制它。存在明显的复杂性并不总是意味着代码很糟糕或难以阅读;有些问题证明实现起来很复杂,结果代码需要进行一些研究才能理解。
我们认为编程是使用和编写抽象的过程。当抽象以相当明显的方式映射到需求时,代码通常更有意义。如果抽象是为了“设计模式”或“最佳实践”本身而无缘无故地应用,代码就会变得更难理解,因为您必须在头脑中遵循互不相连的抽象链:业务需求和软件实现。
在过去,程序员编写的代码很少有其他程序员会看到-他们团队或团队的其他成员、他们的导师,可能是分析师或项目经理。现在我们有了开放源码,人们在博客和Stack Overflow问题中发布他们的代码。程序员倾向于在他们的代码中投入他们的自负,他们担心来自许多他们不认识的人的批评。在程序员论坛上,关于能力的诽谤比比皆是。脱离上下文的代码样本会受到批评,并受到风格上的吹毛求疵。事实证明,这一过程对脸皮厚的程序员很有帮助,他们可以将批评解释为有用的指导或无用的侮辱。经验较少的程序员可能会被误导和责骂。公开的代码审查创造了一种程序员的行为艺术,当程序员编写代码以给其他程序员留下深刻印象时,或者是为了避免来自自封的专家的枯萎的批评。
它有助于发现你自己的错误,或者让别人以一种有用的方式向你指出它们。这就是我们如何学习编程,以及如何编写更好的代码。我不认为在您的脑海中编写带有公开羞辱前景的代码会有任何帮助。
编程是一种基于逻辑的创造性艺术形式。每个程序员都是不同的,编写代码的方式也不同。重要的是产出。-约翰·罗梅罗(John Romero)。
一个程序最重要的属性是它是否达到了用户的目的。--C.A.R.霍尔。
更好的编程来自实践、学习(来自书籍和其他代码)和指导。它不是来自于试图盲目地遵守规则和教条,以及你不理解或不能与实际代码相关的货物崇拜。
当我在20世纪70年代中期开始编程时,我的一位导师给了我一本编程风格元素的副本(Kernighan and Plauger,1974)。从那本书中,我了解到一些技术往往比其他技术效果更好,我学到了语言习语、命名变量和函数,以及其他风格和美学方面的考虑,这些都让我印象深刻。从那里我读到了很多关于“好”编程的书,超越了风格和美学的层面。我读了很多代码,有些是我理解的,也有很多是我不理解的(或者说不是马上就理解的)。与英文写作经典“风格元素”一样,Kernighan和Plauger主要关注编码技术和风格,而不是系统设计、模块分解、内聚和耦合以及需求收集等更大的问题。编程风格的元素作为一个起点,介绍如何开发一种风格,使您的代码更具表现力、简明性和可读性。同样,文体元素建议“省略不必要的词”,并告诫作家远离被动语态和过多的形容词。
我们用建造城市的方式建造我们的计算机(系统):随着时间的推移,没有计划,在废墟上。--艾伦·乌尔曼(Ellen Ullman)。
嗯,是的,而且我们倾向于喜欢我们的许多城市。我同意Ellen Ullman的观点,即软件开发通常遵循一条比我们喜欢告诉自己的更随意的道路。这只是意味着我们不能可靠地预测未来,我们并不总是有资源或意愿来拆毁一切并重新开始。编程格言的流派充斥着这样的陈述,只有当我们相信开发软件的“正确方式”,一套导致“好的”代码的硬的、经过验证的原则,以及我们不能立即理解的任何代码一定具有内在的坏处,或者表明我们在键盘上是个傻瓜时,这些格言才有意义。
程序员似乎相信他们还没有见过或使用过的美丽、可读、易于维护的代码的领域。他们似乎认为其他程序员有时间和支持来编写完美、干净、经过测试的代码。那个神话般的境界并不存在。所有的代码都让相当一部分程序员感到困惑、沮丧和冒犯。所有软件都是在时间、预算、管理、需求和技能限制下开发的,这些限制阻碍了任何事情都做得很完美。当我们查看代码时,我们应该牢记这些约束和限制,并立即得出结论:代码无法理解,或者只有傻瓜才会生产这样的软件。
在短暂的计算史上,从来没有人写过一款完美的软件。你不太可能是第一个。--安迪·亨特。
您如何学习编写可读代码?就像学习写可读的英语一样,你必须大量阅读。花点时间去理解那些与您的偏见和偏好不符的肤浅品质之外的代码。模拟您认为特别易读的代码。阅读一些流行的、广受好评的关于编程风格和代码质量的书籍,从更有经验的程序员那里获得一些好处。尝试用具体的术语描述为什么代码不容易阅读或维护,尝试您的替代方案(重构),确保它们有效,让其他程序员建设性地检查您的代码并听取他们的意见。