运行前的生命。 Yandex报告

在大型项目中,可能会出现通过应用程序前端代码中的差异为最终用户识别更改的任务。 Yandex.Market的开发人员Nikita Sidorov @nickshevr讲述了我们如何使用Diffector库解决此问题,如何在Node.js应用程序中构建和分析模块图以及如何在启动之前查找代码中的缺陷。



-今天,我将尽量与您坦率。 我已经在Yandex.Market工作了一年半以上。 我使用相同数量的网络,并且开始注意到自己的变化,您也可以注意到它们。 我的平均头发长度增加了,胡须开始出现了。 您知道,今天我看了我的同事:在Sergei Berezhnoy veged ,在Vova Grinenko tadatuta中 ,我意识到-这对于我几乎已经成为一名真正的前端开发人员已经很成熟了。

在发誓时,我决定与您谈谈生活,以及我们所有人都参与其中的生活。 主要是关于运行前的生活。 现在,我将解释所有这些内容。



那人生呢? 当然,关于代码的生命。 代码是我们的工作。 让我提醒您,我决定在这里与您保持真诚,因此第一张幻灯片越简单越好。 我接受了真理,这是第一步-领养,您知道,没有人会反对这种公理。



然后我意识到我必须对其进行修改,但是很明显。 让它成为某种对需求的接受。 任何代码都始于您查看任务并尝试接受设置您的要求的事实。



当然,此后,我们开始编写阶段-我们编写代码。 然后我们用测试覆盖它,我们自己检查它的有效性。 之后,我们已经检查了我们的应用程序是否与我们的代码整体兼容。 之后,我们将其交给测试人员-让他检查。 那之后您怎么看? 我提醒你,运行前的生活。 您是否认为运行时遵循此步骤? 实际上,事实证明是这样的。 这不是演示文稿中的错误。 通常在检查的任何阶段-可能比我指示的要多得多-您可能会有一些goto调用来再次写信。 同意,这可能是一个很大的问题。 这可能会减慢生产中某些功能的交付速度,并且从原则上讲,您会拖慢开发人员的速度,因为门票会挂在您身上。 在这里,一切都过去了。 N次检查还有M次,然后代码才在浏览器中到达用户。 但这是我们的目标。 我们的目标是编写真正可供用户使用并切实为用户带来利益的代码。

今天我们将讨论第一部分。 关于之前发生的事情,但实际上与测试无关。



顺便说一句,它看起来像这样。 我轮到跟踪器,收集了自己的跟踪器,计算了中位数。 事实证明,我的票证在开发中比在检查方面要少得多。 如您所知,检查的时间越长,goto出现在开头或goto出现在结尾的机会就越高-我根本不想这样做。

而且,如果您注意的话,幻灯片上有两个词-开发(这是我们,开发人员正在做的事情)和验证(这是我们正在做的事情,同时也是测试人员)。 因此,这个问题实际上与测试人员有关。



目标是平淡无奇的。 总的来说,我想说的是生活必须简化:我们已经和您一起工作了很多。 目标看起来像这样,但您必须承认,它只是短暂的,因此让我们重点介绍目标可能依赖的一些基本标准。

当然,代码越少,对我们来说越容易。 CI检查越快,我们就越早意识到我们是否正确。 也就是说,在本地它通常可以永远开始。 验证速度-这直接适用于测试仪。 如果我们的应用程序很大并且需要进行完整的检查,那么这将花费大量时间。 发布速度取决于所有这些。 包括在内,直到我们通过所有检查,并且直到我们知道代码正是我们想要的代码,才可能发布。

为了解决我们正在谈论的一些问题,让我们分析编程语言中模块的依赖图。 而且,实际上,让我们对其进行描述。



图形是有方向的:它的边缘带有方向。 在图的节点中,我们将只有我们正在谈论的语言的模块。 肋骨是键的一种特殊类型。 通讯有几种类型。



让我们看一个常见的例子。 有文件A。在这里,文件B中的内容被导入到文件B中,这就是节点之间的关系。



如果将import替换为require,也会发生同样的情况。 实际上,这里的一切并不是那么简单。



我建议,由于我们正在谈论的是依赖关系类型,因此至少应考虑两种类型-加快管道运行速度,加快图形遍历速度。 不仅要注意从属模块,还要注意从属模块。 我建议将模块A称为父母,B称为孩子,我建议您始终将链接保留为双链表。 我会提前通知您,这将简化您的生活。

一旦我们以某种方式描述了图表,让我们就如何构建它达成一致。



有两种方法。 使用相同的AST(抽象语法树)或常规使用您喜欢的编程语言中的您喜欢的工具。 这有什么好处? 在这里,您与任何人都没有束缚,但同时您必须自己实现一切。 您将必须描述所使用的所有事物和技术的所有类型的连接,无论它是单独的CSS收集器,还是类似的东西。 但是可以这么说,您拥有完全的飞行自由。

另外,第二个选项,我还将对其进行一些推广,这是一个选项,仅适用于已经配置了构建系统的大多数人。 事实是,默认情况下,组装系统会根据设计收集图形。



让我们看一下Yandex中最流行的组装系统之一,即webpack。 在这里,我给出了一个示例,说明如何将webpack的整个结果收集到一个单独的文件中,然后可以将其输入到我们的分析仪或其他分析器中。 他在AST的帮助下收集了它,并使用了橡子库。 您可能已经注意到她跌倒了。 我注意到了。

还有什么优势。 事实是,当您描述构建系统时,您绝对会诚实地要求输入。 这些是从中解开依赖项的文件,即初始绕过点。 这很好,因为您不必再​​次记录它们。 此外,webpack和babel以及所有这些以及橡子包括在内,它仍然不是您自己维护的。 因此,该语言的各种新功能,各种错误以及其他所有方面的纠正速度都比您进行纠正时要快,尤其是在没有庞大团队的情况下。 是的,即使它很大,也没有开源那么大。

实际上,这既是加也是减。 就像这样获得了一把双刃(双刃剑)。 事实是,此图是在组装期间生成的。 很好,就是我们可以组装项目并立即重用组装结果。 但是,如果我们不想组装一个项目,而只想获得这张图怎么办?

实际上,如此重大的减法。 如果您连接了任何自定义内容,我们将在稍后讨论连接问题,然后构建系统将不允许您这样做。 或者,您将必须集成它,例如webpack插件。



考虑一个具体的例子。 我在我的投影上运行了一个命令,那里只有三个文件,并得到了此输出。 而且我只显示一个键,称为模块。 我们只是在与您讨论模块的依赖关系图,因此我们看一下模块,一切都是合乎逻辑的。



很多信息,但我们不需要一切。 留下一些要点,让我们谈谈。 假设我们考虑第一个模块。 他有名字,有原因。 原因仅仅是与“从属”模块的连接,事实证明是那些将这个模块导入自己的人。 这是在其上构建图的基本数据。



此外,请注意usedExports和provideExports。 我们稍后再讨论。 但是这些也是非常重要的事情。





而且,如果您描述自己的决定,那么您需要讨论模块之间发生的连接类型。 就是说,我们当然在语言内部拥有模块系统:无论是cjs-modules还是esm-modules。 此外,您必须同意我们可以在文件系统本身的级别上在文件系统中的文件之间建立连接。 这些是某种类型的框架:某种类型的框架将要使用,具体取决于爸爸的方式。



还有一个普通的例子-如果您编写了Node的服务器端,那么您经常会看到诸如Config这样的流行npm软件包。 它使您可以非常方便地定义配置。



要使用它,您需要获取config文件夹(其中具有NODE_PATH),并指定多个JavaScript文件-只是在此处提供针对不同环境的配置。 例如,我创建了一个爸爸,指定了默认值,开发和生产版本。



而且,实际上,整个配置的工作方式如下。 也就是说,当您编写require('config')时,它只读取自身内部的模块,并从环境变量中获取模块名称。 如您所知,目前尚不清楚这些文件是否以某种方式使用,因为没有直接导入/需要,webpack甚至无法识别它。


幻灯片链接

今天我们还讨论了依赖注入。 我并不是受启发的人,但是在支持方面,我看了这里的一个库。 它称为Inverseify JS。 如您所见,它提供了一种相当自定义的语法:lazyInject,nameProvider,就在这里。 而且,您必须承认,目前尚不清楚它是什么类型的提供程序,它在这里真正注入了什么样的模块。 我们需要它,我们必须了解它。 也就是说,构建系统将无法解决,我们必须自己解决。

假设我们已经建立了一个图形,建议您先将其存储在某个地方。 什么将使我们能够做到这一点? 这将使我们能够进行某种启发式分析,玩一些数据科学,并专注于时间片。



有什么想法? 实际上,这直接是我们的数据。 我们最近刚刚在Yandex.Market中实现了我们的设计系统,尤其是实现了组件库作为该设计系统的一部分。 在这里您可以看到:我们考虑了导入次数,库中的react组件,common组件。 您可以在目录中分发。 在这种情况下,我们有一个这样的非单一存储库,因此我们有platform.desktop,platform.touch和src。

看到这些数字,我们会怎么想? 我们可以假设touch命令似乎并没有增加对通用组件的使用。 这意味着这些组件要么对移动设备不利-制造不佳,要么触摸命令很懒。 但是真的是这样吗?



如果我们查看更长的时间,更长的时间,这使我们可以在每次发布后仅存储图形,那么我们将理解,实际上,一切都可以接触,指标正在不断增长。 对于src,甚至更好,对于台式机,事实并非如此。



听众还有一个问题,如何解释对管理者的重要性。 这是按时间划分的库导入总数。 哪些经理不喜欢图形? 您可以建立这样的时间表,并看到库的使用正在增长,这意味着这至少是一件有用的事情。

我最喜欢的部分之一。 我将简要介绍一下。 这是对图中缺陷的搜索。 今天,我想与您讨论两种类型的缺陷:这是模块和一些未使用模块的循环依赖关系,即死代码消除问题。



让我们从循环依赖开始。



这里的一切似乎都很简单。 您已经有一个有向图,您只需要在其中找到一个循环。 我将解释为什么我要谈论这个。 事实是,在我基本上在Node.js上写服务器端之前,原则上我们没有使用任何webpack / babel,什么也没有。 也就是说,它们按原样启动。 并且有要求。 谁记得进口和需求有何不同? 一切正确。 如果您编写的代码不好,但是我确实做到了,那么只有在用户提出某些请求或某些其他事件将起作用时,您才能在服务器上发现模块处于某种周期性依赖关系。 那是一个相当全球性的问题。 直到运行时不明白。 也就是说,导入要好得多,不会有这样的问题。



然后只要采用您喜欢的任何算法即可。 在这里,我采用了一个相当简单的算法。 我们需要找到仅具有一种边缘类型的顶点-入站或出站。 如果存在这样一个顶点,我们将其删除,删除边,然后继续进行此过程,我们将发现并证明该图中存在五个周期的周期。

同意,如果您通过代码查看它,也就是说,您仍然可以找到两个或三个长度的循环,但这更加不现实,我们在项目中确实有七个循环,但在生产中却没有。



关于未使用的模块。 还有一种相当简单的算法。 我们需要突出显示图中的连接组件,然后看一下,找到不包含任何入口节点的那些组件。 在这种情况下,事实证明这是连接性的组成部分,两个顶点都是两个节点。 然后称为entry.js。 实际上,无论它叫什么,这就是您在条目汇编配置手段中描述的内容。



但是还有另一种方法。 如果您尚未收集图形,而您仅拥有一个构建系统,那它是最便宜的方法吗? 让我们只标记在组装过程中进入组装的所有文件。 标记它们并创建许多。 之后,我们应该获得项目中所有文件的很多,然后将它们减去即可。 这是非常简单的操作。



现在,我不仅对您说一些理论性的事情,而且受到启发,来到了我的项目,并做到了。 和注意! 我什至没有删除node_modules。 我将此留作下一个评论的增长点。 简而言之,我受到了自己的启发,决定以某种方式制作这张幻灯片,然后重新安排。 让它看起来像这样,因为它真的很酷!

好的数字,你能想象一切都好吗? 然后我被逼进了草原,感觉就像是设计师,并认为这是我想添加到框架中的成就。 而且,正如您所知道的,我站起来,看着并意识到我很可能不是设计师,而是Web开发人员。 但是我不是傻瓜。 我把这个框架,添加到我的SEO护身符网站上。



您甚至可以使用链接。 这样一来,您就不会认为我在欺骗您-我们很坦诚-我真的看了评论。 我想你可以相信他们。



好吧,说实话,它看起来像这样。 我看到了一个新的hypo库thanos-js,接管了它,创建了一个池请求。 秘密地,我在我们的存储库中拥有管理员权限。 然后我把主人弄糊涂了。 你觉得怎么样 好吧,你和我很坦率,实际上,一切看起来都是这样。 如果没有人知道,thanos-js是一个仅会随机删除50%代码的库。



实际上,我还是在那儿使用了该库,但是该库的调用方式有所不同。 它被称为分光器,现在我们将与您讨论。 在这里,我想指出,池请求非常重要,减去了4.4万行代码,您可以想象-它第一次通过了测试。 也就是说,我所谈论的内容确实可以工作。



衍射器 实际上,他不仅从事删除未使用的模块,在图形中查找缺陷的任务,而且还从事更重要的任务。 我最初声明的是帮助开发人员和测试人员,现在我们将讨论它。 它的工作原理是这样的。

我们使用版本控制系统获取已修改文件的列表。 我们已经建立了一个图表-diffector建立了它。 对于每个这样的修改文件,我们寻找条目的路径并标记修改后的条目。 条目将与用户将看到的应用程序页面相对应。 但这是很合逻辑的。

这给了我们什么? 为了进行测试-我们知道应用程序中的哪些页面已更改。 我们可以告诉测试人员,只有他们才值得测试。 我们还可以告诉运行自动测试的ci-job,只有这些页面值得测试。 对于开发人员而言,一切都变得更加简单,因为现在测试人员无需写信给您,也不会问:“为什么需要测试?”



让我们看一个差异器如何工作的例子。 这里我们有一个目录,pages.desktop / *。 它仅包含页面本身的列表。 页面也由几个文件描述。 控制器是页面的服务器端。 视图是某种反应的一部分。 而且,这是来自另一个构建系统。 我们不仅有webpack,还有ENB。



我对该项目进行了一些更改,更改为一个空文件,您看到了该文件的结构。 这就是差异给我的。 我刚刚启动它,diffector是一个命令行应用程序。 我启动了它,他告诉我我已经更改了一页,称为BindBonusPage。



我还可以在详细模式下运行它,查看更详细的报告,并真正看到它至少在这种简单情况下有效。 如我们所见,在BindBonusPage中,索引文件和控制器已更改。

但是,让我们看看如果我们更改其他内容会发生什么。



我改变了其他东西。 分纸器告诉我,我已经换了九页纸。 而且这不再让我感到高兴,好像他不会真正帮助我一样。



让我们看看为什么? 现在,它显示了为什么认为此页面已被修改的原因。 , . - uikit.



diff. . , diffector . , - , .



, , . , , entry, , , test-scope, . .

. , , , , .



. - , . — i18n, , . , , , . , , - .

? , , , , , .



- . , B , , -2 . . , esm.



.



.



, value, . , , . , .

, AST, , 250 , , . , , - , , .



, - GlobalContext - , . , modify, , ? , - GlobalContext. . . , side effects. , , webpack, , . , webpack sideEffects: true, . .



, - - . , . diffector, . , , . — , . , .

, , diff, expand, log, , , , .



, . D, diff. , , , . , , . , , . .

, , . . . , — , . . . , , , , , . . , .

— diffector ? , , , .



- , , .



, , entry. entry. diffector.



. -. , .



, entry -, .



. diffector. . , , - , -. , . : , BindBonusPage, -, . . , , - . .





— CI. . : , , .



, . 43 — testing, , .



. , , .



, . : , , . , , , , , . , , - .



. , , , . - , - , , , . .

, , , output . , . — . — . 谢谢你

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


All Articles