竹笋

注册

 

发新话题 回复该主题

我从资深软件工程师学到的避坑大法 [复制链接]

1#
北京最专业治疗白癜风医院 http://yyk.39.net/bj/zhuanke/89ac7.html

选自neilkakkar

作者:NeilKakkar机器之心编译参与:陈韵莹、一鸣本文是彭博社的一位开发者所写的文章,介绍了从一位资深工程师同事的身上学到的一些开发经验。过去一年中,我坐在一位资深的软件工程师旁边,可以仔细地观察他是怎么工作的。我们两人经常共同编程,使得这项观察更为容易。此外,在团队文化中,从背后窥探写代码的人并不令人反感。以下是我所学到的:编写代码如何命名我首先着手的是ReactUI。我们有一个主要组件来放置其他所有的组件。我喜欢在代码里加点幽默感,因此我想要将它命名为GodComponent。当进入代码审查环境的时候,我才明白为什么命名这么难。在计算机科学里有两个难题:内存不足、命名、以及差一(off-by-one)错误。——LeonBambrick我每个命名的代码段都有隐藏含义在里面。GodComponent是所有我不必费心去寻找合适位置来存放那些垃圾的地方,它可以容纳所有东西。如果我早早把它命名为LayoutComponent,之后的我就会发现它所做的就是分配layout,没有状态。我发现命名好的另一个好处是:如果它看起来太长了,就像LayoutComponent包含了很多业务逻辑层,我就知道是时候要重构了,因为业务逻辑层并不属于这里。如果是以GodComponent命名,这里的业务逻辑层也不会和其他有所区别。命名你的集群?以在服务器上运行的服务名称来命名更好,直到用它们来运行其他服务为止。我们最终以团队的名字来命名服务器。在函数上也是同样的道理。doEverything()是一个糟糕的名字,会有很多难以预料的后果。如果这个函数能够做所有事情,那么在测试函数某个特定部分时将变得非常困难。因为不管这个函数有多大,你都不会觉得奇怪,毕竟这个函数应该做所有的事情。这时候就需要改名、重构了。有意义的命名也有不太好的一面。如果名字的表意太强,结果掩盖了一些功能上的细微差别怎么办?例如:当你在SQLAlchemy中调用session.close()时,这只会关闭会话但不会关闭底层数据库的连接。在这种情况下,可以以x,y,z来命名而不是count(),close(),insertIntoDB(),这样可防止为其赋予隐性含义并强制开发人员仔细检查它所执行的操作。历史代码和下一名开发者你曾否看过一些代码,觉得它们很奇怪?这些代码为什么这么做呢?它们的实现一点都不合理。我曾负责过遗留代码库。代码中有诸如「当Mohammad发现情况时取消注释代码」这类的注释。这是在做什么?谁是Mohammad?在这里可以做下角色转换——想象下一个人来看我的代码,他们是否会觉得奇怪?同行审查可以某种程度上解决代码注释这个问题。这让我想到了上下文的概念:注意我团队正处的上下文位置。如果我忘记了这部分代码,之后又回到了代码工作上,没有注释的话我不能重新创建上下文,我可能只会想:「为什么他们要这么写?这没有任何意义……哦,等等,是我写的。」这里就是开发文档和注释该出现的地方。文档和注释文档和注释有助于维护上下文和分享知识。正如李在《如何构建好软件》中所说,「软件的主要价值不是编写它的代码,而是编写它的人所积累的知识。」比如说,我们有个似乎没有人用过的、面向随机客户端的API终端。因为这些原因,我就应该把它删除吗?毕竟这是一个技术累赘。如果说,在某个特定国家,有10名记者会一年一次将他们的报道发送到这个终端,怎么办?你如何测试它?如果没有开发文档(那时就没有)就不能测试。所以我们没有测试。我们删除了那个终端。过了几个月后,到了一年中发送的时间,因为这个终端已经不存在了,10名记者也就无法发送这10份重要报告。虽然熟悉产品的人已经离开了团队,但是现在代码中有注释解释终端的作用。据我所知,文档是每个团队都在努力的东西。不仅仅是代码的文档,还有关于代码的流程。自信地删掉垃圾代码我过去很不喜欢删除垃圾代码或过时的代码。我认为过去写的代码都是神圣的。我的想法是:「他们写这些代码的时候肯定有一些想法。」这是传统和文化与第一性原则之间的碰撞,与删除一年一次的终端发生的事相同。我在那里学到了详细的一课。我尝试基于已有代码进行工作,但是资深工程师会尝试解决掉它——全部删除。一个永远无法到达的if声明?一个不应该调用的函数?是的,都消失了。至于我呢?我只会把我的函数写在最上面。我没有减少这些技术累赘,反而增加了代码的复杂程度,以及误导别人的可能。下一个人将事情拼凑起来会更困难。现在我受到的启发是:有一些代码你可能不理解,也有一些代码你知道永远不会用。删除那些你永远都不会用的代码,小心那些你不理解的代码。代码审查代码审查对学习来说非常有用。这是你写代码和其他人写代码时进行的外部反馈循环。两种实现有什么区别呢?一种方法比另一种好吗?每次代码审查时我都问自己:「他们为什么这样做?「。每当我找不到合适的答案时,我就会去和他们谈谈。在第一个月后,我开始在同事的代码中找到错误(就像他们对我代码做的一样)。同行审查对我来说变得更有趣了——这是我期待的游戏——一个提高我代码意识的游戏。我的启发是:在理解代码如何实现前不要批准它。测试我非常喜欢测试,以至于如果没有测试就将代码写入代码库我会感到非常不舒服。如果整个应用程序只做一件事(就像我所有的学校项目),那么手动测试是可以的。但是如果该应用程序可完成种不同的功能,那该怎么办呢?我不想花半个小时来测试所有的功能,何况有时候还会忘记一些需要测试的地方。所以就出现了自动化测试。我认为测试是一种文档,是对代码假设的文档。测试会告诉我(或我之前的人)他们预想代码是如何工作的,以及他们预期哪里会出错。所以,当写测试时,我会记住:

记录如何使用测试时用到的类/函数/系统。

记录我所想到的会出错的地方。

在大多数情况下,以上的结论是在我在测试而不是实现的过程中想到的。以下是我在Google卫生间小休时学到的例子:

我在#2中遗漏了一些东西,那里是bug出现的地方;

所以每当发现bug时,确保修复bug的代码也有相应的测试(称为回归测试),用于记录信息:这里可能出现另一种错误。

仅仅编写这些测试并不能提高我代码的质量,而编写代码却可以。但是我从阅读测试代码中获得了写更好代码的直觉。但是,并不只有这一种测试,这就是为什么有部署环境测试的原因。你可以有完美的测试单元,但是如果没有系统测试,就会出现以下的情况:这同样适用于已经测试好的代码:如果你机器上没有你需要的库,你会崩溃。为了测试你需要:

有一台你用于开发的机器;

有一台你用于测试的机器;

最后,有一台你部署的机器(请不要用与开发程序使用同一台)。

如果测试和部署机器之间的环境不匹配,你就遇到麻烦了。所以这里就出现了部署环境。

我们先有本地开发环境,在我的机器上是docker;

然后有服务器上的开发环境,机器上安装了一系列的库(和开发工具),我们在安装了代码的机器上进行开发。其他相关依赖的测试都可以在这里进行;

接下来是beta/stage环境,它与生产环境完全一样;

最后是生产环境,它是代码运行和服务于实际客户的机器上的环境。

这里的想法是尝试捕获单元和系统测试无法捕获的错误。例如,请求系统和响应系统之间的API不匹配。个人项目与小公司的情况大不一样。不是每个人都有资源来搭建自己的设备。然而,这个想法仍适用于像AWS和AZURE这样的云供应商。你可以为开发和生产设置分开的集群。AWSECS使用docker镜像来部署,所以即使跨环境事情也会相对平稳。棘手的一点是其他AWS服务之间的集成。你是否可以在正确的环境中调用正确的终端呢?你甚至可以更进一步:下载其他AWS服务的备用容器镜像并使用docker-
分享 转发
TOP
发新话题 回复该主题