如何拥有代码

2020-11-07 18:10:50

这是如何阅读代码的第二部分。在第一篇文章中,我询问了如何自信地更改未知的代码库。了解ITS的架构是必要的,但通常还不够。您可以进行更改。但我怀疑这是有信心的。你现在对它的理解是消极的。被动地了解一种应用程序就像学习了一门外语,但从来没有真正说过它。当你试着说这句话的时候,你最好是说些旅游老生常谈,否则你的脑子里就会被语法搞糊涂了。

与此同时,你的外国朋友微笑着,忍受着痛苦,最终离开了。你在笑什么?它是实际做某事的行为。但是,自信地做出改变需要的不仅仅是运行它并希望你的改变奏效。

你需要积极地让这个应用程序成为你自己的应用程序。那么,你是如何拥有Acode的呢?我将假设您了解第1部分中概述的粗略架构。还有一条被动的知识我们还遗漏了:数据可能处于的所有状态的概述。在你启动并运行它之前,很难猜到这一点。当我在一个新软件上尝试我的肮脏爪子时,我会在早期尝试让它在本地运行。尤其是因为我有一种不安的感觉,如果我的设备上没有安装它,我或它可能会做一些淘气的事情。试运行环境在理论上是好的。

但生产细节和服务往往会泄露到舞台和测试环境中。仅仅因为你有舞台环境,并不意味着你与之交谈的第三方系统也有舞台环境。能够在您的机器上本地运行应用程序有很多优点。

反馈迭代循环会和你做的一样快。你可以插入调试探头,打印文件,嗅探网络流量,关闭重要部件,然后在舒适的沙发上看着它燃烧。这是你自己的黑匣子实验室,因为你试图在野外找出答案。布拉德·菲茨帕特里克(Brad Fitzpatrick)在《工作中的程序员》(The Coders At Work)一书中做出了非常好的评论:试着把这该死的东西造出来。跨过那道坎。对于大多数人来说,这往往是最大的障碍。

因为现在更多的语言都是翻译的:让我们把这一点应用到软件的运行中吧。无论如何,这就是编译软件的目标。让它跑起来是最大的障碍,这是非常正确的。将会有依赖关系。将会有代理和防火墙。

活跃的开发人员一旦开始或加入这个项目,就会有一些忘记做的事情。小配置和点文件--这些文件隐藏在他们的主文件夹深处,没有人记得放在那里。

正因为如此,当我第一次进入一个新的项目时,我完全希望花一两天的时间来运行一个应用程序。这个过程通常需要有人快速地向你展示:

哦,你需要这张证件。当然,您需要注释掉这些行。哦,等等-Bob知道您需要从哪里获得与后端系统对话的代理的凭据。特别恶心。我尝试在我的机器上完成这些设置会话。如果是这样的话,我通常会对我们正在做的事情截屏,这样当快速会议结束后,我就可以回去按照自己的节奏纠正或检查它。如果它在别人的电脑上,出于同样的原因,我会让他为我需要做的事情截屏。如果你错过了、没有完全理解或者他们只是忘了告诉你的细节,你可能会羞愧地问几次。这就是专家的问题所在。

他们知道的太多了,以至于忘记了新人知道的有多少。所有的缩略语和TLA:对他们来说都有深刻的内在含义,而对于新来的人来说,它们只是被抛来抛去的奇怪的术语。但!。第一次在当地运行它伴随着它的危险。你需要阻止它对外部世界做任何有害的操作。这个应用程序可能会从某个账户转账,删除数据库,或者打电话给你母亲,告诉她你很久以前在夏令营里做了什么。

我们不希望这样。网络是对任何外部资源都会造成损害的东西。当然,搞砸你自己的电脑也有危险,但至少这是本地的,在最坏的情况下,只需重新安装操作系统即可。

要查看它在构建后是否还能运行,一个好方法是将它隔离在容器环境中,比如没有网络连接的docker环境中。通过这种方式,您可以启动它并查看是否进行了任何网络调用。

然后你就可以深入调查,看看这些电话是不是危险操作。Visual Studio代码很好地在停靠容器中运行代码,这样您就可以在没有互联网的情况下运行和更改它。

编辑:这是使简单的解决方案复杂化的一个很好的例子。令我尴尬的是(在出版之后)你还可以做两个白痴一样的键盘小把戏:拔下网线。或者关闭所有网络接口。

仅仅看着它在容器里运行通常并不那么令人兴奋。它可能什么也做不了,因为你没有告诉它去做。在全面运行它之前,我喜欢对某些关键文件进行测试,以了解它们在发送特定数据时如何交互。

这是我称之为插入Main的一条格言的结果。如果文件可以自己执行(比如Javascript),或者需要在某个地方只有一个main()方法(比如Java),这种方法就会有所不同。这通常意味着语言是否为静态类型。让我们从第一种情况开始:这里我们找到了动态类型的语言,如Javascript、Python或Perl。您也很幸运,因为依赖项往往以对象、字典的形式出现,而且比使用成熟的类型系统更容易模仿。只需选择一个文件。验证它在运行、清除和导入外部文件时不会做一些淘气的事情,并插入对您想要执行的方法的调用。然后独立运行该文件。根据需要重新添加任何所需的依赖项,以便该方法不会失败。

但首先要验证依赖项本身不会做一些淘气的事情。在静态类型语言中,通常不可能在同一个程序中有两个主要函数。

Main()可以移到您感兴趣的文件中,但由于是静态键入的,这往往会导致解析导入和名称空间的大量工作。一个更好的方法是劫持一个单元测试,它会给你一个新的运行代码的平台。大多数静态类型语言都有单元测试框架,而且很多都是内置的(例如,Golang就是这样做的)。拿出一个单元测试,去掉所有断言。然后将其用作main()条目,您可以在其中练习方法。另一种方法是使用REPL(本地的或在线的,如repl.it)从较大的文件中剪切出小的方法或片段,并交互地试用它们。静态类型语言也有相应的解决方案。

如果您的应用程序与其他系统交互(大多数系统都是这样做的),那么调用这些系统不仅很危险,而且在您的理解中是一个巨大的黑匣子。另一端是什么?

返回的响应是什么?它们又对您的应用程序产生了怎样的影响?了解该系统如何与其他系统交互的一个非常好的方法是构建它所调用的真实系统的钻机和仿制品。在我所在的一个项目中,我们或多或少地将整个外部世界构建成了假的模仿,我们可以互动并控制他们发送的回应。使用预录的响应构建尽可能小的虚假系统,并将主应用程序连接起来与您的本地模拟程序对话。如果NodeJS是http,我更喜欢NodeJS和Express框架,因为它需要大约5行代码才能上手。

现在,您可以转储传出请求的外观,以及应用程序在收到不同响应时的反应。如果你不知道回复是什么样子的--试着记录下来,要么自己打电话,要么试着在你知道可以安全嗅探的环境中倾倒回复。

如果不是,只需输入一些内容,然后看着应用程序发出错误,直到正确为止。它就像网络驱动的TDD。

只有看到这个系统全面运转,你才能看到全貌。如果数据类型是整数,您仍然不知道数据的范围是多少。它是整个整数范围,还是数字只从1到11?这是我第一次接触调试器。如果你不知道代码走的是哪条路,调试器是很棒的。接口、继承或复杂条件是代码路径难以遵循的常见来源。

解决一些错误导致的错误,甚至比你的程序深几层的错误,调试器是必不可少的。当我试图理解一个应用程序的状态时,我发现调试器的价值较低,而且在printfs中很普通。

用户界面只能显示如此多的状态,您不能将其保存以供以后使用(以免剪切和粘贴)。将文件系统打印到终端或将状态写入磁盘使区分和理解事物更加持久。摆脱调试器,将调试代码插入到实际应用程序中还有另一个好处。您可以对预期状态进行断言,以查看您是否正确。如果此值不是预期数字11,则将其打印出来。否则程序就会崩溃。这样一来,你就可以互动地找出什么是有效状态,以及它是如何产生的。现在,通过与已知内部状态的假模仿对话来解除应用程序的影响,我们已经准备好进行更改。如果不是因为人类心理!那么要绕过的一个心理障碍是,其他人的代码在某种程度上是神圣的,不能被触碰。

这是一个危险的想法。不管原创作者多么知名和受人尊敬,你都有责任去尝试和改进这篇文章。很多人对自己的代码视而不见。

我也是。头几个月很有价值,因为我是看到这些盲点的局外人。不断告诉自己,你的外部观点与主要作者一样有价值(如果不是更高的话)。然而!很可能你想要更改的代码并不适合你。这给我们带来了另一个心理障碍:重构。它不会给利益相关者带来额外的价值,而且你可能会因为修改某人的杰作而踩到他们的脚趾。

那你是做什么的?当然,不管怎样,你还是要这么做。我们在这里拥有这款应用程序。如果一个文件中的布局没有意义--移动它,直到它有意义为止。如果文件、方法或变量的名称过于模糊或过于具体:请更改它们。

如第1部分所述,从学习如何阅读代码开始,我经常随身携带充满注释的分支。在这些分支中,我强调(夹杂着脏话和人身攻击)可以通过重构改进的东西。我甚至已经养成了在日程表上腾出时间进行重构会议的习惯。在正常工作期间,我会列出让我恼火或恼火的地方(通常是应用程序中毛发最多、最丑的部分)。

当重构会议结束时,我拿出我的清单,选择一个区域并开始修复它。这是我从一个堆栈溢出响应中得到的,该响应是关于如何处理编写糟糕的遗留代码的。大多数人会抱怨并忍受它。

但有一个很有启发性的答案是:程序员开始对它进行重构。两年后,它被完全重写了。只有当你对代码进行了猛烈的重构后,你才能开始称它为你自己的代码。

或者共同监护权,如果有其他人在做的话。根据康威定律,你重构的东西总有一天会成为你的,所以选择你最感兴趣的部分。这是一个抢占地盘的好方法。我注意到,大多数人都会喜欢公关,因为你对如何改进代码库有自己的想法。这是一个具有一定实质内容的开场白,因为它不仅是观点,而且是支持这一观点的实际变化。

在经历了这一切之后,你会运行它很多次。你可以根据自己的情况对其进行修改,你也知道数据所在的有效状态。由于你付出的所有努力,你现在可以佩戴写有骄傲代码所有者的别针了。

让我们用昂贵的香槟和可爱的小狗来庆祝吧!几年后,当你的孙子孙女满眼星光地问你为什么要这么做时,你可以在第一口喝了一口可爱的小狗之后平静地回答:是的,孩子们,这是一项艰苦的工作。就像生活中所有美好的事物一样,最美好的事物来之不易。这件事也没有。一开始我甚至看起来很慢,因为我经历了这一切。

但它得到了回报。你看,一开始,我那些看起来更快但更马虎的同事们正在以惊人的速度做出改变。令人印象深刻的是,他们还在玩俄罗斯轮盘赌。会有错误,这些错误会悄悄进入生产。

事情最终会破裂。快而不准,什么都不是。没什么!。另一方面,在经历了上述所有步骤之后,我知道自己在做什么。

所做的更改只会做我期望它做的事情,而不会做其他任何事情,因为我知道如何在本地验证它。当我被问到关于应用程序如何工作的问题时,我通常会牢记在心,或者我可以做一个快速测试来验证我的假设。

但还有更多的原因。这份辛苦的工作让人不知所措。是的,第一个变化真的很慢。但第二次的速度更快。还有第三个。到第十个变化到来的时候,我已经飞快地通过它了。

这是因为,通过深入了解代码,我还可以计划如何更快、更准确地验证更改。在拥有它的同时,我已经建立了一个验证方法的武器库。孙子孙女们敬畏地看着你,思考着你的智慧金块,而你却微微流着口水,打着呼噜睡着了。但带着自信和问心无愧的微笑。