一个 30 年老程序员的修炼之道

一个 30 年老程序员的修炼之道

本文作者 Julio Biason 从 1990 年开始从事软件开发工作,以下是他从过去 30 年软件开发生涯总结出来的一系列冷笑话式的经验之谈。

关于软件开发

规范先行,然后才是代码

在知道要解决什么问题之前,请不要写代码。

Louis Srygley 说过:“如果没有需求或设计,编程就成了一门往空文本里添加 bug 的艺术”。

有时候,仅仅一两段简单的描述就足以说明一个程序是用来干什么的。

每当你停下来,看着代码,并开始思考下一步该做什么的时候,通常是因为你不知道下一步该做什么。这个时候,你需要做的是与同事讨论,或者可能需要重新思考之前的解决方案。

用批注的方式把实现步骤写下来

如果你不知道从哪里下手,先使用英语(或者你的母语)把程序的流程写下来,然后在批注中添加代码。

你也可以把每个批注当成是一个函数,然后用代码实现它们。

用好 Gherkin

Gherkin 是一种测试 DSL,用来描述“系统处于某种状态,如果发生某个事件,这就是所期望的状态”。即使你不使用测试工具,Gherkin 仍然可以帮你更好地了解你能够从程序中获得哪些东西。

单元测试还不够,最好还要有集成测试

在我目前的工作中,我们会进行模块和类级别的测试。这些测试可以让我们知道模块或类的行为,但无法让我们知道系统整体的行为——而集成测试可以告诉我们这些。

测试让 API 变得更健壮

代码是分层的:存储层负责数据持久化,处理层负责转换存储的数据,视图层负责呈现数据,等等……

分层测试可以让你更好地了解各个层的 API,知道如何更好地调用各个层:API 会不会太复杂了?要进行一次简单的调用,是否需要保留很多数据?

通过命令行运行测试用例

命令行对于任何一个项目来说都很重要。在你知道了如何使用命令来执行测试用例之后,就可以进行自动化测试,然后将它们集成到持续集成工具中。

做好丢弃代码的准备

有很多人在开始使用 TDD 时会感到很恼火,因为他们可能需要重写很多代码,包括已经写好的那些。

而这正是 TDD 的“设计哲学”:随时准备好丢弃你的代码。随着对问题研究的深入,你对要解决的问题越来越了解,不管之前写了怎样的代码,它们终究不是解决问题的最终方案。

不过你不用担心,代码并不是一堵墙,如果将它们丢弃掉,也算不上是浪费砖块。但花在写代码上的时间确实会一去不复返,不过换来的是你对问题更好的了解。

好的编程语言自带测试框架

可以肯定地说,如果一门编程语言的标准库自带了测试框架,哪怕这个框架很小,它的生态系统也会得到比那些不提供测试框架的编程语言更好的测试,即使外部为这些语言提供了很好的测试框架。

想得太长远是一种浪费

有时候,程序员在解决一个问题时会想方设法寻找可以解决所有问题的方法,包括那些可能会在未来出现的问题。

但事实是,未来的问题可能永远都不会出现,而你不得不去维护一大堆在未来可能永远都用不上的代码,甚至重写所有代码。

问题要一个一个解决,解决完眼前的,再解决下一个。到了某个时刻,你可能会从解决方案中找到某种模式,而这些模式才是用来解决“所有问题”的良方。

写文档其实是在善待未来的你

谁都知道,给函数、类或者模块编写文档是一件苦差事,但这也是在给未来的你省下很多麻烦。

文档就是契约

代码的文档实际上是一种契约:文档里怎么写的,这个函数就是用来做什么的。

如果后面你发现代码和文档不匹配,那么就是代码有问题,而不是文档有问题。

如果一个函数文档里出现了“和”逻辑,那就一定有问题

一个函数应该只做一件事情。在给函数编写文档时,如果你发现需要用到“和”逻辑,那说明这个函数所做的事情不止一件。这个时候需要把函数拆成多个,不要在文档里出现“和”逻辑。

不要将布尔类型作为参数

程序员在设计函数时通常喜欢在参数列表里添加布尔类型,但请不要这么做。

举个例子:假设你有一个消息系统和一个函数,这个函数将所有消息返回给用户,叫作“getUserMessage”。不过,有时候用户需要获取整条消息,有时候只需要获取消息概要(比如消息的第一段)。于是,你加了一个布尔类型的参数,叫作“retrieveFullMessage”。

再次提醒,最好不要这么做。

因为当别人看到“getUserMessage(userId, true)”这样的代码时,他们可能不知道“true”是什么意思。

你可以新增两个函数“getUserMessageSummaries”和“getUserMessageFull”,然后让这两个函数分别调用“getUserMessage”,并将 true 或 false 传给它,这样可以保证对外的接口是清晰的。

在修改接口时要小心

上面提到了重命名函数,如果调用函数的代码完全处在你的控制之下,那么这么做就没什么问题,你只需要把需要修改的地方找出来,然后改掉它们就可以了。

但如果被重命名的函数是作为库的一部分暴露给外部,那就不能随意修改了。因为这样做会影响到所有调用函数的代码,而这些代码不在你的掌控之下,修改函数名只会给这些代码的主人带来大麻烦。

你可以新增一个函数,然后把旧函数标记为已弃用。经过一些版本发布之后,就可以慢慢将旧函数去掉。

好的编程语言自带集成文档

如果一门编程语言为函数、类、模块提供了文档或者生成文档的方式,那么你就可以肯定,这门语言的函数、类、模块、库和框架也会有很好的文档(即使不是最好的,但肯定不会差)。

相反,不提供集成文档的编程语言通常只有糟糕的文档。

在选择编程语言时,不要只看语言本身

编程语言是你用来解决问题的得力工具,但不仅限于此:它还有构建系统,有依赖管理系统,有工具、库和框架,有社区……

在选择编程语言时,不要仅仅因为它用起来很简单。记住,你可以认为一门语言的语法很简单,但也需要考虑到社区因素。

有时候,让程序奔溃比什么都不做更好

这句话听起来有点奇怪:与其捕获了错误却什么都不做,还不如不捕获错误。

如果你不知道该怎么处理它,还不如把它抛出来,这样起码可以知道什么时候会出现这样的异常。

如果你知道怎么处理异常,那就处理好它

这与上一条刚好相反:如果你知道什么时候会抛出异常、错误或得到返回结果,并且知道怎么处理它们,那就处理好它们。可以把错误消息显示出来,试着把数据保存到某个地方,把用户的输入写入日志文件,等后面再回头来处理。

类型系统会告诉你数据长什么样子

内存里存的只不过是一系列字节,而字节只不过是从 0 到 255 的数字,这些数字的意义需要通过编程语言的类型系统来说明。

例如,在 C 语言中,“char”类型的 65 其实就是字母“A”,而“int”类型的 65 就是数字 65。

在处理数据时要牢记这个。

如果数据是有模式的,那就用合适的结构来保持数据的模式

你可能会用 list(或者 tuple)来保存数据简单的数据,比如只有两个字段的数据。

但如果数据是有模式的,也就是有固定格式的,那么就应该使用合适的结构来保持数据的模式,比如使用结构体或类。

停止盲目跟风

“盲目跟风”的意思是:如果有人这么做了,那我们也可以这么做。大多数时候,盲目跟风是解决问题最简单的方式。

“如果某个大公司是这样保存数据的,那么我们也可以这样”。

“如果有大公司撑腰,那它就是好东西”。

“正确的工具”可能只是个人喜好

“正确的工具”本来应该是指使用合适的工具来完成某个任务,例如,使用合适的编程语言或框架来代替目前使用的语言或框架。

但每当我听到有人提到这个说法时,他们只不过是想用他们喜欢的语言或框架来替代真正合适的语言或框架。

“正确的工具”不一定是正确的

假设你所在的项目需要处理一些文本,你可能会说:“让我们使用 Perl 吧”,因为你知道 Perl 很适合用来处理文本。

但你忽略了一点:你周边的人只懂 C 语言,不懂 Perl。

当然,如果这个项目只是个无关紧要的小项目,那么可以尝试使用 Perl,但如果这个项目对公司来说很重要,那么最好是使用 C 语言。

不要去修改项目以外的东西

有时候,人们会去直接修改外部的工具、库或框架,比如直接修改 WordPress 或 Django 的代码。

这样很快会让项目变得难以维护。当外部工具发布新版本时,你不得不去同步变更,但很快你会发现,之前修改的东西对新版本不再有效了,所以只能保留旧版本,而旧版本可能有很多 bug。

数据流胜过设计模式

如果你知道数据是怎么流经系统的,就可以写出更好的代码,这比应用各种设计模式要好得多(这只是个人观点)。

设计模式是用来描述解决方案的,而不是用来寻找解决方案的

同样,这也只是我的个人观点。

大多数时候,人们会应用设计模式,试图通过设计模式来找到解决方案,但结果是不得不对解决方案(甚至是问题本身)作出调整来匹配设计模式。

这样的事情我见得多了:先是遇到问题,然后找到一个接近解决方案的设计模式,接着开始应用设计模式,然后在解决方案里添加很多东西,让它与设计模式相匹配。

学会基本的函数式编程

你不一定要成为函数式编程专家,但请记住,有时候需要保持数据的不变性。使用新值来创建新元素,如果有可能,不要让函数或类拥有内部状态。

认知成本是代码可读性杀手

“认知冲突”是“需要同时记住两件(甚至更多)东西才能帮你理解事物”的另一种说法。同时记住不同的东西会给大脑增加负担,并且会削弱事物的相关性(因为你需要在脑子里保留更多的东西)。

例如,通过相加布尔类型来判断 True 值的个数就是一种轻微的认知冲突。假设有一个“sum()”函数,你一看就会知道这个函数是用来计算一个列表中所有数值的和。而我却见过有人用这个函数来计算一个布尔值列表中所有 True 值的个数,这样很让人感到困惑。

魔术数 7

魔术数 7 解释了人类在同一时间可以记住多少件东西。

如果你有一个函数,这个函数调用第二个函数,第二个函数又调用第三个函数,第三个函数又调用第四个函数,第四个函数又调用第五个函数,第五个函数又调用第六个函数,到最后你会发现这样的代码可读性很差。

或者更进一步,你拿到一个函数返回的结果,把它传给第二个函数,然后拿到第二个函数返回的结果,把它传给第三个函数,一直这样重复下去……

但问题是:

  1. 现如今,人们谈论更多的是魔术数 4,而不是 7;
  2. 考虑使用函数组合(先调用第一个函数,再调用第二个……)代替函数调用(第一个调用第二个,第二个调用第三个……)。

捷径虽好,但好处是短暂的

有很多编程语言、库或框架会帮你简化代码,减少输入。

但走捷径会在以后给你带来更多麻烦,甚至会让你不得不重新使用复杂的代码代替简单的代码。

所以,在采取捷径之前,要先了解它们。

你不一定要先写出复杂的代码,然后使用捷径来简化:你可以采取捷径,但一定要知道走捷径可能会导致什么后果,或者知道在不走捷径的情况下该如何实现代码。

极牛网精选文章《一个 30 年老程序员的修炼之道》文中所述为作者独立观点,不代表极牛网立场。如若转载请注明出处:https://geeknb.com/11400.html

(39)
打赏 微信公众号 微信公众号 微信小程序 微信小程序
主编的头像主编认证作者
上一篇 2019年11月28日 下午3:46
下一篇 2019年11月28日 下午6:54

相关推荐

发表回复

登录后才能评论
扫码关注
扫码关注
分享本页
返回顶部