1. bcachefs 编码风格

好的开发就像园艺,代码库是我们的花园。每天都要照料它们;留意那些不合适或需要整理的小东西。在这里或那里除除草,会大有裨益;不要等到事情失控才动手。

事情不必总是完美——吹毛求疵往往弊大于利。但当你看到美时,要懂得欣赏——并让人们知道。

你害怕触碰的代码,是最需要重构的代码。

在这里或那里做一些组织工作,会大有裨益。

认真思考如何组织事物。

好的代码是可读的代码,它的结构简单,没有隐藏 bug 的地方。

断言是我们编写可靠代码最重要的工具之一。如果在编写补丁集的过程中,你遇到了不应该发生的条件(如果发生,将会有不可预测或未定义的行为),或者你不确定它是否会发生,也不确定如何处理它——把它变成一个 BUG_ON()。不要让未定义或未指定的行为潜伏在代码库中。

当你完成补丁集时,你应该更好地理解哪些断言需要处理并转化为带错误路径的检查,哪些在逻辑上应该是不可能的。对于那些逻辑上不可能的,保留 BUG_ON()。(或者,如果它们很耗费资源,就把它们变成调试模式断言——但不要把所有东西都变成调试模式断言,这样万一你错了,我们也不会陷入调试未定义行为的困境)。

断言是不会过时的文档。好的断言非常棒。

好的断言可以大大减少发现 bug 所需的测试量。

好的断言基于状态,而不是逻辑。要编写好的断言,你必须思考你的状态上的不变量是什么。

好的不变量和断言应该在你的代码库的任何地方都成立。这意味着你可以在提交的版本中只在少数几个地方运行它们,但是如果你需要调试导致断言失败的原因,你可以快速将它们散布到任何地方,找到打破不变量的代码路径。

好的断言会检查编译器可以为我们检查并省略的东西——如果我们使用的语言具有编译器可以检查的嵌入式正确性证明的话。这在今天已经存在,但可能还需要几十年才能进入系统编程语言。但是,我们仍然可以将这种思考方式融入到我们的代码中,并使用运行时检查来记录不变量——就像使用动态类型语言的人可能会添加类型注释,逐渐使他们的代码静态类型化一样。

寻找使你的断言更简单(更高级)的方法,通常会促使你使整个系统更简单、更健壮。

好的代码是可以让你探究并查看其行为的代码——内省。如果我们看不到发生了什么,我们就无法调试任何东西。

每当我们调试时,如果解决方案不是显而易见的,如果问题是我们不知道问题出在哪里,因为我们看不到发生了什么——首先解决这个问题。

我们有工具可以在运行时有效地使任何东西可见——包括 RCU 和 percpu 数据结构。不要让事情保持隐藏状态。

内省最重要的工具是简陋的漂亮打印机——在 bcachefs 中,这意味着 *_to_text() 函数,它输出到 printbufs。

漂亮打印机很棒,因为它们可以组合使用,而且你可以到处使用它们。拥有用于打印你正在处理的任何对象的功能,将使你的错误消息更容易编写(因此它们实际上会存在)并且信息量更大。而且它们可以从 sysfs/debugfs 以及跟踪点中使用。

运行时信息和调试工具应该附带清晰的描述和标签,以及良好的结构——我们不希望看到像 procfs 那样包含一串裸整数的文件。调试工具的部分工作是教育用户和新的开发人员了解系统的工作原理。

错误消息应尽可能地告诉你调试问题所需的一切。值得为它们付出努力。

不应首先求助于跟踪点。它们是一个重要的工具,但总是要寻找更直接的方法来使事情可见。当我们必须依赖跟踪时,我们必须知道我们正在寻找哪些跟踪点,然后我们必须运行有问题的负载,然后我们必须筛选日志。当用户遇到问题时,这需要经历很多步骤,而且如果问题是间歇性的,甚至可能无法实现。

简陋的计数器是一个非常有用的工具。它们便宜且易于使用,并且许多内部操作复杂、有很多事情可能表现异常(例如,任何涉及内存回收的操作),一旦你在每个不同的代码路径上都有计数器,就会变得非常容易调试。

持久计数器甚至更好。

在调试时,尽量从你遇到的每个 bug 中获得最大的收获;不要急于修复初始问题。寻找可以使下次更容易解决相关 bug 的方法——内省、新断言、更好的错误消息、新的调试工具,并首先执行这些操作。寻找使系统行为更好的方法;通常一个 bug 会通过下游影响发现其他几个 bug。

先修复所有这些,然后再修复最初的 bug——即使这意味着让用户等待。从长远来看,他们会感谢你,而且当他们理解你在做什么时,你会惊讶于他们有多么愿意耐心等待。用户喜欢帮助——否则他们一开始就不会报告 bug。

与你的用户交谈。不要孤立自己。

用户会注意到各种有趣的事情,通过与他们交谈和互动,你可以从他们的经验中受益。

花时间做支持和帮助台工作。不要只写代码——代码只有在无故障使用时才算完成。

这也将激励你使你的调试工具尽可能好,甚至你的文档也是如此。就像生活中的其他事情一样,你花在它上面的时间越多,你就会变得越好,而你,作为开发人员,是最有能力改进工具以使调试快速而容易的人。

小心你如何承担和致力于大型项目。不要让开发变得以产品经理为中心。通常,一个想法是好的,但需要等待合适的时机——但是你只有在开始编写代码时才知道它是否是这个想法的合适时机。

预计会丢弃很多东西,或者把它们半途而废留待以后。没有人会写出所有完美并最终交付的代码,如果你尽早注意到这一点并转向其他事情,那么从长远来看你将更有效率。获得的经验和教训对于你所做的所有其他工作都将很有价值。

但是不要害怕处理需要对现有代码进行重大修改的项目。有时这些可能是最好的项目,因为它们可以引导我们使现有代码更通用、更灵活、更多用途,甚至可能更健壮。如果它看起来会把事情搞砸,不要犹豫放弃这个想法。

复杂的功能通常可以作为一系列重构来完成,最后实现该功能的实际更改只是结尾处的一个非常小的补丁。当这种情况发生时,真是太好了,尤其是当这些重构本身就改进了代码库时。当这种情况发生时,如果你要实现的功能没有成功,浪费精力的风险就会小得多。

始终努力逐步工作。始终努力将大型项目变成可以证明自身价值的小型项目。

不要总是处理那些大型项目,要寻找有用的、能使大型项目更容易实现的小东西。

关于什么可能有用这个问题,初级开发人员最容易误入歧途——因为做一些看起来有用的事情往往会导致过度设计。知道什么是有用的是多年经验的结果,或者与拥有这种经验的人交谈的结果——或者只是阅读大量的代码并寻找常见的模式和问题。不要害怕丢弃东西并做一些更简单的事情。

与你的同事开发人员讨论你的想法;通常最好的东西来自轻松的对话,在对话中,人们不害怕说“如果……怎么办?”。

不要忽视你的工具。

除了编译器和我们的文本编辑器之外,最重要的工具是我们用于测试的工具。尽可能短的编辑/测试/调试周期对于高效工作至关重要。我们通过运行我们的代码并查看发生了什么来学习、获得经验和发现我们想法中的错误。如果你的时间因为你的工具不好或太慢而被浪费了——不要接受它,修复它。

为你的文档、提交消息和代码注释付出努力——但不要过分。好的提交消息是很棒的——但是如果信息重要到可以放入提交消息中,请问问自己,如果它作为代码注释会更好吗?

好的代码注释是很棒的,但更好的是不需要存在的注释,因为代码是如此的直接以至于显而易见;组织成小的、干净整洁的模块,函数和变量具有清晰的描述性名称,并且每一行代码都有明确的用途。