我从领先的程序员那里学到了什么


一年前,我开始在彭博社全职工作。 然后我决定写这篇文章。 我以为我会充满想法,可以在时机成熟时抛在纸上。 但是一个月之内,我意识到一切都不会那么简单:我已经开始忘记自己学到的东西。 要么获得的知识如此之好,以至于我的头脑使我相信自己一直都知道,或者它们只是从我脑海中飞了出来。 1个

这就是我开始写日记的原因之一。 每天,我进入有趣的情况,对它们进行描述。 还要感谢我坐在领先的程序员旁边的事实。 我可以仔细观察他的工作,并发现它与我的工作有何不同。 我们一起编写了很多程序,这使我的观察变得更加容易。 而且,我们的团队不谴责“监视”编写代码的人。 当我觉得正在发生一些有趣的事情时,我转身看了看。 由于不断上升,我一直都知道发生了什么。

我在领先的程序员旁边呆了一年。 这是我学到的。

目录内容



代码编写


如何在代码中命名事物


我的首要任务之一是在React UI上工作。 我们有一个包含所有其他组件的主要组件。 我喜欢在代码中添加一些幽默感,并且想命名GodComponent的主要组件。 现在是审查代码的时候了,我明白了为什么这么难给名字起名字。


我命名的每一段代码都有一个隐含的目的。 GodComponent ? 这是得到所有我不想放在正确位置的垃圾的组件。 它包含了一切。 将其命名为LayoutComponent ,将来我会决定该组件分配一个布局。 它不包含状态。

我学到的另一个重要经验教训是,如果某个东西看起来太大了,例如带有一堆业务逻辑的LayoutComponent ,那么是时候对其进行重构了,因为这里不应该有业务逻辑。 对于名称为GodComponent情况,业务逻辑GodComponent存在将无关紧要。

需要命名集群吗? 在它们上运行的服务之后调用它们是一个好主意,直​​到您在这些集群上运行其他东西为止。 我们给他们起了一个名字,以纪念我们的团队。

同样的事情也适用于功能。 doEverything()是一个可怕的名称,具有许多后果。 如果函数执行所有操作,将很难测试其各个部分。 不管这个功能有多大,对您来说它永远不会太陌生,因为它应该做的一切。 因此更改名称。 反驳。

一个有意义的名字有一个缺点。 突然间,这个名称将变得太有意义了,并且隐藏了某种细微差别? 例如,当在SQLAlchemy中调用session.close()时, 关闭会话不会关闭数据库连接。 我应该已经阅读了文档并防止了该错误,有关自行车问题的更多信息,请参见。

从这个角度来看,将函数命名为xyz而不是count()close()insertIntoDB()不允许我们赋予某些含义,并且让我仔细监视这些函数的作用。 2

我从没想过要写多于一行文本的命名原则。

继承的代码和下一个开发人员


您是否看过代码,对您来说似乎很奇怪? 你为什么写那个? 这没有道理。

我有机会使用继承的代码库。 您知道这点,并带有“当穆罕默德了解情况时取消注释代码”之类的注释。 你在这做什么 谁是穆罕默德?

我可以切换角色并考虑将要得到我代码的那个人,他觉得这很奇怪吗? 对您的代码进行部分审查有助于同事审查您的代码。 这使我想到了上下文:我需要记住团队工作的上下文。

如果我忘记了此代码,请稍后返回并无法恢复上下文,我会说:“他们到底在做什么? 这很愚蠢……啊,等等,我做到了。”

代码中的文档和注释在这里起作用。

代码中的文档和注释


它们有助于保持环境并传达知识。 正如Lee在“ 如何构建好的软件”中所说:

软件的主要价值不在于所创建的代码,而在于其创建者所积累的知识

您有一个似乎没有人使用过的开放客户端API端点。 是否只需要删除? 一般来说,这是一项技术义务。 如果我告诉你,在一个国家中,有10位记者每年将他们的报告发送到该端点一次? 如何检查? 如果(在文档中)未提及,则无法对其进行检查。 我们尚未检查。 他们将其删除,几个月后,这一年非常重要。 十个新闻记者无法发送他们的重要报告,因为终点不再存在。 具有产品知识的人员已经离开团队。 当然,现在在代码中有注释解释了为什么这样做是必要的。

据我所知,每个团队都在努力处理文档。 有了文档,不仅通过代码,而且还通过与之相关的流程。

我们尚未提出完美的解决方案。 就个人而言,我喜欢Antirez将代码中的注释分成不同类型的值的方式

原子提交


如果您需要回滚(并且需要它。请参见“ 测试”一章),那么作为单个模块进行此提交是否有意义?

如何自信地删除糟糕的代码


我非常不高兴删除糟糕或过时的代码。 在我看来,几个世纪以前写的一切都是神圣的。 我想:“当他们这样写的时候,他们会想到一些东西。” 一方面,这是传统与文化之间的对抗,另一方面是“基本原理”风格的思考。 这与删除年度端点相同。 我上了一堂特别的课。 3

我将尝试绕过代码,而领先的开发人员将尝试通过它。 删除它。 如果无法访问if表达式? 是的,我们擦除。 我做了什么 我只是在上面写了我的函数。 我没有减少技术债务。 无论如何,我只是增加了代码的复杂性和转发。 下一个人将图片拼凑起来将更加困难。

根据经验,我得出的结论是:有些代码您不理解,但是绝对不会与您联系。 删除您不联系的代码,并小心不了解的代码。

代码审查


代码审查是一个很好的自我教育工具。 这是一个外部反馈循环,显示了他们将如何编写代码以及如何编写代码。 有什么区别? 一种方法比另一种更好? 我在每次评论时都问自己:“他们为什么这么写?” 如果他找不到合适的答案,他就去问。

第一个月之后,我开始发现同事代码中的错误(就像我在我的同事中发现的那样)。 这是某种疯狂。 这篇评论对我来说变得更加有趣,它变成了我所缺少的游戏,并且改善了我的“代码意识”。

以我的经验,在我了解代码的工作方式之前,您无需批准代码。


我的github统计信息。

测试中


我爱上了测试,以至于没有测试就无法在代码库中编写代码。

如果您的应用程序只做一件事(就像我所有的学校项目一样),那么您仍然可以手动进行测试。 4这正是我所做的。 但是,如果应用程序执行100个不同的任务会怎样? 我不想花半个小时进行测试,有时我看不到某些东西。 一场噩梦。

测试和测试自动化在这里有帮助。

我将测试视为文档。 这是我关于代码的想法的文档。 测试告诉我(或我之前的其他人)如何想象代码的工作以及预期的错误之处。

今天,当我编写测试时,我尝试:

  1. 展示如何使用测试类,函数或系统。
  2. 显示我认为可能出问题的地方。

结果,我经常测试行为,而不测试实现( 这是我在Google休息期间选择的一个示例 )。

在第2段中,我没有提及错误的来源。

当我发现错误时,请确保该修复程序具有适当的测试(称为回归测试)来记录信息。 这是可能出问题的另一个原因。 5

当然,代码质量的提高不是因为我编写测试,而是因为我编写了代码。 但是阅读测试有助于我更好地了解情况并编写更好的代码。

这是测试的一般情况。

但这不是我应用的唯一测试。 我说的是部署环境。 您可能有理想的单元测试,但是如果没有系统测试,可能会发生以下情况:



这也适用于经过良好测试的代码:如果您的计算机上没有必需的库,则所有内容都会崩溃。

  • 您正在开发机器(所有模因的来源,例如“它在我的计算机上工作过!”)。
  • 有些机器正在测试(可能与之前的机器一致)。
  • 最后,有些机器要在其上进行部署(它们不应与您所开发的机器相吻合)。

如果测试和部署环境在计算机上不匹配,则将出现问题。 部署环境将有助于避免这种情况。

我们正在计算机上的Docker中进行本地开发。

我们有一个开发环境,这些计算机配备了一组库(和开发工具),在这里我们安装了书面代码。 在这里可以使用所有必要的系统进行测试。 我们还有一个Beta /登台环境,可以完全重复操作环境。 最后,我们有一个操作环境-可以为客户执行代码的机器。

这个想法是要捕获在单元和系统测试期间尚未出现的错误。 例如,请求系统和响应系统之间的API差异。 我认为对于个人项目或小型公司,情况可能会完全不同。 并非每个人都有机会创建自己的基础架构。 但是,您可以求助于AWS和Azure等云服务。

您可以配置单个集群以进行开发和操作。 AWS ECS使用Docker映像进行部署,因此不同环境中的流程将相对一致。 不同的AWS服务之间的集成存在细微差别。 您是否在正确的环境中调用正确的端点?

您甚至可以走得更远:下载其他AWS服务的替代容器映像,并设置一个基于Docker-Compose的本地功能齐全的本地环境。 这加快了反馈循环。 6也许当我创建并启动我的副项目时,我会获得更多经验。

降低风险


您可以采取哪些步骤来减少灾难风险? 如果我们正在谈论新的根本性变化,那么如果出现问题,我们如何验证最小的停机时间? “由于所有这些新更改,我们不需要完全部署系统。” 到底是什么 我为什么不考虑呢!

建筑学


为什么在编写代码和测试后谈论架构? 它可以放在第一位,但是如果我没有在所使用的环境中进行编程和测试,则可能无法成功创建一个考虑该环境特征的体系结构。 7

创建架构时,您需要考虑很多。

  • 数字将如何使用?
  • 会有多少用户? 它们的数量可以增加多少(数据库中的行数取决于此)?
  • 哪些礁石可以相遇?

我需要将其转换为一个名为“声明收集”的清单。 目前我经验不足,明年尝试在彭博社做。 这个过程在很大程度上与敏捷相反:在进行实施之前,您可以设计多少架构? 一切都与平衡有关,您需要选择何时以及要做什么。 什么时候向前冲有意义,什么时候-向后退? 当然,收集要求并不等于考虑所有问题。 我认为,如果我们将开发过程包括在设计中,那将会有所收获。 例如:

  • 当地发展将如何进行?
  • 我们将如何打包和部署?
  • 我们将如何进行端到端测试?
  • 我们将如何对新服务进行压力测试?
  • 我们将如何保守秘密?
  • CI / CD集成?

我们最近为BNEF开发了一个新的搜索引擎。 进行这项工作真是太棒了,我组织了一次本地开发,并了解了DPG(软件包及其部署),并秘密进行了部署。

谁会想到将秘密部署到产品上可能如此简单:

  1. 无法将它们放置在代码中,因为有人可以注意到它们
  2. 要将其存储为环境变量,因为规范提供了12个应用因素? 这不是一个坏主意,但是如何将它们放置在那里? (每次开车时都要去产品上填写环境变量,这很痛苦)
  3. 将它们部署为文件? 但是它们将来自何处以及如何填充它们?

我们不想手动做所有事情。

结果,我们进入了具有基于角色的访问控制的数据库(只有我们和我们的计算机可以与数据库通信)。 我们的代码在启动时会从数据库接收秘密。 在开发,登台和操作环境的框架内可以很好地复制此方法;机密存储在适当的数据库中。

同样,对于像AWS这样的云服务,情况可能完全不同。 您无需以某种方式照顾秘密。 为您的角色获得一个帐户,在界面中输入机密,然后您的代码将在需要时找到它们。 这极大地简化了一切,但是我很高兴获得了经验,因此我很欣赏这种简单性。

我们创建架构,而不忘记维护


系统设计令人鼓舞。 和护送? 不会太多 我在伴游世界中的旅程使我想到一个问题:系统为何以及如何退化? 答案的第一部分与所有已过时的退役无关,而只是增加了一个新的。 倾向于添加而不是删除(与任何事物都不相似吗?)。 第二部分是在设计时牢记最终目标。 随着时间的流逝,系统开始执行原本不希望的工作的系统,不一定会像最初为相同任务设计的系统那样工作。 这是一种后退式的方法,而不是技巧。

我至少知道三种降低降解速率的方法。

  1. 独立的业务逻辑和基础架构:基础架构通常会退化-负载增加,框架变得过时,出现零日漏洞等。
  2. 创建流程以获得将来的支持。 对旧位和新位应用相同的更新。 这样可以防止新旧代码之间的差异,并使所有代码保持在“现代”状态。
  3. 确保丢弃所有不必要的和过时的东西。

部署方式


我会打包功能还是一次部署一个? 根据当前的过程,如果将它们打包在一起,请等待故障。 问自己为什么要打包功能?

  • 部署需要很多时间?
  • 代码审查不是很有趣吗?

无论是什么原因,都需要纠正这种情况。 我至少知道两个与包装有关的问题:

  1. 如果其中一个功能有问题,您自己可以阻止所有功能。
  2. 您增加了问题的风险。

无论选择哪种部署过程,您始终希望您的汽车像牲畜,而不像宠物。 它们不是唯一的。 您确切地知道每台计算机上执行了什么,如何在死机时重新创建它们。 如果有任何一辆汽车死了,您不会感到沮丧,您只需接一辆新车。 你放牧他们,而不是成长。

当出现问题时


万一出了问题,而且确实如此,那么就有一条黄金法则:将对客户的影响降至最低。 万一发生故障,我的第一个愿望就是修复它。 这似乎不是最佳解决方案。 即使可以一行完成,也不需要固定,而是需要先回滚。 返回到先前的操作状态。 这是使客户恢复工作版本的最快方法。 只有这样,我才能找出问题所在并加以解决。

这同样适用于群集中的“损坏”计算机:在找出发生了什么事之前将其关闭,将其标记为不可访问。 我感到奇怪的是,我的天生欲望和本能与最佳解决方案相抵触。

我认为这种本能还导致我更长时间地修复错误。 有时候,我意识到有些事情是行不通的,因为我编写的代码某种程度上是错误的,于是我疯狂地看着每一行。 类似于“先深入”搜索。 事实证明问题是由于配置更改引起的,也就是说,我没有首先检查它,这种情况使我感到不安。 我浪费了很多时间寻找错误。

从那时起,我学会了搜索“广度优先”,因此已经“深度优先”以排除高层原因。 我可以用当前资源确切地确认什么?

  • 汽车行吗?
  • 代码是否正确安装?
  • 有配置吗?
  • <特定于代码的配置>,例如,路由拼写是否正确?
  • 架构版本是否正确?
  • 然后,我投入到代码中。

我们认为nginx的安装不正确,但事实证明只是配置被禁用

当然,我不需要每次都这样做。 有时仅是一条错误消息就足以立即了解代码。 当我无法确定原因时,我尝试最小化代码更改的数量以找到原因。 变化越少,我就能更快地找到问题的真正根源。 另外,现在我有一个关于错误的备忘录,这使我节省了一个多小时的时间来思考“我错过了什么?” 有时我忘了最简单的检查,例如配置路由,匹配模式和服务版本等。 这是我使用的技术堆栈开发的又一步,您只有从经验中获得的经验就是凭直觉来确定到底什么不起作用。

单车


没有故事,这篇文章是不完整的。 , . SQLAlchemy. BNEF , . . SQLAlchemy, , Solr. - .

«MYSQL server has gone away.» . , , . , . , . , ?

, ? , , . , , __exit__() session.close() .

, , . . . . .

Session.close() MySQL- SQLAlchemy , NullPool. . , , . : StackOverflow (, !) , , SQL- . , . , , (), .

«» , 1 8. , , — .

, .


, . , , . , .

, , , . , . , . « ?! , ? ».

, : , . , , . , , . , . — ? , , . . , -, , , , , .. .

. , , ? (, AWS CloudWatch Grafana). .

. , , 50 %, — . ? . , — (, ?).

. , , , , ? ? , ?

, . , . — - .

结论


. , , , . , - !

. , !

, . , — How to Build Good Software .


. : ! , .

  1. ?
  2. ? , ? , ?
  3. . , ? ?
  4. (utils) (, , , ) , « »?
  5. ?
  6. , , - ?
  7. — API , ?
  8. ? , .
  9. , , . , , .
  10. PR: « , , 52 , , , , , ». ?
  11. . ?
  12. ?
  13. ?


  1. . , ? - ? , ?
  2. , x() , y() z(), x() , y() z() . , WYSIATI .
  3. .
  4. . , ?
  5. , - , , . , .
  6. , , Docker- AWS.
  7. .

Source: https://habr.com/ru/post/zh-CN465561/


All Articles