创建和维护通用组件是一个必须让许多团队参与的过程。 Yandex通用组件服务负责人Vladimir Grinenko
tadatuta解释了他们的开发如何超出了专门的Lego团队,我们如何使用Lerna在GitHub上建立了一个单一存储库,以及如何使用直接在CI中实现的服务来设置Canary版本,需要什么以及做什么。还有。

“很高兴欢迎大家。” 我的名字叫弗拉基米尔(Vladimir),我在Yandex界面中做一些普通的事情。 我想谈谈他们。 也许,如果您不十分深入地使用我们的服务,您可能会遇到一个问题:我们都在排版什么? 有什么要排版的?


搜索结果中有一个答案列表,有时在右侧有一列。 你们每个人一天之内都会应付。 如果您还记得有不同的浏览器等等,那么我们又增加了一天来修复错误,然后每个人都会去解决。

有人会记得仍然有这样的界面。 考虑到所有的小事,你可以给他另一个星期再去做。 迪玛(Dima)告诉我们,我们很多人需要一所自己的学校。 所有这些人一直在组成页面。 他们每天上班排版,想像吗? 显然还有别的东西。

实际上,Yandex中的服务确实更多。 甚至比这张幻灯片还多。 每个此类链接的后面是一堆变化很大的不同接口。 它们用于不同的设备,使用不同的语言。 他们有时甚至在汽车和其他各种奇怪的事物中工作。

如今,Yandex不仅是网络,还不仅仅是具有仓库,交货等所有不同商品。 汽车黄骑。 不仅可以吃什么,也不仅可以吃铁。 不仅是各种自动情报。 但是,以上所有方面的统一是因为每个项目都需要接口。 通常-非常有钱。 Yandex是数百种不同的庞大服务。 我们每天都在不断创造新事物。 我们有数千名员工,其中包括数百名前端开发人员和界面开发人员。 这些人在不同的办公室工作,生活在不同的时区,新员工不断上班。
同时,我们力所能及,力求使其单调,统一。

这是在Internet上搜索文档。 但是,如果我们切换到发行映像,尽管标头是一个单独的存储库,但标头仍会匹配,该存储库由完全独立的团队(甚至可能使用其他技术)组成。 看来那里很复杂? 好吧,他们做了两顶帽子,就像一件简单的事情。 瓶盖中的每个按钮都有其自己独立的丰富内部世界。 这里会出现一些弹出窗口,也可以在此处弹出一些消息。 所有这些都翻译成不同的语言,并在不同的平台上运行。 在这里,我们从图片转到视频,这又是一个新服务,另一个团队。 另一个仓库。 但是帽子还是一样的,尽管有区别。 所有这些都必须统一。
像这样在幻灯片上进行切换,以确保像素上没有任何东西,这有什么价值? 我们试图防止这种情况的发生。

为了进一步显示比例,我截取了存储库的屏幕快照,其中仅存储新浏览器的前端代码-仅存储文档的输出,而没有图片和视频。 有成千上万的提交和近400个贡献者。 这仅在布局中,只有一个项目。 这是您经常看到的蓝色链接的列表。
我的领导者Sergey Berezhnoy非常喜欢这个故事,因为既然我们在公司中聚集了很多东西,我希望我们的互动就像在JavaScript中一样一起工作:一加一等于二。

我们正在努力从交互中获得一切。 在这种情况下首先想到的是重用。 这里,例如,视频搜索结果中服务上的视频片段。 这是带有签名和其他一些不同元素的图片。

如果您进一步看,这是通常的文件发行。 但是在这里,也有完全相同的代码段。

或者说,有一个单独的Yandex.Air服务,该服务略少于完全由类似片段组成的服务。

或者说,通知程序中的视频片段,位于门户网站的不同页面上。

或者,这里是将视频片段添加到收藏夹中,然后在收藏夹中观看的视频片段。

听起来像吗? 显然,似乎。 那又怎样 如果我们真的让服务轻松地将完成的组件集成到其他门户服务中,那么显然,由于用户可以与不同站点上的数据进行交互,因此该服务将吸引更多的用户。 太好了 用户也将从中受益。 他们将平等地看到相同的事物。 它们将照常运行。 也就是说,人们不必一再猜测设计师在这里的想法以及如何与之交互。
最后,该公司将从中获得明显的节省。 此外,似乎-组成视频缩略图和某种签名的东西/实际上,要使视频缩略图像这样,您需要进行许多不同的实验,测试不同的假设,选择大小,颜色,缩进。 可能添加一些元素,然后删除,因为它们没有飞行。 而发生的事情,真正起作用的是一个非常漫长的过程的结果。 而且,如果每次在每个新的地方都重新做一次,这都是很大的努力。
现在想象一下。 假设我们得到了一些效果很好的产品。 他们在任何地方,任何地方都实施了此计划,然后他们进行了新的实验,并意识到可以改进的地方。 同样,我们必须重复整个实施链。 贵的

好吧,重用似乎很明显。 但是现在我们必须解决许多新问题。 您需要了解将此类新代码存储在何处。 一方面,这似乎是合乎逻辑的。 这里有一个视频片段,由视频团队制作,他们的项目中有一个存储库。 大概应该放在那儿。 但是如何将其分发到所有其他人的其他存储库中呢? 如果其他人想将自己的东西带到此片段中? 再次不清楚。
有必要对它进行版本控制。 您无法更改任何内容,因此,瞧,这一切都会突然推出。 需要测试一些东西。 此外,例如,我们在视频本身的服务上对此进行了测试。 但是,如果将其集成到另一服务中时出现故障怎么办? 再次不清楚。
最后,有必要以某种方式确保对不同服务的足够快的交付,因为如果我们在以前的版本中有了新的地方,这将很奇怪。 用户似乎单击了同一件事,并且有不同的行为。 而且我们需要以某种方式为不同团队的开发人员提供机会来更改此类通用代码。 我们需要以某种方式教他们如何使用这一切。 我们有很长的路要走,以方便重用接口。

我们从远古时代开始,回到了SVN,它像灯一样方便,是一个带有HTML的父亲,就像在Bootstrap中一样。 您将其复制到自己。 父亲旁边有样式,那里是某种JS,然后他们知道如何简单地显示/隐藏某些东西。 仅此而已。

组件列表看起来像这样。 此处突出显示了负责授权的b-domeg。 也许您仍然记得,在Yandex上,确实有这样一种带有屋顶的登录名和密码表格。 我们将其称为“房子”,尽管它暗示了邮件信封,因为它们通常会输入邮件。
然后,我们提出了一种能够支持通用接口的整体方法。

公司内部的图书馆本身已通过搜索和任何分类法获得了自己的网站。

现在,存储库如下所示。 您将看到近一万次提交和100多个贡献者。

但这是新世代中B屋的文件夹。 现在她看起来像这样。 您自己的文件夹中已经有超过半个屏幕的文件夹了。

因此,该网站今天看起来不错。

结果,共享库在Yandex内的360多个存储库中使用。 并且有不同的实现,调试的发布周期等。在这里,我们似乎有了一个公共库,现在让我们在任何地方使用它,一切都很好。 在任何地方引入普通物品的问题都得到解决。 不完全是

在已经有现成代码的阶段尝试解决重用问题为时已晚。 这意味着从设计者绘制服务的布局,将其分发给服务,尤其是分发给处理通用组件的团队的那一刻起,已经过去了一段时间。 最有可能的是,这一次结果将使得在每个单独的服务或至少其中的几个服务上,也组成了相同的接口元素。 他们以自己的方式组成。
而且即使通用解决方案稍后出现在共享库中,它也仍然可以解决,因此您现在必须重新实现在每种服务上要完成的所有操作。 这又是一个问题。 证明这一点非常困难。 这是团队。 她有自己的目标,一切都已经很好。 我们说了这样的话-看,最后我们有了一个共同的小东西,接受它。 但是团队就是这样-我们已经有足够的工作。 我们为什么需要它? 而且,突然之间有些事情不适合我们吗? 我们不想。
实际上,第二个大问题是关于这些超酷新组件是什么的信息的传播。 正因为有这么多的开发人员,他们忙于日常工作。 他们有机会坐下来研究共同领域中正在发生的事情,无论实际上意味着什么。
最大的问题是,根本不可能用一个专门的团队来解决所有服务的共同问题。 也就是说,当我们有一个处理视频的团队并使用视频制作自己的摘录时,很显然,我们将与他们达成一致,并在某个集中式库中进行摘录。 但是在不同的服务上直接有成千上万个这样的例子。 当然,这里没有双手是足够的。 因此,唯一的解决方案是每个人都应该始终处理通用组件。

很奇怪,您需要从界面开发人员而不是设计师开始。 他们也明白这一点。 我们内部同时进行了几次尝试,以使该过程收敛。 设计师制作设计系统。 我真的希望早晚有可能将它们简化为考虑到所有需求的单个通用系统。

现在有几个。 令人惊讶的是,任务完全相同:加快开发过程,解决一致性问题,不重新发明轮子并且不重复已完成的工作。

解决信息交流问题的一种方法是允许开发人员了解其他团队,其中包括处理通用接口组件的团队。 我们通过一个训练营来解决这一问题,当一个开发人员出现在Yandex上时,它首先允许他去不同的团队工作8个星期,看看它是如何工作的,然后选择在哪里工作。 但是在这段时间里,他的视野将大大扩大。 他将被指引到那里。
我们谈论了普通的事情。 现在,让我们看一下一切看起来如何接近开发过程。 假设我们有一个称为Lego的公共库。 我们想在其中实现一些新功能或进行某种修改。 我们修复了代码并发布了版本。
我们需要在npm中发布此版本,然后转到使用该库的某个项目的存储库并实现此版本。 最有可能的是,这将修复package.json中的一些数字,然后重新启动程序集。 也许甚至重新生成包锁,创建拉取请求,看看测试如何通过。 我们会看到什么?

最有可能的是,我们将看到已发生错误。 因为很难预测在不同服务上使用组件的所有方式。 如果那件事发生了,那么我们的出路是什么? 因此,我们意识到它并不合适。 我们继续重做。 我们使用共享库返回存储库,修复错误,发布新版本,将其发送到npm,进行部署,运行测试,这是什么? 很可能会再次发生错误。
当我们在一项服务中实现它时,这仍然很好,并且一切都立即崩溃了。 当我们完成所有这些工作并在十个不同的服务中实施它时,这真是令人遗憾。 那里什么都没有打破。 我们已经去煮冰沙,或者需要什么。 目前,版本是在第11个项目或第25个项目中引入的。 而且有一个错误。 我们沿着整个链条返回,打补丁并在之前的所有20个服务中实施。 此外,此修补程序可能会在以前的修补程序之一中爆炸。 等等。 玩的开心
看来唯一的出路是非常快速地编写大量代码。 然后,如果我们极有可能非常非常快地运行,那么迟早我们将设法有时间推出没有错误的版本。 但是随后会出现一个新功能,什么也救不了我们。
知道了 实际上,该方案可能类似于以下内容。 自动化将为我们提供帮助。 实际上,这就是整个故事。 我们想到了可以根据单存储库方案构建具有公共库的存储库的想法。 您可能遇到过,现在有很多这样的项目,尤其是基础设施项目。 当内部有许多不同的npm软件包时,各种各样的Babel以及类似的东西都像单一存储库一样生活。 它们可能以某种方式相互连接。 而且,例如通过Lerna对其进行管理,因此,在给定依赖关系的情况下,可以方便地发布所有这些信息。
正是根据该方案,可以组织一个项目,在该项目中为整个公司存储所有共同点。 可能有一个图书馆,该图书馆由一个单独的团队负责。 并且,其中可能包括每个单独服务开发的软件包,但他希望与其他人共享。

然后电路看起来像这样。 开始没有什么不同。 一种或另一种方式,我们需要更改通用代码。 然后,在自动化的帮助下,我们希望不仅在此代码旁边运行测试,而且还希望立即在所有嵌入了该通用代码的项目中运行测试。 并查看其汇总结果。
然后,即使在那里发生了错误,我们仍然没有设法发布任何版本,也没有在任何npm中发布它,我们还没有专门实现它,我们还没有付出所有这些额外的努力。 我们看到了一个错误,立即在本地对其进行了修复,再次运行了常规测试,这些都已投入生产。

在实践中看起来像什么? 这是带有修复的请求请求。 在这里,您可以看到自动化程序在那里要求必要的审阅者来检查代码中的所有内容是否正确。 确实,审阅者来了并同意一切都很好。 此时,开发人员只需在pull请求中编写一个特殊的/ canary命令。
机器人来了-说,好的,我为下一个奇迹创造了任务。 奇迹是,具有这些更改的金丝雀版本现已发布,并且已在使用此组件的所有存储库中自动实现。 就像在此存储库中一样,在此启动了自动测试。 在这里,您可以看到已经启动了许多检查。
每个测试的后面可以是另外一百个不同的测试。 但是重要的是,除了我们可以分别写入组件的本地测试之外,我们还为实现该组件的每个项目启动了测试。 集成测试已经在那里启动:我们检查该组件在服务上所构想的环境中是否正常工作。 这已经真正确保我们没有忘记任何东西,也没有对任何人破坏任何东西。 如果这里一切正常,那么我们真的可以放心发布一个版本。 如果这里有什么不好的地方,我们会在这里修复。
看来这应该对我们有帮助。 如果您的公司有类似的产品,您会发现某些零件可以重复使用,但是由于没有自动化,现在您必须重新安排它们,我建议您使用类似的解决方案。

我们得到了什么? 用于重建短绒的一般单一存储库。 也就是说,每个人都以相同的方式编写代码,它具有各种测试。 任何团队都可以来,放置其组件并使用JS单元测试对其进行测试,并使用屏幕截图进行覆盖,等等。 我提到的聪明的代码审查。 多亏了丰富的内部工具,在这里真的很聪明。
开发人员现在在休假吗? 将他叫入请求请求是没有意义的;系统会考虑到这一点。 开发商病了吗? 系统还将考虑到这一点。 如果两个条件均未满足且开发人员似乎有空,则他将在自己选择的一个使者中收到通知。 他就是这样:不,现在我正忙于紧急事情或开会。 他可以来那里,只需编写/ busy命令。 系统将自动了解您需要分配列表中的下一个。
下一步是发布相同的金丝雀版本。 也就是说,在进行任何代码更改后,我们都需要发布一个服务包,以便可以检查不同的服务。 接下来,在部署到所有这些服务时,我们需要运行测试。 当所有这些放在一起时-启动发行版。
如果更改影响应从CDN加载的某些静态数据,则需要自动将其单独发布。 . , , , , . , , , changelog - .
, , , , . , , .
, . . , , : , ? . , . . , .