优秀代码的六大戒律:编写经得起时间考验的代码

您如何定义好的代码?是 100% 的测试覆盖率,还是与十年前的硬件向后兼容?我们可能还无法结束这场争论,但好的软件似乎总是符合代码的某些特定品质。

在本文中,自由软件工程师 Zachary Goldberg 向我们介绍了六个简单的想法,可以帮助您制作更好、更易于维护的软件。

Zachary 曾担任谷歌工程主管和腾讯入驻企业家,是一位拥有 C 级经验的资深专业人士。

人类与计算机编程的艺术和科学的斗争才刚刚开始大约半个世纪。与大多数艺术和科学相比,计算机科学在很多方面仍像个蹒跚学步的孩子,走路时会撞到墙上,被自己的脚绊倒,偶尔还会把食物扔到桌子上。

由于它相对较新,我认为我们尚未就“好代码”的正确定义达成共识,因为该定义仍在不断发展。有些人会说“好代码”是测试覆盖率为 100% 的代码。其他人会说它速度超快,性能极佳,可以在 10 年前的硬件上运行。

虽然这些都是软件开发人员值得称赞的目标,但我还是大胆地将另一个目标加入其中:可维护性。具体来说,“好代码”是指整个组织(而不仅仅是作者!)可以轻松、随时维护的代码,并且其寿命将超过编写它的时间。以下是我在美国和国外的大公司和小公司担任工程师期间发现的一些事情,它们似乎与可维护的“好”软件相关。

戒律 1:对待自己的代码,就像你希望别人对待你一样

你的代码的主要受众是下一个必须阅读、理解、维护和改进代码的人(六个月后不一定是你)。任何值得他们薪水的工程师都可以编写“有效”的代码;优秀工程师的区别在于,他们可以高效地编写可维护的代码,以长期支持业务,并有能力以简单、清晰和可维护的方式解决问题。

在任何编程语言中,都有可能编写出好代码或坏代码。假设我们根据编程语言编写好代码的便利程度来判断它(无论如何,这至少应该是首要标准之一),任何编程语言都可以是“好”或“坏”,这取决于它如何使用(或滥用)。

许多人认为“干净”且易读的语言的一个例子是 Python。该语言本身内置 API 丰富且相当一致。话虽如此,也有可能创造出无法形容的怪物。例如,可以定义一个类,并在运行时定义/重新定义/取消定义该类上的任何方法(通常称为 monkey patching)。这种技术自然会导致 API 不一致,最坏的情况是变成无法调试的怪物。人们可能会天真地认为,“当然,但没人这样做!”不幸的是,他们确实这样做了,而且在浏览pypi时,您很快就会遇到大量(且流行!)的库,它们广泛使用 monkey patching 作为其 API 的核心。我最近使用了一个网络库,其整个 API 会根据对象的网络状态而变化。例如,想象一下,调用client.connect()有时会出现错误MethodDoesNotExist / HostNotFound / NetworkUnavailable

戒律2:好的代码应该易于阅读和理解,无论是部分还是全部

好的代码易于被他人阅读理解部分或全部内容(将来的作者也可以轻松阅读和理解,以避免“我真的写了这个吗?”综合症)。

我说的“部分”是指,如果我打开代码中的某个模块或函数,我应该能够理解它的作用,而不必阅读整个代码库的其余部分。代码应该尽可能直观和自文档化。

代码不断引用代码库其他部分行为的细微细节,就像读一本书,你必须在每句话的末尾引用脚注或附录,你永远也读不完第一页!

关于“本地”可读性的其他一些想法:

  • 封装良好的代码往往更具可读性,能够分离各个层面的关注点。

  • 名称很重要。 激活“快慢思考”系统 2,大脑通过该系统形成想法,并对变量和方法名称进行实际、仔细的思考。多花几秒钟可以带来巨大的回报。命名良好的变量可以使代码更加直观,而命名不当的变量则会导致误解和混淆。

  • 聪明是敌人。使用花哨的技术、范例或操作(例如列表推导或三元运算符)时,请小心使用它们,以使您的代码更具可读性,而不仅仅是更短。

  • 一致性是一件好事。风格上的一致性(无论是括号的放置方式还是操作方式)可以大大提高可读性。

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

戒律三:好的代码具有深思熟虑的布局和架构,使状态管理变得显而易见

状态是敌人。为什么?因为它是任何应用程序中最复杂的部分,需要非常谨慎和周到地处理。常见问题包括数据库不一致、部分 UI 更新(新数据未反映到所有地方)、无序操作,或者只是令人麻木的复杂代码,其中到处都是 if 语句和分支,导致代码难以阅读甚至更难维护。将状态放在非常重要的位置并小心处理,并在如何访问和修改状态方面保持极其一致和谨慎,可以大大简化您的代码库。一些语言(例如 Haskell)在编程和语法级别强制执行此操作。如果您拥有不访问任何外部状态的函数库,然后有一小部分引用外部纯功能的状态代码,您会惊讶地发现您的代码库的清晰度可以提高多少。

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

在重新发明轮子之前,请考虑一下您要解决的问题或您要执行的功能有多普遍。可能有人已经实施了您可以利用的解决方案。如果合适且可用,请花时间思考和研究任何此类选项。

话虽如此,一个完全合理的反驳是依赖已有的轮子并非“免费”而没有任何缺点。使用了一些第三方或开源库,表明您正在对该库做出承诺并变得依赖该库。这是一个很大的承诺;如果它是一个巨大的库,而您只需要一小部分功能,那么如果您升级到例如 Python 3.x,您真的想承担更新整个库的负担吗?此外,如果您遇到错误或想要增强功能,您要么依赖作者(或供应商)提供修复或增强功能,要么,如果它是开源的,您会发现自己处于探索(可能很重要的)您完全不熟悉的代码库的境地,试图修复或修改一个不起眼的功能。

当然,您所依赖的代码使用得越好,您自己投入时间进行维护的可能性就越小。最重要的是,您值得自己进行评估是否要包含外部技术,以及该技术将为您的代码增加多少维护?

戒律五:业务逻辑分层!

编程设计有很多好的模型,发布/订阅参与者MVC等。选择你最喜欢的,并坚持下去。处理不同类型数据的不同逻辑应该在代码库中物理隔离(再次强调,这种关注点分离概念可以减少未来读者的认知负担)。例如,更新 UI 的代码应该与计算 UI 内容的代码在物理上有所区别。

戒律六:如果可能,让电脑来做工作

如果编译器可以捕获代码中的逻辑错误并防止不良行为、错误或直接崩溃,我们绝对应该利用这一点。当然,有些语言的编译器比其他语言更容易做到这一点。例如,Haskell 有一个著名的严格编译器,这导致程序员花费大部分精力只是为了编译代码。一旦编译成功,“它就可以正常工作”。对于那些从未用强类型函数式语言编写过代码的人来说,这可能看起来很荒谬或不可能,但不要 相信 我的 。说真的,点击这些链接,生活在一个没有运行时错误的世界是完全可能的。它真的就是那么神奇。

不可否认的是,并非每种语言都有编译器或语法,可以进行大量(在某些情况下甚至任何!)编译时检查。对于那些没有的语言,请花几分钟研究一下您可以在项目中启用哪些可选的严格性检查,并评估它们是否对您有意义。我最近对具有宽松运行时的语言使用的一些常见检查的简短、非全面列表包括:

结论

这绝不是编写“好”(即易于维护)代码的详尽或完美的戒律清单。话虽如此,如果我将来必须学习的每个代码库都遵循此列表中的一半概念,我的头发就会少很多,甚至可能在我的生命结束时能够多活五年。我肯定会发现工作更愉快,压力更小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最强玩者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值