共计 4222 个字符,预计需要花费 11 分钟才能阅读完成。
写了多年的代码,始终感觉如何写出洁净优雅的代码并不是一件容易的事件。按 10000 小时刻意训练的定理,假如每天 8 小时,一个月 20 天,一年 12 个月,大略也须要 5 年左右的工夫成为巨匠。其实咱们每天的工作中真正用于写代码的工夫不可能有 8 个小时,并且很多时候是在实现工作,在业务压力很大的时候,可能想要达到的指标是如何尽快的使得性能 work 起来,代码是否洁净优雅十分可能没有能放在第一优先级上,而是怎么快怎么来。
在这样的状况下是非常容易欠下技术债的,工夫长了,这样的代码基本上无奈保护,只能推倒重来,这个老本是十分高的。欠债要还,只是迟早的问题,并且等到要还的时候还要赔上额定的不菲的利息。还债的有可能是本人,也有可能是起初的继任者,但都是团队在还债。所以从团队的角度来看,写好代码是一件十分有必要的事件。如何写出洁净优雅的代码是个很艰难的课题,我没有找到万能的 solution,更多的是一些 trade off,能够略微讨论一下。
代码是写给人看的还是写给机器看的?
在大部分的状况下我会认为代码是写给人看的。尽管代码最初的执行者是机器,然而实际上代码更多的时候是给人看的。咱们来看看一段代码的生命周期:开发 –> 单元测试 –> Code Review –> 功能测试 –> 性能测试 –> 上线 –> 运维、Bug 修复 –> 测试上线 –> 退休下线。开发到上线的工夫兴许是几周或者几个月,然而线上运维、bug 修复的周期能够是几年。
在这几年的工夫外面,简直不可能还是原来的作者在保护了。继任者如何能了解之前的代码逻辑是极其要害的,如果不能保护,只能本人从新做一套。所以在我的项目中咱们常常能见到的状况就是,看到了后任的代码,都感觉这是什么垃圾,写的乌七八糟,还是我本人重写一遍吧。就算是在开发的过程中,须要他人来 Code Review,如果他们都看不懂这个代码,怎么来做 Review 呢。还有你也不心愿在休假的时候,因为其他人看不懂你的代码,只好打电话求助你。这个我印象极其粗浅,记得我在工作不久的时候,一次回到了老家休假中,忽然共事打电话来了,呈现了一个问题,问我该如何解决,过后电话还要收漫游费的,十分贵,然而我还不得不反对直到耗光我的电话费。
所以 代码次要还是写给人看的,是咱们的交换的路径。那些十分好的开源的我的项目尽管有文档,然而更多的咱们其实还是看他的源码,如果开源我的项目外面的代码写的很难读,这个我的项目也基本上不会火。因为代码是咱们开发人员交换的根本路径,甚至可能口头探讨不分明的事件,咱们能够通过代码来说分明。代码的可读性我感觉是第一位的。各个公司预计都有本人的代码标准,遵循相干的标准放弃代码格调的对立是第一步(举荐谷歌代码标准和微软代码标准)。标准里个别都包含了如何进行变量、类、函数的命名,函数要尽量短并且放弃原子性,不要做多件事件,类的根本设计的准则等等。另外一个倡议是能够多参考学习一下开源我的项目中的代码。
KISS(Keep it simple and stupid)
个别大脑工作记忆的容量就是 5-9 个,如果事件过多或者过于简单,对于大部分人来说是无奈间接了解和解决的。通常咱们须要一些辅助伎俩来解决简单的问题,比方做笔记、画图,有点相似于在内存不够用的状况下咱们借用了外存。
学 CS 的同学都晓得,外存的访问速度必定不如内存访问速度。另外一般来说在逻辑简单的状况下出错的可能要远大于在简略的状况下,在简单的状况下,代码的分支可能有很多,咱们是否可能对每种状况都思考到位,这些都有艰难。为了使得代码更加牢靠,并且容易了解,最好的方法还是放弃代码的简略,在解决一个问题的时候尽量应用简略的逻辑,不要有过多的变量。
然而事实的问题并不会总是那么简略,那么如何来解决简单的问题呢?与其借用外存,我更加偏向于对简单的问题进行分层形象。网络的通信是一个非常复杂的事件,两头应用的设施能够有无数种(手机,各种 IOT 设施,台式机,laptop,路由器,交换机 …), OSI 协定对各层做了形象,每一层须要解决的状况就都大大地简化了。通过对简单问题的合成、形象,那么咱们在每个档次上要解决解决的问题就简化了。其实也相似于算法中的 divide-and-conquer,简单的问题,要先拆解掉变成小的问题,从而来简化解决的办法。
KISS 还有另外一层含意,“如无必要,勿增实体”(奥卡姆剃刀原理)。CS 中有一句 “All problems in computer science can be solved by another level of indirection”,为了零碎的扩展性,反对未来的一些可能存在的变动,咱们常常会引入一层间接层,或者减少两头的 interface。在做这些决定的时候,咱们要多考虑一下是否真的有必要。减少额定的一层给咱们的益处就是易于扩大,然而同时也减少了复杂度,使得零碎变得更加不可了解。对于代码来说,很可能是我这里调用了一个 API,不晓得理论的触发在哪里,对于了解和调试都可能减少艰难。
KISS 自身就是一个 trade off,要把简单的问题通过形象和分拆来简单化,然而是否须要为了保留变动做更多的 indirection 的形象,这些都是须要认真思考的。
DRY(Don’t repeat yourself)
为了疾速地实现一个性能,晓得之前有相似的,把代码 copy 过去批改一下就用,可能是最快的办法。然而 copy 代码常常是很多问题和 bug 的本源。有一类问题就是 copy 过去的代码蕴含了一些其余的逻辑,可能并不是这部分须要的,所以可能有冗余甚至一些额定的危险。
另外一类问题就是在保护的时候,咱们其实不晓得修复了一个中央之后,还有多少其余的中央还须要修复。在我过来的我的项目中就呈现过这样的问题,有个问题明明之前做了修复,过几天另外一个客户又提了相似的问题呈现的另外的门路上。雷同的逻辑要尽量只呈现在一个中央,这样有问题的时候也就能够一次性地修复。这也是一种形象,对于雷同的逻辑,形象到一个类或者一个函数中去,这样也有利于代码的可读性。
是否要写正文
集体的观点是大部分的代码尽量不要正文。代码自身就是一种交换语言,并且一般来说编程语言比咱们日常应用的书面语更加的准确。在放弃代码逻辑简略的状况下,应用良好的命名标准,代码自身就很清晰并且可能读起来就曾经是一篇良好的文章。特地是 OO 的语言的话,自身 object(名词)加 operation(个别用动词)就曾经能够阐明是在做什么了。反复一下把这个操作的名词放入正文并不会减少代码的可读性。并且在后续的保护中,会呈现批改了代码,却并不批改正文的状况呈现。在我做的很多 Code Review 中我都看到过这样的状况。尽量把代码写的能够了解,而不是通过正文来了解。
当然我并不是拥护所有的正文,在公开的 API 上是须要正文的,应该列出 API 的前置和后置条件,解释该如何应用这个 API,这样也能够用于主动产品 API 的文档。在一些非凡优化逻辑和负责算法的中央加上这些逻辑和算法的解释还是十分有必要的。
一次做对,不要置信当前会 Refactoring
通常来说在代码中写上 TODO,等着当前再来 refactoring 或者改良,基本上就不会再有当前了。咱们能够去咱们的代码库外面搜寻一下 TODO,看看有多少,并且有多少是多少年前的,我置信这个后果会让你很诧异(欢送大家留言分享你查找之后的后果)。
尽量一次就做对,不要置信当前还会回来把代码 refactoring 好。人都是有惰性的,一旦实现了以后的事件,move on 之后再回来解决这些概率就十分小了,除非下次真的须要批改这些代码。如果说不会再回来,那么这个 TODO 也没有什么意义。如果真的须要,就不要留下这个问题。我见过有的人留下了一个 TODO,throw 了一个 not implemented 的 exception,而后几天之后其他同学把这个代码带上线了,间接挂掉的状况。尽量不要 TODO, 一次做好。
是否要写单元测试?
集体的观点是必须,除非你只是做 prototype 或者疾速迭代扔掉的代码。
Unit tests are typically automated tests written and run by software developers to ensure that a section of an application (known as the “unit”) meets its design and behaves as intended. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method.
From Wikipedia
单元测试是为了保障咱们写出的代码的确是咱们想要表白的逻辑。当咱们的代码被集成到大我的项目中的时候,之后的集成测试、功能测试甚至 e2e 的测试,都不可能笼罩到每一行的代码了。如果单元测试做的不够,其实就是在代码外面留下一些本人都不晓得的黑洞,哪天调用方改了一些货色,走到了一个不罕用的分支可能就挂掉了。我之前带的我的项目中就呈现过相似的状况,代码曾经上线几年了,有一次略微改了一下调用方的参数,感觉是个小改变,然而上线就挂了,就是因为遇到了之前基本没有人测试过的分支。单元测试就是要保障咱们本人写的代码是依照咱们心愿的逻辑实现的,须要尽量的做到比拟高的笼罩,确保咱们本人的代码外面没有留下什么黑洞。对于测试,我想独自开一篇探讨,所以就先简略聊到这里。
要写好代码的确是曾经十分不容易的事件,须要思考正确性、可读性、鲁棒性、可测试性、能够扩展性、能够移植性、性能。后面探讨的只是集体感觉比拟重要的入门的一些点,想要写好代码须要通过刻意地思考和练习能力真正达到目标!
首届云原生微服务大会
首届云原生微服务大会 PC 端地址:https://developer.aliyun.com/topic/microservices2020#/
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术畛域、聚焦云原生风行技术趋势、云原生大规模的落地实际,做最懂云原生开发者的公众号。”