以GitLab为例处理旧代码的方法

您可以无休止地欺骗GitLab是否是一个好产品。最好看一下这些数字:根据投资回合的结果,GitLab的估值为27亿美元,而之前的估值为11亿美元,这意味着快速增长,该公司将雇用越来越多的前端开发人员。

这是有关GitLab前端外观的故事。



这是GitLab中前端供应商数量的图表,从2016年开始,当时根本没有供应商,到2019年结束,当时已经有几十家供应商。但是GitLab本身已经存在了7年。因此,直到2017年,前端的主要代码都是由后端开发人员编写的,更糟糕​​的是,Ruby on Rails上的后端开发人员(在任何情况下我们都不想冒犯任何人,并在下面解释我们所谈论的内容)。

7年以来,无论您喜欢与否,任何项目都已过时。在某个时候,重构不再可能被推迟。然后旅程就开始了,充满了冒险,而其中的最后一点从未达到。Ilya Klimov说,关于GitLab中这是如何发生的。


关于演讲者:Ilya Klimov(an)GitLab的高级前端工程师。在此之前,他曾在初创公司和外包中工作,领导了一家小型外包公司。然后我意识到我还没有时间设计产品,就去了GitLab。

这篇文章基于FrontendConf的Ilya的报告,因此,其结构并没有太多地反映演讲者的经历。它看起来似乎过于对话,但从处理旧版的角度来看同样有趣。

与其他许多项目一样,在GitLab中,它们正在逐渐从旧技术迁移到更相关的东西:

  • JavaScript中的CoffeeScript。Ruby on Rails的开发人员在启动项目时不禁要注意CoffeeScript,它看起来与Ruby非常相似。
  • JQuery Vue. , JQuery. GitLab SPA. server-side rendering progressive enhancement, Vue-. , : Vue-, .
  • Karma Jest. Jest - . Karma , , .
  • REST GraphQL , , Vuex Apollo. Vuex, Redux Vue, Apollo local state , . GraphQL , .

同时,替换同时在多个方向进行,在项目中同时存在不同阶段的遗留代码。

现在,假设您要进入一个处于所有这些迁移中间的项目。对我而言,标准和参考点是您打开编辑项目并按保存按钮时的情况-您认为这是怎么回事?如果您认为我们是HTML的老噬菌体,那就不会。 JavaScript的来历是,您需要“演化”才能显示弹出窗口。这是我的遗留图片中的最低点。

进一步:jQuery,Vue组件中的自写类,以及作为最高点的,用Vuex,Apollo,Jest等编写的新的现代功能。

这就是我在GitLab上的贡献图。



其中-这对于理解故事的实质和我的所有痛苦非常重要-可以区分以下几个部分:

  • 在四月地区入职当我刚开始在GitLab工作时是“蜜月”。这时,为初学者提供了更轻松的任务。
  • 从4月底到5月中旬,只有几次提交-一段时期被拒绝:“不,不可能一切都以这种方式完成!” 我试图了解我不了解的地方,因此提交的次数很少。
  • 5月下半月很生气:“我不会对所有事情都该死-我需要转移产品,共享功能,并尝试对此做一些事情。”
  • 6月初(零提交)令人沮丧这不是一个假期,我看着并了解到我的手在跌落,我不知道该怎么办。
  • 之后,我同意了自己的看法,并决定聘请我为专业人士,这样我可以使GitLab变得更好。在六月和七月,我提供了大量改进。由于我们将要讨论的原因,并非所有人都引起了共鸣。
  • 现在,我正处于收养阶段,并清楚地了解:如何,在何处,为什么以及如何处理所有这一切。

我会更详细地告诉您从八月到十月的工作。老实说,在一家小型外包公司或一家初创公司中,在这三个月里,我被这样的生产力解雇了五次。

因此,在三个月内,我做到了:

  • 分段控制-三个按钮。
  • 存储本地历史记录的搜索字符串是一个稍微复杂的组件。
  • 微调器。并且此组件尚未冻结。



接下来,我们将逐步分析这种情况发生的原因以及如何忍受它。如果您觉得我很夸张,这是GitLab上挂在我身上的一些任务的屏幕截图(您可以直接查看GitLab,它是打开的)。



参见:错过了12.1,错过了12.2,错过了12.3。冲刺持续一个月,分段控制-3个冲刺。 Spinner仍然不见了,他将成为我们的主角。

重构的问题和重构的哲学在人类中已经存在了很长时间了-几千年了。现在我将证明:
« ; , , ; ; , .

, , , : ».

圣经告诉我们如何结合新旧功能。从管理的角度来看,报价的第二部分很有价值:无论您采取什么主动行动,都会遇到很大的阻力。

在萧条阶段,我看到了很多有关重构大型项目的报告,但是其中约有70%的内容使我想起了一个笑话。

Javist演讲:
-我们如何加快Java应用程序的速度?
-哦,所以我有报告了!你想告诉吗?
-可以告诉我,我会加快速度!

如果您仍然决定走上危险而摇摇欲坠的重构之路,那么我有一些为我自己开发的简单食谱,这些食谱可以在接近现实的条件下工作。

1.绝缘


为了加快工作速度,改进工作,重构工作,您需要将大象切成薄片,也就是将任务分成几部分。 GitLab非常大,我们有一个Slack频道“这是已知的”,人们在这里问诸如“这是错误还是功能,谁可以解释?”之类的问题。 -并不一定能找到答案。

一个简单的例子:GitLab中相同位置的屏幕截图,相差一天。



我很不高兴,因为我正在使用此按钮,而这是按钮的全部或另一种问题。

发生了什么?很简单:我们开发了一个设计系统,并且作为测试设计系统的单独故事书工具的一部分,我们禁用了全局CSS GitLab,以检查CSS组件与全局CSS的隔离方式。

总结:CSS不再保存至少在GitLab中。

我从事JavaScript已有14年了,从未见过一个项目,该项目至少需要一两年的时间来保存完全托管的CSS。顺便说一句,HTML也不能保存(肯定在GitLab中)。

manbetx客户端打不开已经开发了很长一段时间了。他们做出了使用Bootstrap的有争议的决定,因为Bootstrap提供了后端友好的布局系统。

但是,就组件隔离原理而言,Bootstrap是什么?这是大约600-700个遍历整个应用程序的全局类(实际上,每个CSS类都是全局的)。在可管理性方面,没有任何好处。

下一步操作(别称这是一个错误)-GitLab使用了Vue.js。选择是合理的,因为有了这三个框架,正是Vue使您能够最顺畅地重写某些内容。您无需立即抛出并剪切大型单页应用程序,但可以重写单个小节点。现在可以在Angular上完成,但是3-4年前,当Angular 2出现时,它不能在一个以上的实例中共存。现在,React也可以实现,但是所有这些魔术都缺少构建步骤,因此将规模推向了Vue。

结果,一种邪恶与第二种邪恶结合在一起。这很不好,因为Bootstrap样式对组件系统一无所知,而Vue组件是首先编写的。因此,做出了创建自己的设计系统的强烈意愿。我们叫它睡衣,但没人能向我解释原因。

我看到现在有越来越多的我们自己的设计系统,这很好。

设计系统涉及隔离,但是由于GitLab已经用Bootstrap编写,所以我们设计系统的大约50-60%是Bootstrap / Vue组件的包装,但功能有所降低。这是必需的,以便设计系统不会允许您错误地使用组件。如果我们谈论的是抽象库,那么灵活性就很重要,例如,制作所需按钮的能力。如果在GitLab中微调器可以是设计者认可的四种尺寸,那么您实际上就不应该让其他人这样做。

总有一天,好事必胜,我们将拥有一个重要的工具,当然,如果您在IE和Edge的支持方面得分很高,则可以有效地重构前端项目-这就是Shadow DOM。 Shadow DOM解决了全局样式流入组件的问题。请不要使用甚至Google都已经埋葬的Polymer。使用lit-element和lit-HTML,您可以使用自己喜欢的框架来构建孤立的样式。

您可以说React具有CSS模块,Vue具有范围相同的样式。请非常小心:CSS模块不能提供100%的隔离,因为它们仅适用于类。通过Vue中的作用域样式,当顶部组件的样式落入父级的根元素,并且在那里使用数据属性会减慢速度时,可以实现非常有趣的方案。

尽管我责骂Angular三年了,但现在我不得不承认,目前最好地实施了它。在Angular中,为了确保良好的样式隔离,只需切换隔离模式就足够了,并且,如有必要,可以使用Shadow DOM,否则可以使用普通仿真。

回到微调器。在与他作战的三个月中,有一段时间我从事一项令人兴奋的业务:清洁。



loading-container是微调器的实现细节,即,它是微调器实现中的类。由于不保存CSS,我们决定在睡衣中基于Atomic CSS创建单独的CSS。我个人并不真正喜欢Atomic CSS概念,但是我们拥有我们所拥有的。

就是说,我从事清理主产品代码中悬挂在实现细节元素上的样式。一切看起来都很简单-当然,因为在GitLab中进行了测试。

2.测试


GitLab中的测试涵盖所有代码,提供可靠性。因此,管道在98分钟内完成。



GitLab在GitLab.com上收集40%的公共竞赛者时间,因为GitLab会为每个合并请求收集管道。

我很受启发:我终于参加了一个项目,其中所有内容都包含在测试中!后端代码覆盖率接近100%,而我到达时的前端代码覆盖率达89.3%。

不幸的是,事实证明,这种覆盖大部分是垃圾,因为:

  • 在进行与组件无关的更改时发生故障;
  • 进行更改时不会中断。

我将举例说明。我们选择Jest是因为我们认为他在某些情况下将允许我们不要写断言,而是使用快照。问题是,如果您没有配置Jest并添加正确的序列化程序,则Vue Test Utils只会以HTML输出props。然后,例如,得出具有名称user的prop,该对象在参数中带有名称数据的prop,对象对象已传递给该对象。传输数据格式的任何更改都不会导致快照失败。

Ruby开发人员习惯于进行测试,大致来说,涵盖了所有方法。
在测试Vue或React组件时,我们需要测试公共API的行为。
因此,我们对计算所得的属性进行了巨大的测试,在某些情况下未使用过,但是在其他情况下,从物理上讲,达到此计算状态将无法调用。特别感谢Vue,其中的模板是字符串,因此您无法计算模板的测试覆盖率。在Vue 3中,将显示Source Maps并具有修复它的功能,但不会很快。

幸运的是,有一项简单的技能可以让您有效地重构遗留物。这是编写大型测试世界中所谓的固定测试的能力。

钉扎测试


此测试试图捕获您正在重构的行为。请注意,固定测试很可能最终不会提交到存储库。也就是说,您通过各种改进(例如,使用登台环境),为自己编写了描述组件渲染方式的测试。重构之后,固定测试应该生成相同的HTML,这很可能是一个好兆头,或者您应该了解发生了什么更改。

我将举一个生活的例子。几个月前,我进行了合并请求审核,并重构了一个下拉列表。传统上下文是这样的:早先,为了通过下拉列表中的破折号将朋友的分支彼此分开,仅传递了文本字符串“ divider”。因此,如果您的分支机构称为分频器,那么您就不走运了。在重构过程中,一个人在一个HTML节点中交换了两个类,这已投入生产并毁了它。公平地说,当然不是完全生产,而是分期演出,但尽管如此。

结果,当我们开始编写这样的测试时,我们发现,尽管测试覆盖率指标很酷,但测试的编写却不正确。因为,首先,我们对Karma(即旧的)进行了测试。其次,几乎所有测试都对组件内部进行了假设。也就是说,它们假装为单元测试,并且基本上像端到端一样工作,检查是否正在渲染具有特定类的特定标签,而不是检查是否正在使用特定道具渲染特定组件。了解区别:类是组件吗?

结果,我的18个合并请求和重构测试共进行了8到9 000行,结果总变更日志约为2万,因为减少了1.1万。



同时,出于某种原因,我正式对所有这些测试进行了重做:删除有关微调器类的断言,而是检查是否在此处渲染了具有正确道具的微调器。

乍看之下,这是一项令人费力的工作。但是,针对正确的架构重写测试非常容易向企业出售。GitLab是一种商业获利产品。当然,如果您告诉产品经理您需要三个迭代来重写20个测试,请猜测将您发送到哪里。另一件事:“我需要三个迭代来重写测试。这将使我们能够更有效地引入微调器,并加快未来设计系统新元素的实施。” 在这里,我们变得很重要。

3.抵抗


GitLab设计系统中还有更多我的微调器正在等待的另一个功能-这些是普通的SVG图标。


我们有由设计师绘制的图标用于主要项目,但由于GitLab的童年时期很艰苦,因此它们并未出现在设计系统中。例如,在2019年,CSS并不是通过Webpack收集的,而是由一个名为Sprockets的片断收集的-这是管道Ruby,因为我们需要在后端和前端重用相同的CSS。因此,图标必须以不同的方式连接到不同的项目。因此,有人将主代码库重构了三个月,以便您可以将图标从设计系统连接到相关项目。

这里有一个重要的地方,您将不可避免地遇到。重构是一个持续改进的过程。但是迟早您必须停止。
停止而不完成重构,但是获得可衡量的具体改进是绝对正常的。
但是,如果您正在从事旧项目,则不可避免地会遇到这样做的人。

这意味着他们以旧的方式书写,因为他们已经习惯了。例如,我们的支持者说:“我不想教您开玩笑。我已经为Karma编写了三年的测试,我需要添加新的功能,并且由于没有测试它们将无法使用功能,因此这里是针对Karma的测试。”

您的任务是尽可能地抵抗这一点。这是相对容易打架的,但有更大的罪过。有时在重构过程中,您会遇到问题,并且希望将其放在一边。

也就是说,仅由于某些原因而无法进行重构就可以替换新的拐杖。例如,如果我们在将图标集成到主代码库时遇到问题,我们可以保留一个实用程序类,该类将从全局Application CSS中提取。正式地,业务任务将得到解决,但是在实践中,就像Lernean hydra的历史一样:存在8个错误,有4个已修复,剩余13个。

重构就像维修房屋一样,不可能完成,只能停下来。
重构的前80%花费了20%的时间,其余80%的重构(正像那样)又花费了80%的时间。
请勿在重构过程中引入新的hack,这一点很重要。相信我,在开发过程中,它们本身会出现。

4.工具


幸运的是,即使在我到达之前,GitLab也走上了引入优质工具的正义之路:Prettier,Vue Test Utils,Jest。尽管Prettier的执行方式不正确。

我将说明有什么危险。尽管从历史上我弄清楚了什么以及为什么,但我的搜索中有80%涉及到37,000行美化代码的提交。使用历史记录几乎是不可能的,我必须为VS Code配置该插件,以便在搜索更改历史记录时将排除此提交。

当然,工具很重要,但是您需要仔细选择它们。例如,我们有Vue,而Vue有一个很好的测试工具-Vue Test Utils。但是,如果Vue 2是在2-3年前发布的,那么Vue Test Utils仍然没有脱离beta版本。而且,根据内部人士的信息,目前Vue Test Utils的唯一开发人员并未在Vue上进行编写。

在选择工具的过程中,您会碰到命运,并尝试赢得胜利。

GitLab的CoffeeScript在儿童时期受伤。这就是为什么即使在GitLab的TypeScript中也无法推动编写理论的想法。一切都分解成一个简单的论点:当编译成JavaScript的语言死亡后,它是否会与CoffeeScript有所不同。
选择工具时,请尝试更换工具,或者在极端情况下,应独立维护。
我们在GitLab使用了一个很酷的东西叫做Danger。

这是他们2019年网站的真实屏幕截图但是,同事们说,实际上在2019年该网站可能看起来像任何东西。

Danger是一种漫游器,它位于CI中的短绒与书面指南之间的中间状态。该机器人可以扩展,并且可以拉取请求,或者在正确调用我们的情况下合并请求并留下如下注释:
  • “此文件中有一个ESlint禁用注释,请对其进行修复。”
  • “此文件曾经由此人统治。也许您需要对此进行审查。”

我认为,这是一个很好的,重要的和可扩展的框架,用于监视代码库的状态。

5.抽象


我将从一个例子开始。几个月前,我看到了一条消息:“ GitHub摆脱了jQuery。我们已经走了很长一段路,不再使用jQuery。” 自然,我认为我们还需要摆脱GitLab中的jQuery。



快速搜索显示jQuery使用了300个文件。它看起来很吓人,但没什么-眼睛在害怕,手在做。但不是!jQuery是GitLab代码库中不可或缺的粘合剂,因为我们有Bootstrap。

Bootstrap最初是用jQuery编写的。这意味着,例如,如果您需要在Bootstrap中捕获下拉打开事件,则这是一个jQuery事件。您不能本地截取它。

这是使用遗留代码时应该抽象的第一件事。如果您有无法抛弃的jQuery,请编写自己的Event Emitter,它将隐藏在jQuery事件的内部。

当光明的未来到来时,我们可以删除jQuery,但是对不起,您现在需要集中精力govnokod。在常规的遗留项目中,它均匀地分布在整个代码中。将其收集在标有“没有化学防护服的情况下切勿进入”标记的瓶颈中。

6.指标


您无法做一些其结果无法衡量的事情。在manbetx客户端打不开,我们衡量我们所做的一切,以客观地了解代码是否运行得更好。



例如,我们有一个从Karma测试(蓝色)到Jest(绿色)的迁移计划:

您会看到逐步的进展。我们有很多这样的时间表。但重要的是要了解并非总是一切都好。

我将再举一个例子(报告中的演示从这一刻开始)。



这是GitLab生产中通常的合并请求界面。显然,我们可以折叠文件,单击标题,文件将开始折叠。

您怎么看,收起50行文件需要多长时间,而采用第八代Core i7的机器却为了获得最佳性能而扭曲了吗?部署需要多长时间?

文件崩溃所需的时间为7到15秒。部署立即发生。在重构之前,两者的工作速度相当快。

这就是为什么拥有指标非常重要的原因。

我会告诉你这里发生了什么。这就是Vue,它的反应性系统会跟踪该值:如果更改,则会调用所有依赖于该值的依赖项。每行都是Vue组件,由很多东西组成,因为您可以在一行上放置注释,可以从服务器动态加载注释,等等。所有这些都订阅了Vue商店,这也是Vue组件。

当您关闭合并请求时,例如,更新商店时,所有2万个商店订阅都需要更新。如果该行被删除,则必须将其从依赖项中删除。然后是简单的数学运算:您需要查看由2万个元素组成的数组,以查找需要删除的元素。假设有500条这样的行,每行是几个组成部分。结果是一个数学运算O(N),即O(20,000)* 500。 JavaScript一直都在运行。

部署可以立即进行,因为添加依赖项只是对阵列的推动,即数学运算O(1)。

有时,提高代码质量会降低性能和其他指标。衡量它们非常重要。

总结:隔离错误代码并跟踪指标。

legacy — . , – TechLead Conf — , . – , legacy Python, PHP.

, ++, , FrontendConf. .

All Articles