作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
扎卡里·戈德堡的头像

Zachary Goldberg

曾任谷歌工程主管和腾讯常驻企业家, 扎卡里是一位经验丰富的专业人士,拥有高管级别的经验.

Expertise

Previously At

Google
Share

人类与计算机编程的艺术和科学搏斗了大约半个世纪. 与大多数艺术和科学相比, 计算机科学在很多方面还只是蹒跚学步, walking into walls, 被自己的脚绊倒, 偶尔还会把食物扔到桌子对面.

因为它相对年轻, 我认为我们还没有就“好代码”的正确定义达成共识, 随着这个定义的不断演变. 有些人会说“好的代码”是具有100%测试覆盖率的代码. 其他人会说它超级快,有杀手级的性能,可以在有10年历史的硬件上运行.

虽然这些都是值得称赞的软件开发人员的目标, 然而,我冒昧地提出另一个目标:可维护性. Specifically, “好代码”是指易于组织维护的代码(而不仅仅是作者维护的代码)!),并且会比它所写的sprint更长久. 以下是我在大公司和小公司担任工程师期间发现的一些事情, 在美国和国外, 这似乎与可维护性有关, “good” software.

永远不要满足于“可以工作”的代码.写出优秀的代码.

第一条:你希望别人的代码如何对待你,你就如何对待你的代码

我不是第一个认为你的代码的主要受众不是编译器/计算机的人, 但下一个要读的人, understand, maintain, 改进代码(六个月后不一定是你). Any engineer worth their pay can produce code that “works”; what distinguishes a superb engineer is that they can write maintainable code efficiently that supports a business long term and have the skill to solve problems simply and in a clear and maintainable way.

在任何编程语言中,都有可能写出好的代码或糟糕的代码. 假设我们判断一门编程语言的标准是它编写好的代码的便利程度(它至少应该是最重要的标准之一), anyway), 任何编程语言都可以“好”或“坏”,这取决于它是如何被使用(或滥用)的。.

许多人认为“干净”且可读的语言的一个例子是Python. 该语言本身强制执行某种程度的空白规则,并且内置的api丰富且相当一致. 也就是说,创造无法形容的怪物是可能的. For example, 可以定义一个类,并在运行时定义/重新定义/取消定义该类上的任何和每个方法(通常称为猴子补丁)。. 这种技术自然会导致最好的API不一致,最坏的情况是一个无法调试的怪物. 有人可能会天真地认为,“当然,但没有人这样做。!“不幸的是,他们有,而且不需要花很长时间浏览 pypi 在你遇到实质性(和流行)之前!)库(ab)广泛使用猴子补丁作为其api的核心. 我最近使用了一个网络库,它的整个API会根据对象的网络状态而变化. 想象一下,例如,打电话 client.connect() 有时得到一个 MethodDoesNotExist error instead of HostNotFound or NetworkUnavailable.

第二条:好的代码是容易阅读和理解的,无论是部分代码还是整体代码

好的代码易于阅读和理解, 部分地和全部地, 由他人(以及作者在未来)撰写, trying to avoid the “真的是我写的吗??” syndrome).

我说的"部分"就是这个意思, 如果我在代码中打开某个模块或函数, 我应该能够理解它的作用,而不必阅读整个其余的代码库. 它应该尽可能地直观和自我记录.

不断引用影响代码库其他(看似无关的)部分行为的微小细节的代码就像阅读一本书,你必须在每个句子的末尾引用脚注或附录. 你连第一页都看不完!

关于“局部”可读性的其他一些想法:

  • Well encapsulated 代码更容易读懂, separating concerns at every level.

  • Names matter. Activate 思考快与慢system 2 大脑形成思想并付诸实际的方式, 仔细考虑变量和方法的名称. 额外的几秒钟可以带来显著的好处. 一个命名良好的变量可以使代码更加直观, 然而,命名不当的变量可能会导致混淆.

  • 聪明是敌人. 当使用花哨的技巧时, paradigms, 或操作(如列表推导式或三元运算符), 小心地使用它们,使您的代码 more 可读,而不仅仅是更短.

  • 一致性是一件好事. 风格一致性, 包括如何放置大括号,还有操作, 大大提高了可读性.

  • 关注点分离. 一个给定的项目在代码库的不同点上管理着无数的局部重要假设. 将代码库的每个部分暴露给尽可能少的关注点. 假设您有一个人员管理系统,其中person对象的姓氏有时可能为空. 对于在显示person对象的页面中编写代码的人来说,这可能真的很尴尬! 除非您维护一本手册,说明“我们的代码库中有一些尴尬和不明显的假设”(我知道我没有) programmer 不知道姓氏可以为空,并可能会写代码与空指针异常在它的姓氏是空的情况下出现. 相反,使用经过深思熟虑的api和契约来处理这些情况,代码库的不同部分使用这些api和契约来相互交互.

戒律3:好的代码有一个经过深思熟虑的布局和架构,以使管理状态显而易见

State is the enemy. Why? 因为它是任何应用程序中最复杂的部分,需要非常慎重和深思熟虑地处理. 常见的问题包括数据库不一致, 局部UI更新,新数据不会到处反映, 无序操作, 或者只是考虑麻木的复杂代码,到处都是if语句和分支,导致难以阅读,甚至更难维护代码. 把国家放在一个非常小心的基座上, 并且在如何访问和修改状态方面保持高度一致和深思熟虑, 极大地简化了代码库. 一些语言(例如Haskell)在编程和语法级别强制这样做. 您会惊讶地发现,如果您有类库,您的代码库的清晰度可以提高多少 pure 不访问外部状态的函数, 然后是一小部分引用外部纯功能的有状态代码.

戒律4:好的代码不是重新发明轮子,而是站在巨人的肩膀上

在可能重新发明轮子之前, 想想你要解决的问题或者你要执行的函数有多常见. 有人可能已经实现了您可以利用的解决方案. 如果合适的话,花点时间考虑和研究这些选择.

That said, 一个完全合理的反驳论点是,依赖关系不是“免费”的,没有任何缺点. 通过使用第三方或开源库添加一些有趣的功能, 你正在做出承诺, 变得依赖, that library. That’s a big commitment; if it’s a giant library and you only need a small bit of functionality do you really want the burden of updating the whole library if you upgrade, for example, to Python 3.x? And moreover, 如果遇到错误或希望增强功能, 您要么依赖作者(或供应商)提供修复或增强, or, if it’s open source, 发现自己处于探索一个完全不熟悉的(潜在的)代码库的位置,试图修复或修改一个模糊的功能.

当然,您所依赖的代码使用得越好, 你就不太可能把时间花在维护上. 底线是,值得您自己进行研究,并对是否包括外部技术以及特定技术将为您的堆栈增加多少维护进行自己的评估.

下面是一些比较常见的例子,你可能不应该在你的项目中重新发明(除非这些是你的项目)。.

Databases

找出哪一个 CAP 您的项目需要,然后选择具有正确属性的数据库. 数据库不再仅仅意味着MySQL,你可以选择:

  • “传统”模式SQL: Postgres / MySQL / MariaDB / MemSQL / Amazon RDS等.
  • Key Value Stores: Redis / Memcache / Riak
  • NoSQL: MongoDB/Cassandra
  • Hosted DBs: AWS RDS / DynamoDB / AppEngine数据存储
  • Heavy lifting: Amazon MR / Hadoop (Hive/Pig) / Cloudera / Google Big Query
  • Crazy stuff: Erlang的Mnesia, iOS的Core Data

数据抽象层

You should, 在大多数情况下, 不要向您选择使用的任何数据库编写原始查询. 更有可能, 在数据库和应用程序代码之间存在一个库, 将管理并发数据库会话的关注点和模式细节与主代码分离. At the very least, 永远不要在应用程序代码中使用原始查询或内联SQL. Rather, 将其包装在一个函数中,并将所有函数集中在一个文件中,该文件的名称非常明显(例如.g., “queries.py”). A line like Users = load_users() ,就比……容易读得多 users = db.查询用户username, foo, bar, LIMIT 10 ORDER BY ID. 这种类型的集中化也使得在查询中保持一致的样式更加容易, 并限制在模式更改时更改查询的位置数量.

考虑利用的其他公共库和工具

  • 排队或Pub/Sub服务. 选择您的AMQP提供商,ZeroMQ, RabbitMQ, Amazon SQS
  • Storage. 亚马逊S3,谷歌云存储
  • Monitoring: Graphite/Hosted Graphite, AWS Cloud Watch, New Relic
  • 日志收集/聚合. Loggly, Splunk

Auto Scaling

  • Auto Scaling. Heroku, AWS Beanstalk, AppEngine, AWS Opsworks Digital Ocean

戒律5:不要越过小溪!

编程设计有很多好的模型, pub/sub, actors, MVC etc. 选择你最喜欢的,并坚持下去. 处理不同类型数据的不同类型的逻辑应该在代码库中物理隔离, 这种关注点分离概念和减少未来读者的认知负荷). 更新UI的代码应该与计算UI内容的代码在物理上不同, for example.

第六条:如果可能的话,让电脑来做

如果编译器可以捕获代码中的逻辑错误并防止任何不良行为, bugs, or outright crashes, 我们绝对应该好好利用这一点. 当然,有些语言的编译器比其他语言更容易做到这一点. 例如,Haskell就有着出了名的严格 compiler 这导致程序员花费大部分精力来编译代码. 一旦编译完成,“它就可以工作了”. 对于那些从未使用过强类型函数语言的人来说,这可能看起来很荒谬或不可能, but don’t take my word for it. Seriously, 点击其中一些链接, 完全有可能生活在一个没有运行时错误的世界中. 这真的很神奇.

Admittedly, 并不是每种语言都有适合自己的编译器或语法(或者在某些情况下没有)!)编译时检查. 对于那些不知道的人, 花几分钟研究一下你可以在项目中启用哪些可选的严格检查,并评估它们是否对你有意义. A short, 我最近使用的一些具有宽松运行时的语言的非全面列表包括:

Conclusion

这绝不是一个详尽的或完美的诫命清单,以产生“好”.e.(易于维护)的代码. That said, 如果我将来不得不选择的每个代码库都遵循这个列表中的一半概念, 我会有更少的白头发,甚至可能在我生命结束时多活五年. 我肯定会发现工作更愉快,压力更小.http://en.wikipedia.org/wiki/CAP_theorem

聘请Toptal这方面的专家.
Hire Now
扎卡里·戈德堡的头像
Zachary Goldberg

Located in 洛杉矶,美国

Member since April 20, 2016

About the author

曾任谷歌工程主管和腾讯常驻企业家, 扎卡里是一位经验丰富的专业人士,拥有高管级别的经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

Previously At

Google

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.