O JavaScript é freqüentemente chamado de “linguagem mais popular”, mas parece que ninguém fala do desenvolvimento de JS como “o mais seguro”, e o número de problemas ocultos no ecossistema é grande. Como contorná-los de forma eficaz?
Ilya Klimov pensou nisso quando o erro foi muito caro (literalmente) - e, eventualmente, fez uma apresentação no HolyJS. E como as críticas do público foram excelentes, preparamos agora uma versão em texto deste relatório para Habr. Sob o corte - texto e vídeo.
Olá pessoal. Meu nome é Ilya Klimov, sou de Kharkov, Ucrânia. Eu tenho minha própria empresa pequena, de até dez pessoas, terceirizada. Fazemos tudo pelo que o dinheiro é pago ... no sentido, programamos em JavaScript em todos os setores. Hoje, falando sobre JavaScript confiável, quero compartilhar minhas melhores práticas em algum lugar durante o ano passado, pois esse tópico começou a me incomodar muito e com seriedade.
Quem trabalha com terceirização entenderá o conteúdo do seguinte slide:

Tudo o que falaremos, é claro, não tem nada a ver com a realidade. Como se costuma dizer em South Park, todos os personagens são mimados e miseravelmente. Naturalmente, os locais onde há suspeita de violação da NDA foram acordados com representantes de clientes.
Nada afetou meus pensamentos sobre confiabilidade e afins como base da minha própria empresa. Quando você inicia sua própria empresa, de repente acontece que você pode ser um programador muito legal, pode ter caras muito legais, mas às vezes coisas incríveis acontecem, algumas impossíveis e completamente loucas.
Eu tenho um projeto educacional Ninja JavaScript. Às vezes eu faço promessas. Às vezes eu até os sigo. Prometi em 2017, como parte de um projeto educacional, gravar um vídeo sobre o Kubernetes. Percebi que havia feito essa promessa e seria bom cumpri-la, 31 de dezembro. E sentei-me para gravar (
aqui está o resultado ).
Como gosto de gravar vídeos o mais próximo possível da realidade, aproveitei exemplos de um projeto real. Como resultado, no cluster de demonstração, implantei algo que recebia pedidos reais da produção real e os colocava em um banco de dados Kubernetes separado no meu cluster de demonstração.
Desde 31 de dezembro, parte dos pedidos desapareceu. Baixa para a sazonalidade: todo mundo foi beber chá. Quando o cliente acordou entre 12 e 13 de janeiro, o custo total do vídeo foi de cerca de US $ 500.000. Ainda não tive uma produção tão cara.

Exemplo número dois: outro cluster Kubernetes. Ecossistema de nova moda Infraestrutura como código: tudo o que pode ser descrito por código e configurações, o Kubernetes se contrai programaticamente a partir de shells JavaScript e assim por diante. Legal, todo mundo gosta. Uma pequena alteração no procedimento de implantação e chega um momento em que você precisa implantar um novo cluster. Surge a seguinte situação:
const config = { // … mysql: process.env.MYSQL_URI || 'mysql://localhost:3306/foo' // ... }
Muitos de vocês provavelmente também têm essa linha de código em suas configurações. Ou seja, pegamos a configuração da variável mysql ou pegamos o banco de dados local.
Por causa de um erro de digitação no sistema de implantação, verificou-se que o sistema foi novamente configurado como produção, mas o banco de dados MySQL usou um banco de dados de teste - local, que era para testes. Dessa vez, menos dinheiro foi gasto - apenas US $ 300.000. Felizmente, essa não era minha empresa, mas o lugar em que eu trabalhava como consultor engajado.

Você pode pensar que tudo isso não lhe interessa como front-end, porque eu falei sobre o DevOps (a propósito, admiro o nome da conferência do
DevOops , ela descreve a essência perfeitamente). Mas vou falar sobre outra situação.
Existe um sistema que controla epidemias veterinárias na Etiópia, desenvolvido sob os auspícios da ONU. Ele contém um dos elementos da interface quando eles chegam a uma pessoa e ele insere manualmente as coordenadas: quando e onde houve surtos de uma determinada doença.
Há um surto de alguma febre aftosa regular (não sou forte em doenças de vacas) e, com pressa, o operador pressiona acidentalmente o botão “adicionar” duas vezes, deixando os campos vazios. Como temos JavaScript, e JavaScript e tipos são muito bons, os campos vazios de latitude e longitude levam alegremente a zero coordenadas.
Não, não enviamos o médico para o oceano, mas tudo foi calculado anteriormente do ponto de vista do agrupamento para exibição no mapa, criação de relatórios, análises e análise da localização de pessoas no back-end. Estamos tentando unir os pontos de foco.
Como resultado, o sistema fica paralisado durante o dia, porque o back-end está tentando calcular o cluster com a inclusão desse ponto, todos os dados se tornam irrelevantes, pedidos completamente irresponsáveis da série "drive 400 kilometros" chegam aos médicos. 400 quilômetros na Etiópia é um prazer duvidoso.
A estimativa de perda total é de cerca de um milhão de dólares. No relatório sobre essa situação, estava escrito "Fomos vítimas de um conjunto infeliz de circunstâncias". Mas sabemos que o ponto é JavaScript!

E o último exemplo. Infelizmente, embora essa história tenha sido há muito tempo, ainda não sei o nome da empresa. Mas esta é uma empresa que possui sua própria companhia aérea, seus próprios hotéis e assim por diante. Ela trabalha de maneira muito interativa com as empresas, ou seja, fornece a elas uma interface separada para reservar ingressos e assim por diante.
Uma vez ocorrido um acidente absurdo, chega um pedido de reserva de ingressos de Nova York a Los Angeles no valor de 999.999 peças. O sistema da empresa comprou alegremente todos os voos de sua própria companhia aérea, descobriu que não havia assentos suficientes e enviou os dados ao sistema internacional de reservas para compensar a escassez. O sistema internacional de reservas, ao receber um pedido de aproximadamente 950.000 bilhetes, desconectou esta companhia aérea do seu sistema.
Como o desligamento é um evento fora do comum, o problema foi resolvido em sete minutos. No entanto, nesses sete minutos, o custo das multas a serem pagas era de apenas US $ 100.000.

Felizmente, tudo isso aconteceu em mais de um ano. Mas esses casos me fizeram pensar em questões de confiabilidade e fazer duas perguntas russas originais: quem é o culpado e o que fazer sobre isso?
Por que isso acontece: a juventude do ecossistema
Se você analisar muitas histórias, descobrirá que há mais histórias sobre problemas semelhantes com JavaScript do que com outra linguagem de programação. Esta não é minha impressão subjetiva, mas os resultados da análise inteligente de notícias no Hacker News. Por um lado, essa é uma fonte hipster e subjetiva, mas, por outro lado, é bastante difícil encontrar qualquer fonte sadia por fakap no campo de programação.
Além disso, há um ano, realizei uma competição na qual tive que resolver problemas algorítmicos todos os dias. Desde que eu estava entediado, eu os resolvi em JavaScript usando programação funcional. Eu escrevi uma função completamente pura e, no Chrome atual, funcionou corretamente 1197 vezes e 3 vezes produziu um resultado diferente. Foi apenas um pequeno erro no otimizador TurboFan, que acabou de entrar no Chrome principal.
Obviamente, foi corrigido, mas você entende: isso significa, por exemplo, que se seus testes de unidade passaram uma vez, isso não significa que eles funcionem no sistema. Ou seja, executamos o código cerca de 1197 vezes, então o otimizador veio e disse: “Uau! Recurso quente! Vamos otimizar ". E no processo de otimização levou ao resultado errado.
Em outras palavras, podemos chamar a juventude do ecossistema de uma das primeiras razões pelas quais isso acontece. O JavaScript é uma indústria relativamente jovem, precisamente no campo de programação séria, nos casos em que milhões giram, onde o custo de um erro é medido de cinco a seis caracteres.
Durante muito tempo, o JavaScript foi percebido como um brinquedo. Por isso (não porque não levamos a sério), ainda temos problemas com a falta de ferramentas.
Portanto, para lidar com esse motivo, que é o princípio fundamental de tudo o que falarei hoje, tentei formular as regras de confiabilidade que poderia impor em minha empresa ou transferir como consultor para outras pessoas. Como diz o ditado, "a regra número um não é falar sobre confiabilidade". Mas mais a sério, então ...
Regra de confiabilidade nº 1: tudo o que pode ser automatizado deve ser automatizado
Incluindo, a propósito, e verificação ortográfica:
Texto ocultoRegra de confiabilidade nº 1: tudo o que pode ser automatizado deve ser automatizado
Tudo começa com as coisas mais simples. Parece que todo mundo está escrevendo Prettier há muito tempo. Mas somente em 2018, tudo o que usamos, bom e sonoro, aprendemos a trabalhar com o git add -p quando adicionamos parcialmente arquivos ao repositório git, e queremos formatar o código de maneira agradável, digamos, antes de enviá-lo ao repositório principal. O bem conhecido utilitário realinstaged, que permite verificar apenas os arquivos que foram alterados, tem exatamente o mesmo problema.

Continuando a jogar Captain Evidence: ESLint. Não vou perguntar quem o usa aqui, porque não faz sentido para todo o público levantar as mãos (bem, espero que sim e não quero me decepcionar). É melhor levantar as mãos que possuem suas próprias regras escritas personalizadas no ESLint.
Essas regras são uma das maneiras muito poderosas de automatizar a bagunça que acontece em projetos em que as pessoas escrevem no nível júnior e afins.
Todos nós queremos um certo nível de isolamento, mas mais cedo ou mais tarde surge uma situação: “Veja, esse ajudante Vasya vendeu em algum lugar do diretório do seu componente muito próximo. Eu não vou tirar isso em comum, então eu vou. A palavra mágica é "mais tarde". Isso leva ao fato de que não dependências verticais começam a aparecer no projeto (quando os elementos superiores conectam os inferiores, os inferiores nunca sobem atrás dos superiores), mas o componente A depende do componente B, que está em um ramo completamente diferente. Como resultado, o componente A não é tão facilmente transportado para outros componentes.
A propósito, eu expresso meu respeito ao Alfa-Bank, eles têm uma biblioteca de componentes muito bem e muito bem escrita no React, é um prazer usá-lo precisamente em termos de design da qualidade do código.
A regra banal do ESLint, que monitora de onde você importa entidades, permite aumentar significativamente a qualidade do código e salvar o modelo mental durante a revisão do código.
Eu já sou do ponto de vista do mundo do front-end. Recentemente, na região de Kharkov, uma empresa grande e séria da PricewaterhouseCoopers concluiu um estudo, e a idade média do fornecedor front-end é de 24 a 25 anos. Já é difícil para mim pensar sobre tudo isso. Quero me concentrar na lógica de negócios durante uma revisão de solicitação de recebimento. Portanto, tenho o prazer de escrever regras ESLint para não pensar em tais coisas.
Parece que você pode ajustar as regras usuais para isso, mas a realidade geralmente perturba muito mais, porque acontece que alguns seletores Redux são necessários no componente de reação (infelizmente, ainda está vivo). E esses seletores estão em algum lugar de uma hierarquia completamente diferente, então "../../../ ..".
Ou, pior ainda, o alias do webpack, que quebra cerca de 20% de outras ferramentas, porque nem todo mundo entende como trabalhar com ela. Por exemplo, meu amado Flow.
Portanto, na próxima vez em que você quiser rosnar em um júnior (e o programador tiver um passatempo favorito), pense se você pode automatizar isso de alguma forma para não cometer erros no futuro. Em um mundo ideal, é claro, você escreverá instruções que ninguém lerá de qualquer maneira. Aqui estão os palestrantes do HolyJS - especialistas talentosos com grande experiência, mas quando foi proposto elaborar instruções para os palestrantes em um comício interno, eles disseram "sim, eles não o lerão". E estas são as pessoas para dar um exemplo!
O último do banalismo, e seguir em frente. Essas são as ferramentas para executar ganchos pré-confirmação. Usamos
husky , e eu não pude deixar de inserir esta linda foto husky, mas você pode usar outra coisa.

Se você acha que tudo isso é muito simples - como se costuma dizer, segure minha cerveja, em breve descobriremos que tudo é mais complicado do que você pensa. Mais alguns pontos:
Digitação
Se você não está escrevendo TypeScript, pode pensar nisso. Não gosto do TypeScript, tradicionalmente fluxo de alto fluxo, mas falaremos sobre isso mais tarde, mas aqui do estágio promoverei a solução mainstream com nojo.
Porque O comitê de programa do TC39 recentemente teve uma discussão muito grande sobre onde o idioma geralmente vai. Uma conclusão muito engraçada a que chegaram: no TC39, há sempre um “cisne, câncer e lúcio” que arrasta a língua em direções diferentes, mas há uma coisa que todo mundo quer e sempre é o desempenho.
O TC39 informalmente, em uma discussão interna, divulgou este discurso: "Nós sempre criaremos o JavaScript para que ele permaneça produtivo, e aqueles que não gostam dele usarão algum tipo de linguagem que se compila no JavaScript".
O TypeScript é uma boa alternativa ao ecossistema adulto. Não posso deixar de mencionar meu amor pelo GraphQL. É realmente bom, infelizmente, ninguém permitirá que seja implementado em um grande número de projetos existentes, onde já temos que trabalhar.

Já havia relatórios sobre o GraphQL na conferência, portanto, há apenas um golpe especificamente para a questão da confiabilidade: se você usar, digamos, o Express GraphQL, a cada vez, além de um resolvedor específico, poderá suspender certos validadores que permitem aumentar os requisitos de valor em comparação com tipos padrão de GraphQL.
Por exemplo, gostaria que o valor da transferência entre dois representantes de alguns bancos fosse positivo. Porque o mais tardar ontem, o pop-up do meu banco na Internet anunciou com alegria que eu tinha -2 mensagens não lidas do banco. E é como um banco líder no meu país.
Quanto a esses validadores, que impõem rigor adicional: usá-los é uma boa e sólida ideia, apenas não os use conforme sugerido, digamos, pelo GraphQL. Você se vê muito apegado ao GraphQL como plataforma. Ao mesmo tempo, a validação que você faz é necessária em dois locais ao mesmo tempo: no front-end antes de enviar e receber dados e no back-end.
Eu regularmente tenho que explicar ao cliente por que pegamos o JavaScript e não a linguagem X como back-end. Além disso, a linguagem X geralmente é algum tipo de PHP, e não o Go bonito e similares. Eu tenho que explicar que somos capazes de reutilizar o código da forma mais eficiente possível, inclusive entre o cliente e o back-end, devido ao fato de que eles foram escritos na mesma linguagem de programação. Infelizmente, como mostra a prática, muitas vezes essa tese permanece apenas uma frase na conferência e não encontra personificação na vida real.
Contratos
Eu já falei sobre a juventude do ecossistema. A programação de contratos existe há mais de 25 anos como uma abordagem primária. Se você escreve no TypeScript, faça io-ts, se você escrever no Flow, como eu, faça o contrato digitado e obterá uma coisa muito importante: a capacidade de descrever contratos de tempo de execução dos quais derivar tipos estáticos.

Não há pior para um programador do que ter mais de uma fonte de verdade. Conheço pessoas que perderam quantias de cinco dígitos em dólares simplesmente porque seu tipo é descrito em uma linguagem com digitação estática (eles usaram TypeScript - bem, é claro, isso é apenas uma coincidência) e o tipo de tempo de execução (parece ter usado
tcomb ) ligeiramente diferente.
Portanto, o erro não foi detectado em tempo de compilação, simplesmente porque por que verificá-lo? Não houve testes de unidade para isso, porque fomos verificados por um tipificador estático. Não faz sentido testar coisas que foram verificadas pela camada abaixo, todos se lembram da hierarquia de testes.
Devido ao fato de a sincronização entre esses dois contratos ter sido interrompida ao longo do tempo, um dia uma transferência incorreta foi feita para o endereço que foi para o endereço errado. Como era uma criptomoeda, é impossível reverter uma transação um pouco mais do que em princípio. Ninguém vai bifurcar o ar por sua causa novamente. Portanto, contratos e interações na programação de contratos são as primeiras coisas que você deve começar a fazer amanhã.
Por que isso acontece: isolamento
O próximo problema é o isolamento. É multifacetado e multifacetado. Quando eu trabalhava para uma empresa de hotéis e viagens aéreas, eles tinham um aplicativo no Angular 1. Isso foi há muito tempo, então é desculpável. Uma equipe de 80 pessoas trabalhou nesta aplicação. Tudo foi coberto em testes. estava tudo bem até que um belo dia eu fiz meu recurso, não o congelei e descobri que havia quebrado, durante os testes, lugares absolutamente incríveis no sistema em que nem sequer toquei.
Acabou que tenho problemas com a criatividade. Aconteceu que eu acidentalmente chamei o serviço exatamente o mesmo que outro serviço existente no sistema. Como era Angular 1, e o sistema de serviço não foi estritamente digitado, mas digitado estritamente foi digitado, Angular calmamente começou a deslizar meu serviço em lugares completamente diferentes e, ironicamente, dois métodos de nomeação coincidiram.
Obviamente, isso não foi uma coincidência: você entende que, se dois serviços são nomeados da mesma forma, é provável que eles façam a mesma coisa, mais ou menos. Era um serviço relacionado ao cálculo de descontos. Apenas um módulo estava ocupado calculando descontos para clientes corporativos, e o segundo módulo com meu nome estava relacionado ao cálculo de descontos em ações.
Obviamente, quando o aplicativo é serrado por 80 pessoas, isso significa que é grande. A divisão de código foi implementada no aplicativo, e isso significa que a sequência de conexão dos módulos dependia diretamente da viagem do usuário no site. Para torná-lo ainda mais interessante, aconteceu que nem um único teste de ponta a ponta que testou o comportamento e a passagem do usuário pelo site, ou seja, um cenário de negócios específico, detectou esse erro. Porque parece que ninguém precisará entrar em contato com os dois módulos de desconto ao mesmo tempo. É verdade que isso paralisou completamente o trabalho dos administradores do site, mas com quem isso não acontece.
O problema do isolamento é muito bem ilustrado pelo logotipo de um dos projetos que resolve parcialmente esse problema. Aqui é Lerna.

O Lerna é uma excelente ferramenta para gerenciar vários pacotes npm em um repositório. Quando você tem um martelo nas mãos, tudo se torna suspeito como um prego. Quando você tem um sistema unix com a filosofia certa, tudo se torna suspeito como um arquivo. Todo mundo sabe que em sistemas unix tudo é um arquivo. Existem sistemas nos quais ele é levado ao mais alto grau (eu quase disse "ao ponto do absurdo"), como o
Plano 9 .
Conheço organizações que, tendo sintonizado para garantir a confiabilidade de um aplicativo gigante, tiveram uma idéia simples: tudo é um pacote.

Quando você remove algum elemento da funcionalidade, seja um componente ou qualquer outra coisa, em um pacote separado, você fornece automaticamente uma camada de isolamento. Só porque você normalmente não pode alcançar outro de um pacote. E também porque o sistema de trabalhar com pacotes coletados em um único repositório via npm-link ou Yarn Workspaces é tão terrível e imprevisível em termos de organização interna, que você nem pode recorrer a um hack e conectar algum tipo de arquivo através de "node_modules something", simplesmente porque pessoas diferentes têm tudo isso em uma estrutura diferente. Isso depende especialmente da versão do Yarn. Lá, em uma das versões, eles silenciosamente mudaram completamente o mecanismo de como o Yarn Workspaces organiza o trabalho com pacotes.
O segundo exemplo de isolamento para mostrar que o problema é multifacetado é o pacote que estou tentando usar em qualquer lugar agora - isso é
viciado em cls . Você pode estar ciente de outro pacote que implementa a mesma coisa - isto é
continuation-local-storage . Resolve um problema muito importante que, por exemplo, não é enfrentado, por exemplo, pelos desenvolvedores em PHP.
É sobre isolar cada solicitação específica. Em PHP, temos, em média, em um hospital, todos os pedidos são isolados, interagindo entre eles apenas se você não usar nenhuma perversão como a Memória Compartilhada, não podemos, tudo está bem, pacífico, bonito. Em essência, cls-hooked adiciona a mesma coisa, permitindo que você crie contextos de execução, coloque variáveis locais nelas e, o mais importante, destrua automaticamente esses contextos para que eles não continuem consumindo sua memória.
Esse cls-hooked é baseado em async_hooks, que no node.js ainda estão em status experimental, mas eu conheço uma ou duas empresas que os usam em produção bastante severa e são felizes. Há uma ligeira queda no desempenho, mas a capacidade de coletar lixo automaticamente não tem preço.Quando começamos a falar sobre problemas de isolamento, sobre como colocar coisas diferentes em diferentes módulos de nós.Regra de confiabilidade nº 2: código incorreto e “errado” deve parecer errado
Com o primeiro critério que deduzi para mim, há dez anos não teria concordado. Porque você diria que o JavaScript é uma linguagem dinâmica e me priva de toda a beleza e expressividade da linguagem. Este é um teste grep.O que é isso 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). — .