译:开发者哲学

原文:https://qntm.org/devphilo
作者:qntm
译者:ChatGPT 4 Turbo

编者注: 这是一位资深开发者分享的个人开发哲学,主要包含以下几点建议:1) 要避免代码复杂度持续增加到需要重写的地步,在开发新功能和优化现有代码之间找到平衡;2) 要记住项目完成 90% 需要一半时间,剩下 10% 也需要一半时间,所以要合理规划,为测试、文档和优化预留充足时间;3) 把好的开发实践尽可能自动化,而不是靠人工检查和提醒;4) 要考虑各种极端情况下的数据处理,边缘案例才是开发工作的重点;5) 代码要简单直观,光是正确运行是不够的,要让其他开发者一眼就能看懂;6) 写代码时要考虑到测试的便利性,这意味着接口要清晰,副作用要最小化。这些建议都很实用,对新手开发者特别有帮助。

令人惊讶的是,经过这么多年,世界上仍然有初级开发者存在。

几周前,我们工作中举办了一个讲座,邀请了资深开发者(包括我)每人花大约五分钟时间谈论我们个人的软件开发哲学。这样做的目的是为了与我们的初级开发者分享多年的经验。

讲座结束后,我觉得把我自己的想法写下来,并且添加一些更详细的内容可能会很有价值。所以,我们来了。

这份清单有点杂乱;它并不旨在全面探索我开发软件的方式。此外,如果你已经是一名资深开发者,那么你可能已经熟悉其中的一些内容。或者不同意!软件开发是一个著名的主观领域。我们评论区见。

不惜一切代价避免到达从头开始重写变得有吸引力的场景

通常人们很清楚,从头开始重写是一个有吸引力且极其危险的前景。关于从头开始重写的标准建议是“永远不要这么做”。但我想从这个建议后退一步。

到了从头开始重写开始看起来像是个好主意的时候,可以避免的错误已经被犯下了。这是一个你可以从很远的地方就能看到并且必须积极避免的场景。

要注意的警告信号包括:技术债务的累积。对代码进行看似简单的更改变得越来越困难。记录/评论代码的困难。新开发者的入职困难。了解代码库特定区域实际工作方式的人数减少。没人理解的错误。

必须在每个转折点上抗击复杂性的累积。在扩展(新功能)和整合之间交替进行

当然,从头开始重写实际上可以行得通。它甚至可能是比替代方案(坚持使用你现有的、负债累累的代码沼泽)更好的选择。同样,可能两者都行不通 —— 项目注定失败,你只是在选择它的死法。重点是,这种情况有固有的风险……但这种情况本身是可以避免的,那个风险也是可以避免的。

力求在可用时间的 50% 内完成 90%

在软件开发中有一个著名的格言 —— 实际上,想想看,它可能源于软件开发之外 —— 这样说,

工作的前 90% 需要用掉 90% 的时间。工作的后 10% 需要用掉另外 90% 的时间。

这既有趣又绝对准确。理解了这一点,完全有可能对此进行纠正。

编写代码,一次性完成,并让它工作,需要一定的时间。一旦你做到了这一点,你需要明白你大约完成了一半。将代码打磨到适当的一致性和可维护性水平,适当处理边缘情况和失败情况,单元测试,集成测试,可用性测试/演示,“最后一刻”的功能变更,性能,可服务性,文档……所有这些事情都可能需要大量额外的时间,它们也是你工作的一部分。

这些事情中的许多理论上是可以跳过的。但实际上,当你跳过这些事情时,你最终得到的是一个粗制滥造、不完整的功能。而且没有人会回来“正确地”完成这项工作。总是有更多的工作。这样做三四次之后,你就会得到一个粗制滥造的产品。

此外,编写代码本身也会遇到意料之外的障碍。建议尽可能早地发现这些障碍。

如果奇迹般地发现你不需要你计划的额外时间呢?太好了,是时候实施一些流程改进了!或者偿还一些技术债务(见上文)!

自动化良好实践

有时,项目中的开发者需要开始或停止做某件特定的事情。出现了新的最佳实践。我们需要在每个地方一致地使用新工具;每个源文件都要有一个新的必须的头部;每个人都必须运行的检查;我们集体决定不再使用的方法(无论是内部方法还是第三方 API)。当这种情况发生时,有两种方法可以让整个开发者基础改变其行为:

  1. 社交化它。亲自告诉每个人,一次一个或在 scrum 或在团队会议上。发送电子邮件。将新指南添加到 wiki,或者添加到 repo README,或者拉取请求模板中。一遍又一遍地提醒人们阅读文档。永远手动审查每个人的更改以寻找疏漏。确保你永远不会忘记!添加检查列表,尝试训练每个人正确执行这些检查列表。增加强制性同行评审的级别。再次提醒每个人。再来一次……
  2. 自动化它。

如果指南没有被遵循,添加一个自动化测试来使其失败。或者,如果我们不能一次性修复所有地方的所有问题,添加一个 ratchet。如果没有做正确的事情,快速失败,并给出一个礼貌且有指导意义的警告,或者更好的是,自动修复问题。通常,机械地强制执行最佳实践。

自动化并不是一个完美的解决方案,也不是一个普遍适用的解决方案,对于这类事情来说,也不是一个普遍合适的解决方案。有很多较软的要求和抽象的技术要求无法自动化,引入太多 任意 规则可能会变得 非常 烦人严格,而且有动力的开发者通常可以通过各种方式绕过自动化。但是,如果你发现自己一次又一次地告诉人们,“你忘了做 X,请记得总是做 X”,也许是时候自动化 X 了?

思考异常数据

没有人关心黄金路径。边缘情况是我们的 全部工作。思考事情可能失败的方式。思考尝试让事情崩溃的方法。代码应该处理 每一种 可能性。

如果请求失败了,或者永远停滞不前,或者每秒只发送回一个字节,持续一个小时呢?如果你展示的表格有一百万行?十亿行?如果名字中有斜杠,或者有尾随空格,或者长达一兆字节呢?当你说你能证明那个字符串不可能为空时,我不相信你!

通常有更简单的写法

如果你合理安排了时间(见上文),你就有时间回头看看是否能做得更好。参考老棋谚,“当你看到一个好棋时,寻找一个更好的。”还有一个难以找到出处的引言,“对不起写了这么长的信,但我没有时间写一封短的。”

编写可测试的代码

这意味着要有明确定义的接口和最小的副作用。证明难以测试的代码可能没有适当的封装。

代码不仅要能证明是正确的;它应该是显而易见的、明显的、轻而易举的正确

有些代码似乎偶然工作得很正确,因为可能导致它接收到错误输入并失败的情况被周围其他代码的结构排除了。我不喜欢这样。尽管从技术上讲,代码可能没有错误,但现在重构其他代码变得困难且危险。

这对于安全问题,或理论上的安全问题尤其如此。所有调用这个特定内部函数的调用者现在都是可信的,这并不重要。

我还有另一个事项要加入这个列表,但我现在记不起来了。