强大的JavaScript:追逐神话

JavaScript通常被称为“最流行的语言”,但似乎没有人将JS开发称为“最安全的”,并且生态系统中潜伏的问题很多。 如何有效地绕过他们?



当错误(从字面上看)非常昂贵时, Ilya Klimov对此进行了思考-结果在HolyJS上进行了演示。 而且,由于观众的评论非常好,因此我们现在为Habr准备了此报告的文本版本。 在剪切下-文本和视频。



大家好 我叫Ilya Klimov,来自乌克兰哈尔科夫。 我有自己的小型外包公司,最多可容纳十个人。 我们所做的一切都是花钱的……从某种意义上说,我们在各个领域都使用JavaScript进行编程。 今天,在谈到可靠的JavaScript时,我想在过去的一年中分享我的最佳实践,因为这个话题开始引起我的严重关注。

从事外包工作的人员将理解以下幻灯片的内容:



当然,我们将要谈论的所有与现实无关。 正如他们在南方公园所说,所有角色都被宠坏了,可悲的是。 当然,那些涉嫌违反保密协议的地方是与客户代表商定的。

没有什么像我自己公司的创立那样影响我对可靠性等方面的想法。 当您创办自己的公司时,突然发现您可以成为一个非常酷的程序员,可以有非常酷的家伙,但是有时会发生绝对不可思议的事情,这是一些不可能的事情,而且完全是疯狂的。

我有一个Ninja JavaScript教育项目。 有时候我会许诺。 有时我什至跟随他们。 我在2017年承诺,作为一个教育项目的一部分,将录制有关Kubernetes的视频。 我意识到我已经兑现了这个诺言,很高兴在12月31日兑现这一诺言。 我坐下来记录( 这是结果 )。

由于我喜欢录制尽可能接近真实的视频,因此我利用了真实项目的示例。 结果,在演示集群中,我部署了一个从实际生产中获得实际订单的东西,并将它们放入演示集群中的单独的Kubernetes数据库中。

自12月31日起,部分订单丢失。 为季节性而写:每个人都去喝茶。 当客户在1月12日至13日醒来时,该视频的总成本约为50万美元。我还没有这么昂贵的作品。



第二个示例:另一个Kubernetes集群。 新型的生态系统基础设施即代码:您可以通过代码和配置来描述一切,Kubernetes通过JavaScript外壳以编程方式进行抽动等等。 酷,每个人都喜欢。 部署过程稍有变化,有时需要部署新集群。 出现以下情况:

const config = { // … mysql: process.env.MYSQL_URI || 'mysql://localhost:3306/foo' // ... } 

你们中的许多人可能在配置中也有这行代码。 也就是说,我们从mysql变量中获取配置,或者从本地数据库获取。

由于部署系统中出现错字,事实证明该系统再次配置为生产环境,但是MySQL数据库使用了测试数据库-本地,用于测试。 这次花的钱更少了-仅$ 300,000。幸运的是,这不是我的公司,而是我担任咨询顾问的地方。



您可能会认为所有这些都不是您的前端,因为我谈到了DevOps(顺便说一句,我很佩服DevOops会议的名称,它完美地描述了本质)。 但是,我会告诉您另一种情况。

在联合国的主持下,埃塞俄比亚建立了一个控制兽医流行病的系统。 当一个人接触到它时,它包含一个界面元素,然后他手动输入坐标:何时何地发生某种疾病。

爆发了一些常规的口蹄疫(我不擅长奶牛疾病),操作员急着不小心按了两次“添加”按钮,使田野一片空白。 由于我们拥有JavaScript,并且JavaScript和类型都非常好,因此纬度和经度的空字段会很高兴地导致零坐标。

不,我们并没有将医生送往海洋,但事实证明,所有这些都是从聚类的角度计算出来的,以便显示在地图上,构建报告,进行分析并分析人员在后端的位置。 我们正在努力统一爆发点。

结果,该系统在白天瘫痪了,因为后端试图在包含这一点的情况下计算集群,因此所有数据都变得无关紧要,“ 400公里行驶”系列中的数据完全不负责任。 埃塞俄比亚400公里的路程令人难以置信。

总损失估计约为一百万美元。 在关于这种情况的报告中,写着“我们是不幸的情况的受害者”。 但是我们知道关键是JavaScript!



还有最后一个例子。 不幸的是,尽管这个故事是很久以前的,但我仍然无法命名公司。 但这是一家拥有自己的航空公司,自己的酒店等的公司。 她与企业进行非常互动的合作,也就是说,她为企业提供了单独的界面来预订机票等。

发生一次荒谬的事故,订购了一张从纽约到洛杉矶的机票,总价为999,999张。 该公司的系统很乐意购买自己航空公司的所有航班,发现座位不足,然后将数据发送给国际预订系统以弥补短缺。 国际订票系统看到约有950,000张机票的请求,很高兴将该航空公司从其系统中断开。

由于关机是异常事件,因此在七分钟内解决了该问题。 但是,在这7分钟内,必须支付的罚款费用仅为100,000美元。



幸运的是,这一切发生在一年以上。 但是这些案例让我思考了可靠性问题,并问了两个俄罗斯的原始问题:谁应该受到谴责以及如何应对?

为什么会这样:生态系统的青春


如果分析很多故事,您会发现关于JavaScript类似问题的故事要多于另一种编程语言。 这不是我的主观印象,而是对Hacker News进行新闻智能分析的结果。 一方面,这是一个时髦而又主观的资源,但是,另一方面,在编程领域中,很难通过fakap找到任何理智的资源。

此外,一年前,我举办了一场比赛,每天我必须解决算法问题。 由于无聊,我使用函数式编程在JavaScript中解决了它们。 我编写了一个完全纯净的函数,在当前的Chrome中,它可以正常运行1197次,而3次则产生不同的结果。 这只是TurboFan优化器的一个小错误,该优化器刚刚进入主Chrome。

当然,它是固定的,但是您了解:这意味着,例如,如果您的单元测试通过了一次,那根本不意味着它们将在系统中正常工作。 也就是说,我们执行了大约1197次代码,然后优化器出现并说:“哇! 热门功能! 让我们对其进行优化。” 并在优化过程中导致错误的结果。

换句话说,我们可以将生态系统的年轻人称为发生这种情况的首要原因之一。 JavaScript是一个新兴的行业,恰恰是在严重的编程领域中,在这种情况下,数百万次旋转会导致错误的代价由五到六个字符来衡量。

长期以来,JavaScript被视为玩具。 因此(不是因为我们不认真对待),我们仍然缺少工具。

因此,为了处理这个原因,这是我今天将要讨论的所有事情的基本基本原则,我试图制定我可以强加给我的公司或作为顾问转让给他人的可靠性规则。 俗话说,“第一原则是不要谈论可靠性”。 但是更严重的是...

可靠性规则1:必须自动化的一切都必须自动化


顺便说一下,包括和拼写检查:

隐藏文字
可靠性规则1:必须自动化的一切都必须自动化

这一切都从最简单的事情开始。 似乎每个人都在写Prettier很长时间了。 但是直到2018年,当我们将文件部分添加到git存储库时,我们大家都很好用并学会了使用git add -p这个东西,并且想要很好地格式化代码,例如在将其发送到主存储库之前。 众所周知的realinstaged实用程序具有完全相同的问题,该实用程序仅允许您检查已更改的那些文件。



继续播放队长证据:ESLint。 我不会问谁在这里使用它,因为整个观众举起手是没有意义的(嗯,我希望如此,我也不想失望)。 在ESLint中拥有自己自定义书面规则的人,请更好地举手。

此类规则是使人们在初级或类似水平上写作的项目中发生的混乱自动化的非常有效的方法之一。

我们大家都希望有一定程度的隔离,但是迟早会出现这样的情况:“看,这个助手Vasya在其组件目录中的某个位置卖得很近。 我不会把它拿出来,那么我会。” 神奇的词是“以后”。 这导致以下事实:项目中没有出现垂直依赖性(当上层元素连接下层元素时,下层元素永远不会爬上上层元素),但是组件A依赖于组件B,后者位于完全不同的分支中。 结果,组分A不那么容易被运输到其他组分。

顺便说一句,我对Alfa-Bank表示敬意,他们在React上有一个非常漂亮的书面组件库,很高兴在设计代码质量时准确地使用它。

常规的ESLint规则跟踪您从何处导入实体,该规则使您可以显着提高代码质量并在代码检查期间保存思维模型。

我已经从前端世界的角度来看。 最近,在哈尔科夫地区,一家大型且严肃的公司普华永道已经完成了一项研究,前端供应商的平均年龄约为24至25岁。 我已经很难考虑所有这一切,我想在请求请求审核期间专注于业务逻辑。 因此,我很高兴编写ESLint规则,以免去思考此类问题。

看来您可以为此调整通常的规则,但是现实通常会使情况更糟,因为事实证明,react组件需要一些Redux选择器(不幸的是,它仍然存在)。 这些选择器位于完全不同的层次结构中,因此为“ ../../../ ..”。

或更糟糕的是,Webpack别名破坏了其他工具的20%,因为并不是每个人都知道如何使用它。 例如,我心爱的Flow。

因此,在下一次想大三的时候(程序员喜欢这样的消遣),请考虑下一次是否可以以某种方式使它自动化,以免将来出错。 当然,在理想的世界中,您将编写任何人都不会阅读的说明。 这是HolyJS的发言人-具有丰富经验的才华横溢的专家,但是当有人提议在内部集会上为演讲者起草说明时,他们说“是的,他们不会读。” 这些都是榜样!

最后的平庸主义,然后转移到锡。 这些是运行预提交挂钩的任何工具。 我们使用husky ,我不禁插入了这张漂亮的husky照片,但是您可以使用其他东西。



如果您认为这很简单-就像他们说的那样,拿着我的啤酒,我们很快就会发现,一切都比您想象的要复杂。 还有几点:

打字


如果您不编写TypeScript,则可能需要考虑一下。 我不喜欢TypeScript,我一向以来都是高流量的Flow,但是我们稍后再讨论,但是从这个阶段开始,我将厌恶地推广主流解决方案。

为什么这样 TC39计划委员会最近就该语言的去向进行了大量讨论。 他们得出了一个非常有趣的结论:在TC39中,总有一种“天鹅,癌症和长矛”向不同的方向拖着他们的舌头,但是每个人都想要一件事,而永远都是表现。

TC39在一次内部讨论中非正式地发表了这样的观点:“我们将始终使JavaScript保持生产力,而那些不喜欢它的人将使用某种语言来编译成JavaScript。”

TypeScript是成人生态系统的不错替代品。 我不得不提到我对GraphQL的热爱。 不幸的是,这确实很好,没有人愿意在我们已经必须工作的大量现有项目中实施它。



在会议上已经有关于GraphQL的报告,因此,关于可靠性的问题只有一招:如果您使用Express GraphQL,那么除特定的解析器外,您每次都可以挂起某些验证器,以比起与之相比更加严格的价值要求。 GraphQL的标准类型。

例如,我希望某些银行的两名代表之间的转账金额为正。 因为不迟于昨天,我的网上银行弹出窗口欣然宣布我收到了-2条来自银行的未读邮件。 这有点像我国的领先银行。

至于这些验证器,这些验证器具有更高的严格性:使用它们是一个好主意,只是不要按照GraphQL的建议使用它们。 您会发现自己非常依赖GraphQL作为平台。 同时,您需要同时在两个地方进行验证:在发送和接收数据之前的前端,以及后端。

我经常要向客户解释为什么我们将JavaScript而不是X语言作为后端,而且X语言通常是某种PHP,而不是漂亮的Go之类。 我必须说明,由于它们是用相同的编程语言编写的,因此我们能够尽可能高效地重用代码,包括在客户端和后端之间。 不幸的是,正如实践证明的那样,这篇论文常常只是会议上的一句话,而在现实生活中却找不到。

合约


我已经谈论过生态系统的青春。 合同编程已作为主要方法存在了25年以上。 如果您使用TypeScript编写代码,那么像我一样使用Flow编写代码,则应该输入type-contract,这将带来非常重要的事情:描述从中导出静态类型的运行时合同的能力。



对于程序员而言,没有比拥有多个真理源更糟糕的了。 我知道人们损失了五位数的美元,仅仅是因为他们的类型是用静态类型的语言描述的(他们使用TypeScript-当然,这只是一个巧合)和运行时类型(它似乎使用了tcomb )略有不同。

因此,该错误未在编译时捕获,仅仅是因为为什么要检查它? 没有针对它的单元测试,因为我们由一个静态的分类器检查。 测试已经下一层验证的事物没有任何意义,每个人都记得测试层次。

由于这两个合同之间的同步随着时间的推移而中断,因此有一天,错误地转移了到错误地址的地址。 由于它是一种加密货币,因此不可能比原则上多一些回滚交易。 没有人会再为您着想。 因此,合同和合同编程中的交互是明天您应该开始做的第一件事。

为什么会这样:隔离


下一个问题是隔离。 它是多方面的和多方面的。 当我在一家酒店和航空旅行公司工作时,他们在Angular 1上安装了一个应用程序。那是很久以前的事情了,因此可以原谅。 一个由80人组成的团队致力于此应用程序。 测试涵盖了所有内容。 一切都很好,直到有一天我做了我的功能,没有冻结它,发现我在测试过程中弄坏了我什至没有碰过的系统上绝对不可思议的地方。

原来我的创造力有问题。 原来,我不小心将服务称为与系统中存在的另一个服务完全相同的服务。 由于它是Angular 1,并且没有严格键入服务系统,而是键入了字符串类型,因此Angular相当冷静地开始将我的服务放到完全不同的地方,具有讽刺意味的是,有两种命名方法同时出现。

当然,这不是巧合:您了解如果两个服务的名称相同,那么它们可能会做相同的事情(加或减)。 这是与计算折扣有关的服务。 只有一个模块正在忙于为公司客户计算折价,而第二个模块与我的名字相关,它与计算股票折扣有关。

显然,当有80个人看到该应用程序时,这意味着它很大。 在应用程序中实现了代码拆分,这意味着连接模块的顺序直接取决于用户在站点上的行程。 更有趣的是,碰巧没有一个能测试行为和用户通过网站的行为(即特定的业务场景)的端到端测试捕获此错误。 因为似乎没有人需要同时联系两个折扣模块。 没错,这完全使网站管理员的工作瘫痪了,但与之合作却没有发生。

部分解决此问题的项目之一的徽标很好地说明了隔离的问题。 这是莱娜。



Lerna是用于管理存储库中的多个npm软件包的出色工具。 当您手中拿着锤子时,一切都变得像钉子一样可疑。 当您拥有具有正确哲学的类Unix系统时,一切都变得像文件一样可疑。 每个人都知道,在UNIX系统上,所有内容都是文件。 像计划9一样,在某些系统中将其带到最高的程度(我几乎说过“荒谬至极”)。

我知道那些致力于确保大型应用程序的可靠性的组织已经提出了一个简单的想法:一切都是一个包。



当您在单独的包装中取出功能的某些要素(无论是组件还是其他要素)时,会自动提供隔离层。 仅仅因为您通常无法从一个包裹中拿到另一个包裹。 而且还因为用于处理通过npm-link或Yarn Workspaces在单一存储库中收集的软件包的系统在内部组织方式方面是如此糟糕且不可预测,您甚至无法求助于hack和连接某种通过“ node_modules something”文件,仅仅是因为不同的人将所有东西都放在不同的结构中。 这尤其取决于纱线的版本。 在那里,在其中一个版本中,他们悄悄地彻底改变了Yarn Workspaces如何使用包组织工作的机制。

显示问题是多方面的隔离的第二个示例是我现在尝试在所有地方使用的软件包-这是cls-hooked 。 您可能知道另一个实现相同功能的软件包-这是continuation-local-storage 。 它解决了一个非常重要的问题,例如,PHP开发人员没有面对过。

这是关于隔离每个特定的请求。 在PHP中,平均而言,在医院中,所有请求都是隔离的,只有在不使用共享内存之类的变态的情况下,它们之间才能交互,我们不能,一切都很好,和平,美丽。 本质上,cls-hooked添加了相同的内容,允许您创建执行上下文,将局部变量放入其中,然后最重要的是,自动销毁这些上下文,以免它们继续占用您的内存。

cls-hooked async_hooks, node.js , , . , .

, , node-.

#2: «»


, , . , JavaScript — , . grep-.

这是什么 vim. , - Language Server, - — , , grep. , , . grep- , grep. , , .

Sequelize . ORM . user.getProjects(). , getProjects? .



, Sequelize, , hasMany, belongsToMany. , , , . , .

code review, , . . — , .

, : «merge request 20 — 30 , merge request 5000 — looks good to me». , .

JavaScript Ninja , , , junior-, . « react redux», 8000 , 10 000 » , , « ». , , « », , merge request .

, , merge request , , Linux. , , -. , git. , . , , . .

- . . - , , - .

, . Microsoft Surface Linux, . , , . - .

:




« », « ». , React. React, Fiber — .

, Fiber ( OCaml) JavaScript, . , . , — , proposal, JavaScript. Scheduler — proposal stage 0.

, React , - . , : , DOM. — . , .



— Vue.js. Vue? , . Vue, , , .

Vue React. , Vue, , React.

. Vue , state, , state , . , . Vue .

. , , Web Components c , , . : , pop-up, , , - . — .

Vue — scoped slots. , - . scoped slot — , . . React Render Proper: , . Vue , -.

-, Vue . Vue : , child-, , scoped slots, forceUpdate. , child . .

React . , , shouldComponentUpdate(), . Vue , , , , . . Vue. , - .



Jest . Facebook. JavaScript: , , . . , .

, ECMAScript 2015 . , if, require. Require , . , , , . Jest Babel Require .

NGS- Node, proposal, . JavaScript : Require, . . , Jest c NGS- , , . , .

, , - . , Inversion of Control - Dependency Injection-.

IoC/DI


, , . Angular IoC/DI. React … , , React Vue? dan.church , evan.church .

, , React Dependency Injection, - . Vue inject provide. .

, , . , NestJS . InversifyJS . TypeScript, , , JavaScript. , .



Typescript, Inversify. , : inject (TYPES.Weapon) katana: Weapon. , , TYPES.Weapon Weapon? — .

, , , TypeScript ( Flow ) , inject dependency injection runtime, .

« »? Weapon , , TypeScript , . TypeScript , first-class citizen JavaScript, . , , runtime , katana Weapon. .

- C++, , , RTTI: run-time type information, , . C# Java reflection, . TypeScript, « », , RTTI.

. . Vue Vue 3 TypeScript, , Vue TypeScript , Microsoft: « , TypeScript, ?» , Vue, : props, this. , props , this, . TypeScript , . .

Microsoft , : Vue , Angular, . , TypeScript. React, , .

, . , Dart, mirrors, , Flutter. mirrors, , , .

Inversify, Babel-, , , , runtime-, , .

: . . , , , . , V8 .

V8 , . , , V8, , , . , V8 , TypeScript.

. , , .

#3: «» , «»


«» «» , , «» — , . , Vue, . - , - props' .

. typed-css-modules? : CSS, CSS Modules, .



typed-css-modules , , css-, .

12 , CSS- , . 11 12 undefined, CSS- , , , , undefined, .



Yeoman , , , . , , . Angular CLI, Blueprints (, Angular, GDG SPB , , ).




Anguar , , . .

, «» , «» — , . , «» — . , , , , , , .

, — . junior', , , , . , , React- .

, , , - ESLInt, - , , .

, , . , NestJS Sails.js .



, , , , , - -. Sails ORM, . Waterline — , . , Sails.js . blueprint , . , , .



Nest, — , . Dependency Injection, , middleweight .

. , , , , , : « - ?»

Angular, — , , dependency injection, , .

- Vue (, ), , Vue . Vue, - , — Nest. , , , , TypeScript.



:


, JavaScript. , , , , , .

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



Grafana, , — . , - , . speech recognition API Microsoft, , , 20 . , , , -.



: CI/CD. GitLab, , . GitLab, , JavaScript-friendly environment , , .

. , , … , -.



Blue/Green deployment: - , , . , , !


  • JavaScript , , . , , , JavaScript GitHub. — , , , .
  • JavaScript , ( , ). « , ». , . DI , « ?»
  • , . TypeScript, Flow, Rizen — , runtime exception, , .

    , — -, .

HolyJS, : HolyJS 24-25 . — , ( MobX) ( Chrome DevTools). — .

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


All Articles