选自medium
作者:Hüseyin Polat Yürük
机器之心整理
参与:Geek AI、淑婷、张倩
不想成为好程序员的码农不是好工程师。出色的码农都具备怎样的思维习惯?这里有 25 条成熟的小建议。
「即使进行小的软件变更也很困难!」
「进行变更会破坏软件的特性。」
「修复一个 bug 的同时又引入了一个新的 bug...」
「实现的是没有必要的代码。」
「代码太复杂了!几乎不可能向其中添加新的特性。」
「这产品永远不会交付不了了!」
「抛弃旧代码吧!重头开始写。」
你是否对上面的这些话感到熟悉呢?
世界上每个角落的开发者,每一分钟都在说着(或想着)这些,亚历山大到想哭!这是为什么呢?
这些都是开发者们经常讨论的常见问题。每个开发团队都经历过这些故事。
有许多小的因素会逐渐侵蚀开发人员的项目。它们不会立即造成破坏,而是日积月累,甚至一年或更长的时间内都看不出来。所以当有人指出这些因素时,通常听起来没有什么危害。
即使你开始实施,也可能看起来一切正常。但随着时间的推移,尤其是随着这些东西越积越多,情况也会变得越来越复杂,直到你成为这个「常见恐怖故事」的又一个受害者。
为了避免成为受害者之一,你应该牢记几条软件的基本定律。你应该培养一种开发者必备的思维模式。这种思维模式会帮助你在日常编程过程中做出更好的决定。你可以让你的软件尽可能的简单,防止它成为一个无法管理的复杂系统。
接下来,本文将列出 25 条开发者必须掌握的要点。
1. 构想软件的用途
首先,你需要明白软件的用途。也就是说,实际上所有软件的用途只有一个:帮助人们!
请记住:软件的用途并不是炫耀你有多聪明。
不能构想出软件用途的开发者将写出糟糕的软件。什么是糟糕的软件呢?就是那些对人们没什么帮助的系统。
在对软件做出决策的时候,你应该牢记下面的指导原则:我们如何才能为人们提供帮助?你甚至可以通过这种方式对软件特性的请求进行优先级排序。
2. 明确软件设计的目标
每个程序员都是一个设计师。
当难以创建或修改软件时,开发者往往会将大部分时间花在让软件「能用就行」,而不是关注如何帮助用户。
软件设计的目的是尽可能简化开发人员的工作,这样他们就可以专注于重要的事情。你将创建能够帮助用户的软件,并且你的软件将在很长一段时间内持续对用户提供帮助。
然而,如果你设计了一个糟糕的系统,你的软件生命周期将会很短。
这就向我们指明了软件设计最重要的目标:
设计出开发人员能够尽可能容易创建和维护的系统,从而使这些软件能够持续地为用户提供帮助。
所以设计时有两个关键点:方便自己,帮助他人。
3. 正确理解自己开发的东西
无法完全理解自己作品的开发者往往会开发出复杂的系统。这会招致一个恶性循环:对软件的误解会导致复杂性增加,反过来又会进一步加深对软件的误解,循环往复。
事实上,提升设计技能的最佳方法之一就是:保证你对开发的系统和工具有充分的理解。
对软件的理解程度是优秀开发者与蹩脚开发者之间最关键的差别。
蹩脚的开发者并不能理解他们所做的东西,而优秀的开发者则对此有很清晰的认识。就是这么简单。
4. 简洁
简洁是终极的复杂 ——达芬奇。
编程是化繁为简的艺术。「蹩脚的开发者」无法降低程序复杂性,而「优秀的开发者」会尽其所能简化其代码,使其易于被其他开发者理解。
一名优秀的开发者会创建一些易于理解的东西,这样就很容易发现所有的 bug。
现在的开发者都是聪明人,没有人喜欢被当做傻瓜来对待。具有讽刺意味的是,有时候这种心态却会导致他们创造出复杂的东西。他们大体上是这么想的:
「其它的开发者会能够理解我写的这些代码。所以我应该写出一些难以理解的绝妙代码,这样他们就会认为我很聪明。」
这是一种不良心态导致的错误,并不一定是由于缺乏编程技巧。大多数编程中的失败都是由这样的心态造成的。
炫耀你有多聪明并不能帮你写出好程序。
刚接触你的代码的开发者对这样的代码并没有什么了解,他们必须研究一阵子。
所以,你应该问自己:「我是想让人们理解我的代码并感到快乐,还是希望他们感到困惑和沮丧呢?」
事实上,如果其他阅读代码的开发者能够很容易地理解它,这说明你的代码写得很棒。
复杂与智慧无关,简单却与之相关。——Larry Bossidy
那么问题来了:「应该让代码有多简洁?」
你的答案应该是:「让它变成傻瓜式的代码,任何人都能读懂。」
5. 控制复杂度
控制复杂度是计算机编程的本质。—— Brian Kernighan
复杂性是许多软件失败的根源。一开始,你要做的可能是一个在一个月内就能完成的项目。
接着,你增加了程序的复杂性,使得这项任务需要三个月才能做完。
然后,你又开始加入了一些满足其它用途的新的特性。由于你无缘无故地扩展了软件的用途,这件事开始变得十分复杂,需要六个月才能完成。
但是,这还没完。
接下来,你考虑了每个特性并让整个软件变得更复杂,导致软件需要九个月才能完成。然后,由于代码的复杂度提高,又引入了许多新的 bug。
自然而然地,你开始着手修复这些 bug,然而你并没有考虑到这些修复对程序的其余部分有何影响。
最终,即使是很小的更改也会变得十分困难。当 bug 修复开始引入新的 bug 时,你将不得不面对最流行的编程恐怖故事:从头开始重写代码!
那么,你是如何成为这个恐怖故事的受害者的呢?或者说,更多人应该关心:我们如何才能避免成为这个受害者?
其实很简单。首先,你需要确切地弄清楚你软件的用途及其定义。其次,你需要使你所编写的每段代码尽可能简洁。第三,当一个新的特性或变更请求出现在讨论表中时,你需要基于你软件的用途对它们进行评估,并提出问题。
作为一名开发者,你的第一反应应该是抵制(不必要的)软件变更。这将防止你向软件中添加不必要的代码。当你确信这项变更是必要的时,你可以去实现它。
有许多因素会提高软件的复杂度,但本文提到的这些是最常见的因素。除此之外,你还应该遵循一条原则:你的主要目的是降低复杂度,而不是增加复杂度。
6. 维护
维护是软件开发中最重要的事情之一。很遗憾,开发人员通常会忽略它的重要性。快速编码和快速交付看起来比代码维护更重要。这就是错误的所在——忽视未来的代码维护。
总有一些变更需要实现。不仅必须实现,你还要随着时间的推移维护它们。作为开发人员,考虑未来变更的维护是你的主要职责之一。
所有的变更都需要维护。
简洁性和复杂性是影响代码维护的两个主要因素。任何软件的易维护性都与其各个部分的简洁性成正比。维护的工作量与软件的复杂度成正比。
关于维护,你应该遵循的一条原则是:减少维护的工作比减少实现的工作更重要。
7. 一致性
一致性是简洁性的重要组成部分。如果你在某处用一种方式做了某件事,那么你应该在所有的地方都用这种方法做这件事。例如,如果你将一个变量命名为「thisIsVariable」,那么你所有的变量都应该以这种方式命名(「otherVariable」、「anAnotherVariable」等。而不是「other_variable」)。
缺乏一致性的代码很难被理解。不要让开发者每次阅读一段来自你的系统的新代码时,都要重新学习你系统的规则。
在所有的团体运动中,最好的队伍都是一致的,并产生化学反应。——Roger Staubach。
8. 优先级
你应该如何为你的软件做出决策呢?
当你面临许多可能的方向时,你如何决定哪种选择是最好的?你应该关注什么?应该实现哪些特性?
要回答这些问题,有三个重要的因素可以帮助你做出更好的决策:
变更的意愿(D):你有多想做出该变更?
变更的价值(V):该变更带来了多少价值,或者说对你的用户有多大帮助?
执行变更所需要的工作量(E):变更需要做多少工作?
计算的公式很简单:D=V/E
任何变更的意愿都直接与变更的价值成正比,与进行变更所需的工作量成反比。
当你对工作进行优先级排序时,应该遵循以下的原则:那些为你带来很大价值而需要的工作量很少的变更,要比那些没有价值却需要付出很多努力的变更要好!
9. 解决问题
第一步是理解。你需要清楚地知道要求是什么。大多数难题之所以难,是因为你不理解它们。把你的问题写下来,试着向别人解释。
如果你不能用简单的语言来解释某件事,你就没有理解它。——理查德·费曼
第二步是计划。不要马上开始行动,稍微停一下。给你的大脑一些时间来分析问题和处理信息,但不要花太多时间在计划上。
三思而后行。
第三步是分而治之。不要试图一次解决一个大问题。当你从整体上看问题时,你会感到害怕。把它分解成更小的任务,逐个解决每个子问题。一旦你解决了每个子问题,你就可以把这些点串联起来了。
10. 够用即可
「完美是良好的敌人。」——Voltaire
无论是创建一个新项目,还是向现有的系统添加一个新特性,开发者都倾向于从一开始就对所有事情进行详细的规划。
他们希望第一个版本是完美的。他们并不关注将要解决的问题以及他们的软件将如何帮助人们。他们从能想到的每一个小细节开始。接着会进行假设和预测,然后他们会想「如果... 会怎么样?」
他们不得不预测未来,因为他们现在是如此着迷于脑海中想象出的项目,他们的项目必须像他们想象的那样完美。
事实上,他们并不知道等待他们的是什么,也不知道追求完美会让他们付出多少代价。
让我告诉你会发生什么吧:
你将写出一些实际上并不需要的代码
你将因为加入了不必要的代码而增加复杂度
你将会焦头烂额
你将错过 deadline
你将处理由于高复杂度引起的许多 bug
你想让这一切发生吗?我想不会吧。
那么你应该怎么做呢?
从小的开发原型做起,不断改进它,然后进行扩展。
你应该将增量开发作为行动指南。你可以带着这种思想通过下面的步骤设计一个计算器:
计划做出一个只做加法而不做其它事情的系统。
实现它。
改进现有系统的设计,这样你可以加入其它的操作。
计划减法并重复步骤 2 和 3。
计划乘法并重复步骤 2 和 3。
计划除法并重复步骤 2 和 3。
11. 预测
「预测就是简单地预想未来会发生什么事情。这可能是真实的,基于某种客观数据,也可能是基于假设」。
想到他们的代码可能会在未来进行变更,一些开发者试图通过设计通用的解决方案来解决这个问题,他们相信这个解决方案能够适应未来所有可能的情况。
软件过于通用意味着会写出很多没有必要的代码。
你无法预测未来,因此,无论你的解决方案多么通用,它都不足以满足你未来的实际需求。最有可能的是,你所预测的情况永远不会到来,而你为解决未来问题而编写的代码将增加复杂度,使变更代码片段变得困难,最终它将成为一种负担,可能会毁掉你的软件。
不要预测未来。只要实现你现在应该实现的功能即可。
12. 假设
什么是假设?
「假设是某些你接受为真或相信为真的事,尽管你没有确凿的证据。」
假设是软件项目的一大杀手。让我们看看假设是如何扼杀一个软件项目的。
开发者知道他们必须开发一个系统来做 X。然后他们认为系统将来会要求他们去做 Y,然后他们去实现 Y。于是他们就写了数千行代码来设计 Y。
到了后来,开发者会认识到当前的需求与他们所想的完全不同。但现在,该软件有了一些不必要的代码。这些代码很难扔掉,因为所有的东西都是相互交织的。重构代码需要花费几个月的时间,而现在他们认为从头开始重写整个软件将导致他们浪费几个月的时间。
为了避免成为这样的开发者,你应该遵循这个原则:基于你现在所知道的事情设计代码,而不是你认为将来会发生的事。
13. 不要重造「轮子」
举个例子,假如现在已经有一个很完美的垃圾回收器了,你还想自己发明一个,那么你将花费大量的时间来开发这个垃圾回收器,而你本来可以只专注于开发你的软件。
只有当下面的任何一种情况成立,你才应该重新去发明「轮子」:
你需要一些还不存在的东西。
所有现有的「轮子」都太糟糕了,或者都无法满足你的需求。
现有的「轮子」没有得到适当的维护。
你要遵循的简单原则是:尽量不要重新发明「轮子」。
14. 学会拒绝
作为开发人员,你对于变更申请的第一反应应该是「不!」
永远要学会抵制添加更多的代码、更多的特性,直到你确信它们是必需的,并且有必要实现他们。因为不必要的变更会增多软件中的缺陷。
你怎么知道它们是必需的呢?
请回顾并牢记软件的用途。然后记住「优先级排序」!
15. 自动化
不要把时间浪费在重复的劳动上。将这些任务设置好,然后忘掉它们。它们可以在你睡觉的时候工作。
当你发现自己在一次又一次地做某件事时,尽量把它自动化!
16. 代码衡量
根据代码行数来衡量编程进度就像用重量来衡量飞机建造进度一样。——比尔盖茨
我看到有些开发者根据代码的行数来衡量他们的软件质量。他们认为代码行越多意味着他们做得越好。他们的软件包含数十万行代码,就意味着他们开发的软件非常庞大。
那么问题来了:它真的有那么大吗?还是哪里出了点问题?
最有可能的答案是:他们的设计有问题。大多数简单的解决方案不需要很多代码。你可以使用少量代码实现软件的简洁性并解决问题。
我并不是说代码越少越好。当你想避免上述状况而使用更少的代码时,很容易掉进「陷阱」中,然后写出一些别人难以理解的「聪明」代码。你应该找到一个平衡点。
最好的代码是一小段易于理解和阅读的代码。
17. 工作效率
你如何衡量自己的工作效率?
通过编写更多行的代码?还是通过扔掉数百行代码?
你的主要目标应该是让代码库尽可能的小,问题不在于「如何编写更多代码?」而应该是「如何删除更多代码?」
「我工作效率最高的一天扔掉了 1000 行代码。」——Ken Thompson
18. 代码测试
什么时候应该将日志和错误处理记录添加到项目中?
你应该在很早的阶段就添加日志。这将帮助你轻松地找到问题并节省你的时间。
我发现在代码测试阶段,容易发现很多错误。举个例子,有两个条件,一个简单的 if-else 代码块。开发者将输入传给软件,软件将进入 if 代码块中,开发人员对其进行了测试,并将代码提交给源代码管理。这个部分的测试就完成了!
但是其它程序块呢?当软件被交付到生产环境中时,会导致很多错误。当你测试代码时,必须至少执行一次所有的新代码行,并且应该在进行整体测试之前测试对每部分进行测试(执行集成测试和系统测试之前进行单元测试)。
当你要处理一个 bug 时,首先你应该重现它。你不应该猜测错误的来源并给予你的假设应用补丁。你很可能想错了。在应用补丁之前你应该亲眼看到这个 bug。
你应该要靠谱一些。当团队中的其他开发者看到你向版本控制工具提交了新代码时,每个人都应该明白你的代码已经经过了测试,能够正常工作。
没有经过测试的代码是不能正常工作的代码。
19. 低估开发难度
开发者不太擅长估计软件的开发难度。
通常,他们会低估而非高估事情的难度。他们会低估开发少量代码或特性所需的时间和工作量。最终,这种低估会导致他们错过 deadline。
这个问题的解决方案是:把大项目分解成多个小项目,事情越小就越好估计。你可能仍然会出错,但是你所犯的错误会比估计一个大型项目时要少得多。
请记住:每件事所花的时间都比你想象得更长。
20. 远离重写代码
我相信,如果你接受了上面提到的软件开发基本原则的话,你不会走到这一步。然而,如果你不小心犯了这些错误,并在考虑重写代码的话,那么你要知道的一件事是:重写代码通常是开发人员的错觉,在大多数情况下这并不是正确的解决方案。
为什么这是一种错觉?
因为读代码比写代码难。这就是为什么重用代码如此困难的原因,也是为什么当我们读到其他开发者写的代码时,我们的潜意识会悄悄告诉我们「把它扔掉,重新写」。
在很多情况下,你可能会考虑从头开始重写代码。但是,给你条简单的建议:重构应该是第一选择。
21. 文档和注释
关于注释的一个常见误解是:开发者应该添加注释来说明代码的作用。这是错误的!从代码中就可以明显看出它在做什么。如果这一点不明显,说明代码的可读性很差,你要让代码变得更简单。
当你不能让代码变得更简单时,你应该添加注释来解释这种复杂性。
注释的真正目的是解释「为什么」要做某事,而不是代码「在做什么」。如果你不解释这一点,其他程序员可能会感到困惑,所以当他们变更你的代码时,可能会删除其中的重要部分。
因此,写一条注释来解释「为什么」,而不是解释「是什么」。
另一件需要注意的事是撰写文档。用文档来解释你的软件架构、每个模块和组件很重要。这是从高层次来查看软件所必需的。当一个新的开发者加入你的团队时,他将更容易理解整个软件。
当开发者对软件的其他部分一无所知时,他们很容易在自己负责的部分犯错误,这也会影响其他部分。
22. 选择技术(工具,程序库,等等)
首先,你要牢记这条规则:不要过于依赖外部技术或尽可能减少对它们的依赖。
这是为什么呢?因为它们是复杂性的另一个常见来源。它们会扼杀你的积极发展,让一切变得更加困难。当你严重依赖于外部技术时,你就不是自由的。
如果该技术存在重大缺陷怎么办?你必须等待开发人员来修复这个 bug,如果这个技术是你项目的核心,你基本上就会卡在这里,无法继续工作。因此,为你的项目选择正确的技术非常重要。
在开始使用某些技术之前,你应该考虑以下几个因素:
它的发展前景如何?
它会被继续维护下去吗?
换掉它容易吗?
技术社区对它看法如何?
如果你能找到这些问题的正确答案,就能降低选择错误技术的风险。
23. 提升自我
保持学习的状态。尝试不同的编程语言和工具,阅读软件开发方面的书籍。它们会为你提供另一种视角。每天的小小进步都会让你的知识和技能产生质变。
要有开放的心态。不要痴迷于一种技术。使用所需的技术去解决特定的问题。不要参与没意义的讨论,比如「微软和 Linux 哪个好用?」
要知道每个特定的问题都有特定的解决方案
24. 不要逞英雄
很多时候,及时放弃比逞英雄要好。
比如说,你觉得某个任务你能在两个小时之内完成。但是四个小时过去了,你仍然只完成了四分之一。
你的本能应该是这样想的:「我现在还不能放弃啊,我已经在这上面花了四个小时了!」
所以你开始逞英雄,下定决心要完成它(但尴尬的是,但仍然没有成功)。于是,你开始闭门造车。
不要过于偏执!要学会及时止损。不要羞于求助。
25. 不要立马提问,适时寻求建议
当你要实现某些东西,但又不确定解决方案时,不要去问别人怎么做,至少不要马上去问。相反,去尝试任何你能想到的办法。当你对某种概念或语言越不熟悉时,这一点越重要。
当你自己不能想通任何问题的时候,去搜索!找出答案并试一试。修改这些答案,看看你是否能够理解它们为什么起作用,并改写它们,让它们适应自己的代码。
但同时也一定要学会寻求建议。
当你尝试了所有方法,特别是有了一个可行的解决方案之后,就是你寻求建议的最佳时机了。
你可以请同行和高级开发者帮你检查代码。
原文链接:https://medium.freecodecamp.org/learn-the-fundamentals-of-a-good-developer-mindset-in-15-minutes-81321ab8a682
本文为机器之心编译,转载请联系本公众号获得授权。
✄------------------------------------------------
加入机器之心(全职记者 / 实习生):hr@jiqizhixin.com
投稿或寻求报道:content@jiqizhixin.com
广告 & 商务合作:bd@jiqizhixin.com
1、头条易读遵循行业规范,任何转载的稿件都会明确标注作者和来源;
2、本文内容来自“机器之心”微信公众号,文章版权归机器之心公众号所有。