好的代码的六诫(编写经受时间考验的代码)

本文概述

  • 诫命1:以你希望他人的代码对待你的方式对待你的代码
  • 诫命2:部分或全部内容易于阅读和理解
  • 诫命3:良好的代码应具有深思熟虑的布局和体系结构, 以使状态管理变得显而易见
  • 诫命4:良好的规范并不能重塑方向盘, 它屹立在巨人的肩膀上
  • 诫命5:不要越过溪流!
  • 指令6:在可能的情况下, 让计算机完成工作
  • 总结
大约半个世纪以来, 人类一直在努力应对计算机编程的艺术和科学。与大多数艺术和科学相比, 计算机科学在许多方面仍然只是一个蹒跚学步的孩子, 走进墙壁, 绊倒自己的脚, 偶尔在桌子上扔食物。
【好的代码的六诫(编写经受时间考验的代码)】由于其相对年轻, 我不认为我们对” 好的代码” 的正确定义还没有共识, 因为该定义正在不断发展。有人会说” 好的代码” 是具有100%测试覆盖率的代码。其他人会说它超级快, 性能惊人, 并且可以在使用了十年的硬件上运行。
尽管这些都是软件开发人员值得称赞的目标, 但是我冒昧地将另一个目标引入了可维护性。具体来说, “ 良好的代码” 是可以由组织(而不仅仅是其作者??!)轻松, 容易地维护的代码, 并且其寿命远不只是其编写的sprint。以下是我在我的发现中发现的一些内容在美国和国外的大公司和小公司担任工程师的职业似乎与可维护的” 良好” 软件相关。
好的代码的六诫(编写经受时间考验的代码)

文章图片
永远不要满足于” 工作” 的代码。编写高级代码。
鸣叫
诫命1:以你希望他人的代码对待你的方式对待你的代码 我不是第一个写此书的人, 你的代码的主要读者不是编译器/计算机, 而是下一个必须阅读, 理解, 维护和增强代码的人(不一定要从现在起六个月) )。任何物有所值的工程师都可以产生” 有效” 的代码;杰出工程师的不同之处在于, 他们可以有效地编写可维护的代码, 从而长期支持业务, 并具有简单, 清晰, 可维护的方式解决问题的技能。
在任何编程语言中, 都可以编写好代码或坏代码。假设我们通过一种程序语言对编写好的代码的便利程度(无论如何至少应该是最重要的标准之一)来判断它, 那么任何一种程序语言都可以根据其使用(或滥用)的方式来判断是” 好” 还是” 坏” )。
Python被许多人认为是” 干净” 且可读的语言。该语言本身强制执行一定级别的空白规则, 并且内置的API丰富且相当一致。也就是说, 有可能创造出难以言说的怪物。例如, 可以定义一个类, 并在运行时(通常称为猴子修补)定义/重新定义/取消定义该类上的任何方法。这种技术自然最多会导致API不一致, 最坏的情况是无法调试怪物。一个人可能会天真地想, “ 当然, 但是没有人这样做!” 不幸的是, 它们确实如此, 浏览pypi并不需要花费很长时间, 你会遇到大量(且很受欢迎)的库, 这些库广泛地使用了猴子补丁作为其API的核心。我最近使用了一个网络库, 该库的整个API取决于对象的网络状态。想象一下, 例如, 调用client.connect()有时会收到MethodDoesNotExist错误, 而不是HostNotFound或NetworkUnavailable。
诫命2:部分或全部内容易于阅读和理解 他人(以及将来的作者, 试图避免” 我真的写了吗?” 症候群)容易且部分或全部地阅读和理解了好的代码。
“ 部分” 是指, 如果我在代码中打开某些模块或函数, 那么我应该能够理解其功能, 而不必阅读整个代码库的其余部分。它应该尽可能直观和自我记录。
不断引用影响代码库其他(看似无关)部分行为的细节的代码就像读一本书, 你必须在每句话的末尾引用脚注或附录。你将永远无法浏览首页!
关于” 本地” 可读性的其他一些想法:
  • 封装良好的代码往往更具可读性, 从而在各个级别上分离出关注点。
  • 名字很重要。激活快速思考和缓慢思考的系统2方式, 大脑通过这种方式形成思想, 并将一些实际的, 仔细的思想放入变量和方法名称中。额外的几秒钟可以带来可观的收益。命名正确的变量可以使代码更加直观, 而命名错误的变量可能导致假冒和混乱。
  • 聪明是敌人。使用奇特的技术, 范例或操作(例如列表推导或三元运算符)时, 请小心使用它们, 以使你的代码更具可读性, 而不仅仅是缩短代码。
  • 一致性是一件好事。样式的一致性(无论是在放置括号方面还是在操作方面)都极大地提高了可读性。
  • 关注点分离。一个给定的项目在代码库的各个点管理着无数的重要本地假设。将代码库的每个部分都暴露给尽可能少的那些关注点。假设你有一个人员管理系统, 其中某个人员对象的姓有时可能为空。对于有人在显示人对象的页面中编写代码的人来说, 这可能真的很尴尬!而且, 除非你维护的手册” 我们的代码库具有笨拙且不明显的假设” (我知道我不知道), 否则你的显示页面程序员将不会知道姓氏可以为空, 并且可能会使用空指针编写代码出现姓氏为null的情况时出现异常。而是使用经过深思熟虑的API和合同来处理这些情况, 你的代码库的不同部分使用它们进行交互。
诫命3:良好的代码应具有深思熟虑的布局和体系结构, 以使状态管理变得显而易见 国家是敌人。为什么?因为它是任何应用程序中最复杂的部分, 所以需要非常仔细和周到地处理。常见的问题包括数据库不一致, 部分UI更新(新数据无法在任何地方反映出来), 乱序操作, 或仅凭数字麻木的复杂代码以及if语句和分支到处导致难以阅读甚至难以维护的代码。将状态放在要谨慎对待的基座上, 并且在访问和修改状态方面极为一致和谨慎, 极大地简化了代码库。一些语言(例如, Haskell)在程序和句法层面上强制执行此操作。如果你拥有不访问外部状态的纯函数库, 而又有很小一部分的有状态代码引用外部纯函数, 那么你会惊讶于代码库的清晰度可以提高多少。
诫命4:良好的规范并不能重塑方向盘, 它屹立在巨人的肩膀上 在重新发明轮子之前, 请考虑一下你要解决的普遍问题或你要执行的功能。可能有人已经实施了你可以利用的解决方案。如果可以的话, 请花点时间考虑和研究任何此类选项。
好的代码的六诫(编写经受时间考验的代码)

文章图片
也就是说, 一个完全合理的反驳是, 依赖并不是免费的, 没有任何缺点。通过使用添加了一些有趣功能的第三方库或开放源代码库, 你将致力于并依赖该库。这是一个很大的承诺;如果它是一个巨大的库, 而你只需要一点点功能, 那么你真的要在升级(例如升级到Python 3.x)时负担整个库的更新吗?此外, 如果你遇到错误或想要增强功能, 则可以依靠作者(或供应商)来提供此修复或增强功能, 或者, 如果它是开源的, 则可以自己探索(你可能完全不熟悉尝试修复或修改一些晦涩的功能。
当然, 你依赖的代码使用得越好, 你将花费更多的时间进行维护的可能性就越小。最重要的是, 你值得进行自己的研究, 并对是否包含外部技术以及该技术将为你的堆栈增加多少维护做出自己的评估。
以下是一些较常见的示例, 你可能不应该在现代项目中重新发明这些东西(除非这些是你的项目)。
资料库
确定你的项目需要哪个CAP, 然后选择具有正确属性的数据库。数据库不再只是意味着MySQL, 你还可以选择:
  • “ 传统” 架构的SQL:Postgres / MySQL / MariaDB / MemSQL / Amazon RDS等。
  • 关键价值商店:Redis / Memcache / Riak
  • NoSQL:MongoDB / Cassandra
  • 托管数据库:AWS RDS / DynamoDB / AppEngine数据存储
  • 繁重的工作:Amazon MR / Hadoop(Hive / Pig)/ Cloudera / Google Big Query
  • 疯狂的东西:Erlang的Mnesia, iOS的核心数据
数据抽象层
在大多数情况下, 你不应将原始查询写到你碰巧选择使用的任何数据库。在数据库和你的应用程序代码之间很可能存在一个库, 将管理并发数据库会话和模式细节的关注与你的主代码分开。至少, 在应用程序代码的中间绝对不应包含原始查询或SQL内联。而是将其包装在一个函数中, 然后将所有函数集中在一个文件中, 该文件确实很明显(例如” queries.py” )。例如, 像users = load_users()之类的行比users = db.query(“ 从用户中选择SELECT用户名, foo, bar LIMIT 10 ORDER BY ID” )更容易阅读。这种类型的集中化还使查询中具有一致的样式变得更加容易, 并且限制了架构更改时更改查询的位置数。
其他考虑利用的常见库和工具
  • 排队或发布/订阅服务。选择AMQP提供程序, ZeroMQ, RabbitMQ, Amazon SQS
  • 存储。亚马逊S3, 谷歌云存储
  • 监视:石墨/托管石墨, AWS Cloud Watch, New Relic
  • 日志收集/聚合。 Loggly, Splunk
自动缩放
  • 自动缩放。 Heroku, AWS Beanstalk, AppEngine, AWS Opsworks, Digital Ocean
诫命5:不要越过溪流! 有许多用于编程设计, 发布/订阅, 演员, MVC等的好模型。选择最喜欢的一个并坚持下去。应该在代码库中物理隔离处理不同类型数据的不同逻辑(同样, 关注点概念的这种分离和减轻未来读者的认知负担)。例如, 更新UI的代码在物理上应与计算UI内容的代码不同。
指令6:在可能的情况下, 让计算机完成工作 如果编译器可以在你的代码中捕获逻辑错误并防止不良行为, 错误或彻底崩溃, 我们绝对应该利用这一优势。当然, 某些语言的编译器比其他语言更容易实现。例如, Haskell具有著名的严格编译器, 导致程序员将大部分精力都花在了编译代码上。但是, 一旦编译, “ 它就可以工作” 。对于从未使用过强类型功能语言编写的人来说, 这似乎是荒谬的或不可能的, 但请不要相信我。认真地, 点击其中一些链接, 绝对可以在没有运行时错误的情况下生活。真的是那么神奇。
公认的是, 并非每种语言都有一种编译器或一种语法, 可以使自己(或在某些情况下!)进行大量的编译时检查。对于那些不需要的选项, 请花几分钟的时间来研究可以在项目中启用哪些可选的严格性检查, 并评估它们是否对你有意义。我最近在运行时间宽容的语言中使用的一些常见的简短简短列表包括:
  • Python:pylint, pyflakes, 警告, emacs中的警告
  • Ruby:警告
  • JavaScript:jslint
总结 这绝不是用于生成” 良好” (即, 易于维护)代码的命令的详尽列表或完美列表。就是说, 如果我以后不得不使用的每个代码库都遵循了该列表中的一半概念, 那么我的白发就会减少很多, 甚至有可能在我生命的尽头再增加五年。而且, 我当然会发现工作更有趣, 压力更少。

    推荐阅读