代码只说明它所做的事情

2020-07-07 02:02:46

代码说它做什么。这对计算机很重要,因为代码是我们要求计算机做某事的方式。这对人类来说是可以的,只要我们永远不需要修改或调试代码。一旦我们这么做了,我们就有麻烦了。从根本上说,调试是更改程序所做的事情以匹配其应该做的事情的练习。它要求我们知道程序应该做什么,而代码中没有捕捉到这一点。有时这很容易:它做的是崩溃,它应该做的不是崩溃。在这些微不足道的案例之外,发现意图就更难了。

应该做的时候调试是微妙的,例如在构建分布式系统协议时,尤其困难。在我们数以百万计的微型数据库论文中,我们说:

我们的代码审查、模拟世界测试和设计会议经常引用我们协议的TLA+模型来解决Java代码或书面通信中的歧义。

问题是,实现(在Physalia的情况下是Java代码)既是协议的不完美实现,也是协议过于具体的实现。它过于具体,因为它需要完全指定。虽然协议本身有一定的回旋余地和回旋余地,但计算机要求做到这一点。它也过于具体,因为它必须解决诸如低级性能问题之类的问题,而这些问题是规范无法解决的。

ArrayList中的这些值是因为顺序实际上很重要,还是因为O(1)个随机查找很重要,还是其他原因?这是最容易写的东西吗?当我改变它的时候会发生什么?

业务逻辑代码虽然缺乏分布式协议的威望,但存在更多此类问题。代码都过度指定了业务逻辑,并且指定得不准确。@mcclure111的一条推文促使我写下了这条推文,她一针见血地写道:

由于大多数软件没有正式的规范,大多数软件都是这样做的,因此在编辑其他人的代码时要尊重作者的意图,这是一种令人难以置信的压力。你不知道哪些怪癖是负重的。

-mcc🏳️‍⚧️🏳️‍🌈(@mcclure111)2020年6月20日

这是代码的一个主要问题:您不知道哪些怪癖是负重的。你可能记得,或者能够猜测,或者能够从基本原则中猜出来,或者不在乎,但所有这些事情都很慢,而且容易出错。我们能做些什么呢?

文档不够酷。大多数软件工程师走出校门时似乎认为文档在他们之下(技术作家的工作),或者他们的SE教授谈到的一些和Fortran一样古老的奇怪的事情。这在一定程度上是可以理解的。我自己的软件工程课程强调用UML刻意地记录实现。没有提到其他文件。用UML重写软件基本上对任何人都没有帮助。我完成了我的学位,认为文档是不必要的繁重工作。甚至敏捷宣言也同意我的观点:

我后来发现,设计文档编码了在开发系统期间做出的意图和决策,有助于团队在短期内取得成功,而人们在长期内取得成功。摆脱了头脑中的一切,相信以后可以重新发现被遗忘的事实,这让我变得更有勇气,我可以走得更快。同样的道理也适用于团队。

我看到成功的团队正在做的一件事是,不仅记录他们设计背后的内容和原因,而且记录他们是如何做出决定的。当需要对系统进行更改时-无论是为了调试还是为了响应不断变化的需求-这些文档都是无价的。当你一开始就不知道为什么会是这样的时候,很难决定改变某件事是否安全。记录你是如何做出决定的很重要,因为你是一个有缺陷的人,了解你是如何做出决定的,了解这个决定什么时候看起来很奇怪,或者令人惊讶,这是很有用的。

此文档流程不一定是重量级的。除非你认为有帮助,否则你不必绘制费力的ER图。您可能应该完全忽略UML。取而代之的是,尽可能用散文清晰而简洁地描述系统。可以从为您的团队构建RFC模板开始,这可能会受到您在Web上找到的模板的启发。Squaspace的模板似乎是合理的。有些设计可以很好地适应RFC格式,有些则不适合。你最好在可能的地方写叙事性的文章。

那么,请保管好这些文件。把它们存放在安全的地方。把它们浸泡在醋里,系在你的胸口上。您需要确保需要维护系统的人员能够找到它们。当他们在探索历史时,让他们感觉更像是图书馆的参观者,而不是劳拉·克罗夫特(Lara Croft)。

我并不主张预先进行大设计。我们在项目实施过程中学到的许多最重要的东西。在实施完成数年后,我们学到了一些最重要的东西。设计文档不是静态的一次性提前交付,而是一个持续的过程。最重要的是,设计文档并不是对坏想法的承诺。如果它出了问题,就把它修好,然后继续往前走。文档不是与魔鬼的交易。

很少有话题会像评论那样引起程序员的热议。我们被告知,评论是愚蠢的,或者是幼稚的,或者让人很难展示你在编写那些错综复杂的代码时有多有男子汉气概。如果它很难写,那么它也应该很难读。毕竟,你是代码界的詹姆斯·乔伊斯(James Joyce)。

这意味着“揭示”作者意图的评论是有价值的,而揭示“没有作者意图”的评论就更有价值了。如果没有这些提示,你就会迷信地编辑,即使你不知道为什么也会保留怪癖。https://t.co/YhvWnXjp9i。

-mcc🏳️‍⚧️🏳️‍🌈(@mcclure111)2020年6月20日。

注释允许我们以编程语言并不总是能做到的方式将作者意图编码到我们的代码中。类型、特征、接口和变量名确实将意图放入代码中,但不是完全的(我明白了,请键入System Maximsts)。这些相同的东西允许我们传达缺乏意图-考虑随机访问与ArrayList-但也是不完整的。注释良好的代码应该使作者的意图清晰,特别是在意图在代码转换过程中丢失的情况下,或者在实现约束隐藏设计意图的情况下。链接回设计文档的代码注释特别有用。

有些语言比其他语言更需要注释。有些,比如SQL,我发现几乎总是隐藏在实现细节后面的设计意图。

对规范的需求源于两个观察结果。首先,在做之前想一想我们要做什么是个好主意,正如漫画家金登所写的那样:写作是大自然让你知道你的想法有多草率的一种方式。

第二个观察结果是,要编写一个好的程序,我们需要考虑代码级别之上的问题。

我发现规范,从叙述性写作的非正式规范到TLA+的正式规范,使编写程序的速度更快,并有助于减少错误。尽管我非常喜欢这篇文章,但我认为兰波特忽略了形式规范价值的一个关键部分:它是一个很棒的交流工具。在开发我构建的一些最棘手的系统时,我发现经过大量注释的正式规范是非常有用的文档。规范语言都是关于意图的,其中一些语言使得清晰地将意图与实现分开变得容易。

我们在亚马逊广泛使用TLA+,事实证明它在Phsalia的开发中非常有用。我们的团队以三种方式使用TLA+:编写协议规范以检查我们是否深入理解它们,使用TLC模型检查器对规范的正确性和活动性进行模型检查,以及编写广泛注释的TLA+代码作为分布式协议的文档。虽然这三种方法都使用附加值,但TLA+作为一种自动测试(通过TLC)且极其精确的协议文档格式可能是最有用的。

正式规范是优秀的文档。就像设计文档一样,它们不是一成不变的产物,而是我们对这个问题所学到的东西的反映。

建立持久的、可维护的系统不仅需要与计算机通信,还需要在空间与他人通信,并在时间上与我们未来的自己通信。传达、记录和索引我们设计背后的意图是这幅画的重要组成部分。要么为它腾出时间,要么以后后悔。