免责声明:本文来自微信公众号InfoQ(ID:infoqchina),作者:Julio Biason,Hyun-woo Technology转载授权发布。 本文的作者Julio Biason自1990年以来一直从事软件开发。以下是他过去30年在软件开发方面的一系列冷笑话。 1首先是关于软件开发规范,然后是代码。 在您知道要解决什么问题之前,请不要编写代码。 Louis Srygley说:“如果没有任何要求或设计,编程将成为在空文本中添加错误的艺术。” 有时,只需简短的一两个描述就足以说明程序的用途。 每当您停下脚步,看一下代码,然后开始考虑下一步该怎么做,通常是因为您不知道下一步该怎么做。 目前,您需要做的是与同事讨论,或者您可能需要重新考虑以前的解决方案。 写下注释形式的实施步骤。 如果您不知道从哪里开始,请首先用英语(或您的母语)写下程序流程,然后在注释中添加代码。 您还可以将每个注释视为一个函数,并在代码中实现它们。 良好使用Gherkin Gherkin是一种测试DSL,用于描述“系统处于某种状态,如果发生某种事件,则这是期望的状态”。 即使您不使用测试工具,Gherkin仍然可以帮助您更好地了解从该程序中可以获得什么。 单元测试还不够,最好有集成测试。 在我目前的工作中,我们将进行模块和类级别的测试。 这些测试可以让我们知道模块或类的行为,但不能让我们知道整个系统的行为,而集成测试可以告诉我们这一点。 测试使API更加强大。 代码是分层的:存储层负责数据的持久性,处理层负责转换存储的数据,视图层负责呈现数据,等等。分层测试该测试可以使您更好地了解每一层的API,并知道如何更好地调用每一层:API是否过于复杂? 要进行简单的通话,您是否需要保留大量数据? 通过命令行运行测试用例对于任何项目,“命令行”都非常重要。 在知道如何使用命令执行测试用例之后,您可以执行自动化测试,然后将它们集成到持续集成工具中。 准备丢弃该代码。 许多人开始使用TDD时会感到烦恼,因为他们可能需要重写很多代码,包括已经编写的代码。 这就是TDD的“设计理念”:随时准备丢弃您的代码。 随着问题研究的不断深入,您将越来越了解要解决的问题。 无论您之前编写了什么代码,它们毕竟不是解决问题的最终方法。 但请放心,代码不是一堵墙,如果将其丢弃,也不会浪费砖头。 但是花在编写代码上的时间确实将永远消失,但是作为回报,您将对问题有更好的理解。 测试框架附带了一种好的编程语言。 可以肯定地说,如果一种编程语言的标准库附带了一个测试框架,即使该框架很小,其生态系统也会比那些没有提供测试框架的编程语言更好。 好的测试,即使外界为这些语言提供了很好的测试框架。 思考时间过长是浪费。 有时,当程序员解决问题时,他会尽力找到解决所有问题的方法,包括将来可能出现的问题。 但是事实是,将来的问题可能永远不会出现,并且您必须维护很多将来可能永远不会使用的代码,甚至重写所有代码。 这些问题需要一一解决。 解决紧迫的问题后,再解决下一个问题。 在某些时候,您可能会在解决方案中找到某些模式,这些模式是解决“所有问题”的最佳方法。 编写文档实际上会在将来对您有所帮助。 每个人都知道编写函数,类或模块的文档很麻烦,但是将来也可以为您省去很多麻烦。 文件是合同。 该代码的文档实际上是一个合同:文档中写的内容以及此函数的作用什么。 如果以后发现代码与文档不匹配,则说明代码有问题,而不是文档有问题。 如果一个函数具有“和”逻辑,那么肯定有问题。 函数只能做一件事。 在记录功能时,如果发现需要使用“和”逻辑,则意味着该功能可以完成多项工作。 这时,您需要将函数拆分为多个,并且不要在文档中显示“与”逻辑。 不要将布尔类型用作参数。 程序员通常在设计函数时喜欢将布尔类型添加到参数列表中,但是请不要这样做。 例如:假设您有一个消息系统和一个函数,此函数将所有消息返回给用户,称为“ getUserMessage”。 然而,有时用户需要得到整个消息,并且有时仅该消息的摘要(例如消息的第一段)。 因此,您添加了一个名为“ retrieveFullMessage”的布尔参数。 同样,最好不要这样做。 因为当其他人看到诸如“ getUserMessage(userId,true)”之类的代码时,他们可能不知道“ true”的含义。 您可以添加两个函数“ getUserMessageSummaries”和“ getUserMessageFull”,然后让这两个函数分别调用“ getUserMessage”,并将true或false传递给它,以确保外部接口清晰可见。 修改接口时要小心。 上面提到的重命名功能。 如果调用该函数的代码完全在您的控制之下,那么这样做就没有问题。 您只需要找出需要修改的位置,然后进行更改即可。 没关系。 但是,如果重命名的函数作为库的一部分暴露在外部,则不能随意对其进行修改。 因为这样做会影响所有调用该函数的代码,并且这些代码不受您的控制,所以修改函数名称只会给这些代码的所有者带来大麻烦。 您可以添加一个新功能,然后将旧功能标记为已弃用。 在发布某些版本后,可以慢慢删除旧功能。 集成的文档随附了一种好的编程语言,如果编程语言提供了有关函数,类和模块的文档,或者使用文档的生成方式,那么可以确保该语言的函数,类,模块,库和框架也将具有良好的文档(即使不是最佳文档,但肯定也不错)。 相反,不提供集成文档的编程语言通常只有较差的文档。 选择编程语言时,不要只看语言本身。 编程语言是解决问题的强大工具,但不仅限于此:它还具有构建系统,依赖管理系统,工具,库和框架以及社区...选择编程语言时 ,不仅仅是因为它易于使用。 请记住,您可以将一种语言的语法认为很简单,但是您还需要考虑社区因素。 有时候,让程序崩溃比不执行任何操作更好。 这句话听起来有些奇怪:与其抓住错误而不执行任何操作,不应该抓住错误,这是更好的选择。 在Java中,人们经常这样做:这几行代码除了打印出异常外什么都不做。 如果您不知道如何处理它,不妨将其丢弃,以便至少可以知道何时会发生此类异常。 如果您知道如何处理异常,则应对它们。 这与上一个相反:如果您知道何时将抛出异常,错误或返回结果,并且知道如何处理它们,那么就对其进行处理。 您可以显示错误消息,尝试将数据保存在某处,将用户的输入写入日志文件,然后稍后再处理。 类型系统将告诉您数据的外观。 内存只是一系列字节,字节只是0到255之间的数字。这些数字的含义需要由编程语言的类型系统来解释。 例如,在C语言中,“ char”类型的65实际上是字母“ A”,而“ int”类型的65实际上是数字65。在处理数据时,请记住这一点。 以下是我最近看到的一些JavaScript代码。 某些人使用此方法确定True的值,但这显然是错误的。 如果数据具有模式,则使用结构来维护数据模式。 您可以使用列表(或元组)来存储简单数据,例如只有两个字段的数据。 但是,如果数据具有模式,即具有固定格式是的,那么您应该使用适当的结构来维护数据模式,例如使用结构或类。 停止盲目跟随趋势“盲目跟随趋势”的意思是:如果有人这样做,那么我们也可以这样做。 在大多数情况下,盲目跟随趋势是解决问题的最简单方法。 “如果一家大公司以这种方式保存数据,那么我们也可以这样做。” “如果有大公司支持,那将是一件好事。” “正确的工具”可能只是个人喜好。 “正确的工具”最初应指使用正确的工具完成任务,例如,使用合适的编程语言或框架来替换当前使用的语言或框架。 但是,每当我听到有人提到此声明时,他们只是想用自己喜欢的语言或框架替换真正合适的语言或框架。 “正确的工具”不一定是正确的工具。 假设您的项目需要处理一些文本,您可能会说:“让我们使用Perl”,因为您知道Perl非常适合处理文本。 但是您错过了一点:您周围的人只懂C语言,而不懂Perl。 当然,如果这个项目只是一个很小且无关紧要的项目,则可以尝试Perl,但是如果这个项目对公司很重要,那么最好使用C语言。 除项目外,请勿修改其他内容。 有时人们会直接修改外部工具,库或框架,例如直接修改WordPress或Django的代码。 这很快使项目难以维护。 当外部工具发布新版本时,您必须同步更改,但是很快您会发现先前修改的内容对于新版本不再有效,因此您只能保留旧版本,而旧版本可能具有 许多错误。 数据流比设计模式要好如果您知道数据如何流经系统,则可以编写更好的代码,这比应用各种设计模式要好得多(这只是个人观点)。 设计模式描述的是解决方案,而不是解决方案。 同样,这只是我个人的看法。 大多数时候,人们会采用设计模式并尝试通过设计模式找到解决方案,但结果是他们必须对解决方案(甚至是问题本身)进行调整以适应设计模式。 这样的我看到了很多东西:首先遇到问题,然后找到接近解决方案的设计模式,然后开始应用设计模式,然后向解决方案中添加很多东西以匹配设计模式。 学习基本的函数式编程您不必一定是函数式编程专家,但请记住,有时您需要保持数据不变。 使用新值创建新元素。 如果可能,不要让函数或类具有内部状态。 认知成本是代码可读性的杀手。 “认知冲突”是“需要同时记住两件(或更多件)事物以帮助您理解事物”的另一种表达方式。 同时记住不同的事物会加重大脑的负担,并削弱事物的相关性(因为您需要记住更多的事物)。 例如,通过添加布尔类型来判断True值的数量是轻微的认知冲突。 假设有一个“ sum()”函数,您将一眼就知道该函数用于计算列表中所有值的总和。 但是我已经看到人们使用此函数对布尔列表中的True值进行计数,这非常令人困惑。 魔术数字7魔术数字7(https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two)解释了人类可以同时记住多少件事。 如果您有一个函数,则此函数调用第二个函数,第二个函数调用第三个函数,第三个函数调用第四个函数,第四个函数调用第五个函数,第五个函数调用第六个函数,最后 您会发现这样的代码可读性很差。 甚至更进一步,您获得了一个函数返回的结果,将其传递给第二个函数,然后获取了第二个函数返回的结果,将其传递给第三个函数,然后以这种方式重复...但是问题是: 如今,人们更多地谈论魔术数字4而不是7。 考虑使用函数组合(首先调用第一个函数,然后调用第二个...),而不是使用函数调用(第一个调用第二个,第二个调用第三个...)。 尽管捷径不错,但好处是短暂的。 有许多编程语言,库或框架可帮助您简化代码并减少输入少。 但是采用快捷方式将在将来给您带来更多麻烦,甚至使您不得不重用复杂的代码而不是简单的代码。 因此,在采用捷径之前,请先了解它们。 您不必先编写复杂的代码,然后使用快捷方式进行简化:您可以采用快捷方式,但是您必须知道采用快捷方式可能导致的后果,或者知道如何在不采用快捷方式的情况下实现代码。 抵制“简单” IDE的诱惑,为我们提供了许多自动完成功能,以便我们可以更轻松地构建项目,但是您知道它背后发生了什么吗? 您知道构建系统如何工作吗? 如果没有IDE,您将知道如何构建项目? 如果您不使用自动完成功能,您还记得这些功能的名称吗? 因此,我们必须对这些背后的事物感到好奇。 将时区带入日期处理日期时,请记住带时区。 由于计算机或服务器的时区可能不正确,因此在对问题进行故障排除时可能会浪费大量时间,因为接口返回的时区不正确。 始终使用UTF-8。 在对字符进行编码时,日期也会遇到类似的问题。 因此,字符串始终会转换为UTF8格式,并以UTF8格式存储在数据库中。 API返回的字符串也使用UTF8格式。 极简主义摆脱IDE可以从“极简主义”开始:仅使用具有代码突出显示功能的编译器和编辑器,并使用它们来构建和运行代码...但是这样做并不容易。 但是,当再次使用IDE时,按下这些按钮后,您将知道IDE会做什么。 日志用于记录事件,不需要显示给用户。 很长一段时间以来,我一直在通过日志告诉用户系统发生了什么。 但是实际上,我们可以使用标准输出告诉用户系统中发生了什么事件,使用标准错误输出告诉用户系统中发生了什么错误,然后使用日志记录事件以方便 这些事件的后续分析。 您可以将日志视为将来需要从中提取信息的数据。 它们不是面向用户的,因此它们不必是人类可读的。 调试器被高估了。 许多人认为代码编辑器没有调试功能。他们都不是好编辑。 但是,一旦代码进入生产环境,您就不会使用调试器,即使您最喜欢的IDE也不会使用,但是日志无处不在。 您可能不知道发生故障时发生了什么,但是您可以在日志中找到原因。 我并不是说调试器是无用的,但它没有大多数人认为的有用。 您必须使用版本控制系统您可以说“我只是想使用这个项目来学习一些东西”,但这不能成为不使用版本控制系统的理由。 如果从一开始就使用版本控制系统,则在出现问题后可以轻松回滚。 一项更改对应一项提交。 我经常在代码提交中看到诸如“修复问题#1,#2和#3”之类的消息。 除非这三个问题是重复的(应关闭其中两个),否则应以三份意见书的形式提交,每份意见书都对应一个问题。 如果代码更改太多,则可以使用“ git add-p”。Git允许用户通过“ -p”参数进行部分提交,即选择仅提交部分代码更改,并将其余部分保存为 以后提交。 “根据数据或类型而不是功能来组织代码。许多项目的代码结构类似于以下内容:也就是说,它们基于函数组织代码(所有模型都放在同一目录中,所有过滤器都放在另一个目录中。 目录),实际上是没有问题的,但是如果根据数据组织代码,则将项目拆分为小项目会更容易,这样您就可以分离每个模块,例如仅处理Data1 Module ,或仅处理Data2的模块。如果还有另一个需要处理Data1的项目,则可以重用Data1模块。创建一个公共库,我已经看到许多项目使用相同的单个大型代码库,而不是这样做,为什么不这样做 使用将公共部分提取并制成公共库,然后在各个项目中引用这些库?在学习使用监视之前,为了了解系统的行为,我将向系统添加很多指标。 习惯了这些,如果系统不监控,我只是感觉很奇怪。 仅发送简单的请求不足以判断系统是否正常。 尽早向系统添加监视,可以使您更好地了解系统的行为。 使用配置文件假设您编写了一个仅接受值作为参数的函数。 如果有两个值需要传递给它两次,则需要两次调用此函数。 您还可以使用一个配置文件,在两个配置文件中写入这两个值,然后运行该程序两次。 命令行选项非常有用将参数写入配置文件后,您还可以添加命令行参数以指定要使用的配置文件。 许多编程语言都提供了命令行参数解析器,可用于构建有用的命令行程序。 不仅功能组成,我们还具有程序组成。 Unix的理念是“程序只做一件事,而最终做事。” 您可以使用一个程序和多个配置文件,但是如果需要使用所有程序的运行结果,该怎么办? 您可以编写另一个程序,以将多次运行的结果合并为一个。 程序组合也可以从简单的程序组合模型开始,最终成为微服务体系结构,并且微服务体系结构需要服务之间的良好通信机制。 但是您不必担心问题,可以让程序通过文件进行通信,一个写入文件,另一个从文件读取。 了解网络通信机制后,请考虑其他更复杂的通信问题。 将代码优化工作留给编译器。 您希望获得更好的代码性能,因此始终希望在此处和此处进行一些优化。 但是您必须知道优化代码是编译器擅长的。 聪明的编译器甚至可以帮助您删除返回相同结果的代码。 您需要做的是如何更好地设计代码,而不是寻找改进现有代码的方法。 延迟评估很久以前,某些编程语言在使用表达式时而不是在出现时对表达式进行评估。 Lips很久以前就这样做了,现在许多编程语言都这样做了。
可能您还想看