A qualidade do código é um tema que nasceu com a programação. O ISO 9000 é usado para avaliar e controlar a qualidade do gerenciamento corporativo, o GOST e o mesmo ISO são usados para produtos, mas não há código GOST para avaliação da qualidade. Também não há definição exata e padrão para a qualidade do código.

Cada desenvolvedor entende a qualidade à sua maneira, com base na experiência. As opiniões dos joons e do lead são diferentes, e isso leva a desacordo. Cada equipe de projetos individuais avalia o código à sua maneira. A equipe está sendo atualizada, os desenvolvedores estão saindo, os líderes da equipe estão mudando - a definição de qualidade está mudando.
Ivan Botanov (
StressoID ), do Tinkoff.ru, desenvolvedor de front-end, tutor on-line do Angular, palestrante em reuniões e conferências, tutor do YouTube e, às vezes, treinador de empresas, tentará ajudar a resolver esse problema.
Ao decifrar o relatório de Ivan no
Frontend Conf, falaremos sobre legibilidade, nomeação, declaratividade, estilo de código e indiretamente abordaremos as relações de joons e leads: erros, rakes e "queima" de timlids.
Isenção de responsabilidade: prepare-se mentalmente, haverá
muito código incorreto no texto
, extraído de um site "especial" .Um pouco de história
Todos nós escrevemos de maneiras diferentes. Você deve ter notado isso quando mudou seu local de trabalho, projeto ou equipe - coisas incomuns são imediatamente evidentes. Isso também aconteceu comigo muitas vezes, razão pela qual este relatório nasceu. Eu me concentrei em desenvolvedores iniciantes, mas o artigo será útil para quem já gerencia mais do que escreve ou personaliza os processos de desenvolvimento. Sobre o que vamos falar:
- Sobre os problemas do código ilegível.
- Vamos discutir a nomeação.
- Vamos ver quais são as diferenças entre o estilo declarativo e o imperativo e quais são os problemas.
- Sobre modularização e digitação de código.
- Sobre o estilo do código e a dívida técnica, sobre confirmações e fluxo git.
- Sobre as ferramentas que você pode usar e como corrigir erros no produto.
Antes de começar, farei a pergunta:
"Quantos programadores o ônibus deve decolar para que o projeto pare de se desenvolver?" . A resposta correta: todos.
O que é um fator de barramento?
Condicionalmente, Petya ou Vasya estão trabalhando em equipe, que sabem tudo sobre o projeto: vão até ele, perguntam sobre isso e aquilo, como funciona aqui e como funciona lá. Todo mundo depende da Petya, o número do barramento do projeto é um. Quanto menor o número, mais difícil é desenvolver o projeto, porque todo mundo distrai Petya, e ele é legal, deve executar as tarefas e não responder a perguntas.
Você dirá
como é legal ser Pete! Todo mundo o ama, o aprecia, precisa dele.Isto não é verdade. Normalmente, Petya é um líder de equipe e deve lidar com outras tarefas: discutir o desenvolvimento do projeto, construir arquitetura, gerenciar a equipe, mas não vá às dunas e explique por que está escrito aqui e não o contrário.
Se o código estiver limpo e bom, é bom lê-lo e há menos perguntas dos jones.
O código limpo aumenta a viabilidade do projeto e reduz o limite de entrada . Quando novas pessoas aparecem na equipe, elas fazem menos perguntas. Nesse projeto,
é mais fácil atrair desenvolvedores devido ao baixo limite de entrada.
O código de qualidade aumenta os números de ônibus.
Legibilidade
A legibilidade é afetada pelo recuo, nomeação distorcida e aninhamento forte - muitos projetos sofrem com isso. Além da indentação, operadores ternários multilinha, a falta de um estilo único de código, uma combinação de abordagens de desenvolvimento e uma redefinição óbvia de variáveis reduzem a legibilidade. Todas essas são as causas mais comuns de baixa legibilidade do código.
Para mim, identifiquei o termo
código linear - é um código que pode ser lido como um livro.
O código linear é lido da esquerda para a direita, de cima para baixo, sem precisar retornar ao código escrito anteriormente.
Um exemplo desse código:
list.forEach((element1) => { if (element1.parent_id == null) { output.push(element1); list.forEach((element2) => { if (element2.parent_id == element1.id) { output.push(element2); list.forEach((element3) => { if (element3.parent_id == element2.id) { output.push(element3); list.forEach((element4) => { if (element4.parent_id == element3.id) { output.push(element4); } }) } }) } }) } })
Esse código é linear, mas tem outro problema - está fortemente aninhado. Portanto, também devemos monitorar o aninhamento.
Exemplo de código
não linear :
if(!brk && html.childNodes[0].value && html.childNodes[0].max) { if(clear) html.childNodes[0].value = 1; else if(html.childNodes[0].value <= html.childNodes[0].max) { ++ html.childNodes[0].value; if(brk) { for(id = 1; id < html.childNodes.length; ++ id) findActive(html.childNodes[id], true); html.parentNode.className = ""; } return null; } else { html.parentNode.className = "Ready"; html.className = ""; return html; } }
Se descartarmos tudo o que é supérfluo e descobrirmos o que quebra a linearidade, veremos algo assim:
if (condition) { return; } else { return; }
Quando existe mais, primeiro você precisa examinar o que está escrito em um lugar, depois em outro. Se for um caso grande ou fortemente aninhado, a atenção será dispersa e o código será difícil de ler.
Como reduzir o aninhamento e obter código linear?
Combine condições . Essa é a coisa mais simples que podemos fazer - aninhadas se eu puder combinar em condições e reduzir levemente o aninhamento.
Foi:
if (isUser()) { if (isAdmin()) { console.log('admin'); } }
Tornou-se:
if(isUser() && isAdmin()) { console.log('admin'); }
Aplique um
padrão de retorno antecipado . Ele permite que você se livre completamente dos outros. Posso substituir o método ou um trecho de código por if-else com um retorno antecipado e, em seguida, um bloco de código ou outro será executado. Isso é muito conveniente - você não precisa rolar e retornar a algumas partes do código.
Foi:
if (isAdmin()) { return admin; } else { return user; }
Tornou-se:
if (isAdmin()) { return admin; } return user;
Aplique a corrente da promessa . Este é um código típico do transferidor. Eu escrevi testes E2E não faz muito tempo, e esse código machucou meus olhos:
ptor.findElement(protractor.By.id('q01_D')).click().then(() => { ptor.findElement(protractor.By.id('q02_C')).click().then(() => { ptor.findElement(protractor.By.id('q03_D')).click().then(() => { console.log('done'); }); }); })
Com a cadeia de promessas, o código gira:
ptor.findElement(protractor.By.id('q01_D')).click() .then(() => { return ptor.findElement(protractor.By.id('q02_C')).click(); }) .then(() => { return ptor.findElement(protractor.By.id('q03_D')).click(); }) .then(() => { console.log('done'); });
Se usarmos o conhecimento da função de seta, podemos fazer o seguinte:
ptor.findElement(protractor.By.id('q01_D')).click() .then(() => ptor.findElement(protractor.By.id('q02_C')).click()) .then(() => ptor.findElement(protractor.By.id('q03_D')).click()) .then(() => console.log('done'));
É legível, declarativo, bonito - tudo é claramente visível.
Ordem superior observável. Como sou angular e uso o RxJS, estou enfrentando o problema de aninhamento forte de
Observable aninhado por código espaguete, ou seja, assinaturas aninhadas. Há um fluxo e, dentro do fluxo, você precisa obter o valor e, em seguida, algo a ver com outro fluxo. Alguns escrevem assim:
Observable.of(1,2,3) .subscribe(item => { item += 2; Observable.of(item) .subscribe(element => { element += 1; }) })
E isso realmente afeta projetos adultos. Você pode fazer isso:
Observable.of(1,2,3) .mergeMap(item => Observable.of(item + 2)) .mergeMap(element => Observable.of(element + 1)) .subscribe()
Aplicando o conhecimento da API RxJS, nos afastamos de aninhamentos fortes, graças à
ordem superior observável , e chegamos ao
declarativo . Essa coisa, que lança o valor do fluxo interno para o externo, é tudo. Mas é limpo, linear, bonito e não investido.
Operadores ternários aninhados
Na minha opinião, o pior que pode ser encontrado no código são
operadores ternários aninhados . Reescreva-os em instruções condicionais de bloco, tente não usá-las. Não falaremos sobre condições implícitas - isso é um fracasso.
Um exemplo de operadores ternários aninhados e condições implícitas:
arr.length > 0 ? arr[1] == 1 ? arr[1] = 2 : arr[1] = 1 : console.log('empty arr'); !a && b && func()
Eu escrevi este ternário simples em 5 minutos. Ele tem o comprimento da matriz e algumas operações, mas é difícil de ler, porque em algum lugar uma pergunta, em outro lugar - tudo não está claro. Este código pode ser reescrito usando operadores ternários aninhados em várias linhas:
arr.length > 0 ? arr[1] === 1 ? arr[1] = 2 : arr[1] = 1 : console.log('empty arr');
Você dirá:
-
tudo bem!Viu?- é
visível!E o que você diz sobre isso:
return query instanceof RegExp ? (function () { fn.each(function (id) { if (id.match(query)) { seatSet.push(id, this); } }); return seatSet; })() : (query.length == 1 ? (function (character) {
Alguém escreveu e apoiou este ternarnik. Ao acessar essa seção de código, será difícil descobrir onde o ponto de interrogação começa e onde termina. Se você usa ternários, não invista um no outro - isso é ruim.
Nomeação
Outro código incorreto:
var _0x30119c = function() { var _0x3af68e = { 'data': { 'key': 'cookie', 'value': 'timeout' }, 'setCookie': function(_0x3543f3, _0x13e5c1, _0x586dac, _0x1c9d63) { _0x1c9d63 = _0x1c9d63 || {}; var _0x47b83f = _0x13e5c1 + '=' + _0x586dac; var _0xae3be = 0x0; for (var _0xae3be = 0x0, _0x5d2845 = _0x3543f3['length']; _0xae3be < _0x5d2845; _0xae3be++) { var _0x440369 = _0x3543f3[_0xae3be]; _0x47b83f += ';\x20' + _0x440369; var _0x411875 = _0x3543f3[_0x440369]; _0x3543f3['push'](_0x411875); _0x5d2845 = _0x3543f3['length']; if (_0x411875 !== !![]) { _0x47b83f += '=' + _0x411875; } } _0x1c9d63['cookie'] = _0x47b83f; } };
Podemos dizer que esse código é
ofuscado , mas mesmo assim: vemos que existe uma função compreensível, um 'dado' claro, o setCookie faz alguma coisa e, em seguida, é apenas um cobertor e nada é claro - algo é
concatenado , em algum lugar espaço. Tudo está muito ruim.
O que você precisa considerar ao nomear
Use a
notação CamelCase :
camelCaseNotation
.
Não há transliteração, todos os nomes dos métodos estão apenas em inglês :
ssylka, vikup, tovar, yslyga
ou
checkTovaraNaNalichieTseni
é uma falha. A propósito, escrevi quando estava começando a programar.
Nenhum item, dados, el, html, arr , especialmente ao iterar através de matrizes. Por exemplo, para uma variedade de produtos ou ofertas, escolha nomes amigáveis:
product, offer, etc
A diferença entre item e produto não é tão grande, mas a legibilidade é maior. Mesmo se você tiver uma função de uma linha que acrescente algo, um nome adequado para os negócios aumentará a legibilidade.
private_property
para propriedades privadas :
private_property
. Eu adicionei essa regra porque escrevi o TypeScript pelo segundo ano, mas não há modificadores de acesso em JS e, na convenção de nomenclatura, concordamos que o sublinhado define propriedades privadas para outros desenvolvedores.
Constantes em letras maiúsculas :
const BLOCK_WIDTH = 300;
e
nomes de classes em maiúsculas: class SomeClass
. Eu escrevo no TypeScript e tudo fica claro lá, tudo está claro no
ES6 , mas também há projetos legados nos quais todas as classes de função com o
new
operador escrevem em maiúsculas.
Nenhuma variável de uma letra :
u = user
. Esta é uma referência ao
i - não. Escreva de forma clara, ou seja, funcionalmente nos negócios. Não há necessidade de fazer o método Check, que verifica alguma coisa, mas o que não está claro. Escreva os
nomes de addProductToCard(); sendFeedback()
dos métodos :
addProductToCard(); sendFeedback()
addProductToCard(); sendFeedback()
.
Imperatividade
Uma pequena digressão. A imperatividade apareceu simultaneamente com a programação. Naquele momento, eles codificaram o Assembler e escreveram imperativamente: cada comando, cada etapa foi descrita em detalhes e uma célula de memória foi atribuída ao valor. Vivemos em 2019 e não escrevemos mais em JS.

Este é um código simples, mas imperativo, que possui um loop for, variáveis. Não está claro por que eles foram adicionados aqui.
for (let i = 0; i >= 10; i++) { const someItem = conferences[i]; const prefixString = 'Hello '; if (someItem === 'Frontend Conf') { console.log(prefixString + someItem); } }
Problemas imperativos de código
: muitas variáveis, muitas construções de manutenção dessas variáveis e muitos comentários, porque essas variáveis precisam ser descritas de alguma forma - você não pode criar uma variável e esquecê-la. Tudo isso afeta a legibilidade do código.
Declaratividade
O estilo declarativo foi substituído. Escrevemos em JavaScript e está disponível para nós. O estilo declarativo é assim:
conferences .filter(someItem => someItem === 'Frontend Conf') .forEach(someItem => console.log('Hello' + someItem));
É o mesmo que no imperativo, mas muito mais simples e mais compreensível.
Benefícios de um código declarativo
Esse código é mais fácil de ler, manter e testar, e construções de código complexas podem ser ocultadas por trás de métodos e abstrações. Você pode entender as diferenças entre os estilos imperativo e declarativo pelo exemplo da fritura de ovos. Para fritar ovos fritos em um estilo imperativo, pegamos uma frigideira, colocamos no fogo, despejamos óleo, pegamos um ovo, quebramos, despejamos. Em estilo declarativo, dizemos: "Ovos fritos", e o processo ficará oculto por trás das abstrações. Queremos fritar ovos mexidos, não para descobrir como funciona.
Os problemas começam quando desenvolvedores não muito experientes vêm da universidade onde estudaram Pascal e escrevem assim:
const prefix = 'Hello '; conferences .forEach(someItem => { if (someItem === 'Frontend Conf') { const result = prefix + someItem; console.log(result) } });
Essa é uma
combinação de estilos declarativo e imperativo. Não há legibilidade, nenhum imperativo completo, algumas variáveis e
if
. Isso
if
pessoa adicionou porque simplesmente não sabia sobre o filtro. Se você é um líder e vê esse código, apareça, pique um link
com um stick e traga o código para declarativo.
Criando variáveis
Não crie variáveis para o bem de variáveis - isso é uma má ideia. Quando descobri pelos desenvolvedores por que eles estão fazendo isso, ouvi:
- Bem, aumenta a legibilidade!
O que aumenta a legibilidade aqui -
const username = user.name
? Se você deseja criar uma variável, dê um nome ao significado. Por exemplo, temos uma expressão regular:
const somePattern = /[0-9]+/; str.split(somePattern); const someResult = a + b - c;
Aqui, eu criaria uma variável para que uma pessoa não perca tempo nos procedimentos, mas leia que isso verifica regularmente o telefone e vai além. Se você possui operações matemáticas, escreva também para uma variável, porque, com certeza, a operação matemática possui uma entidade comercial, um determinado marco comercial, por exemplo, para calcular a cesta ou fazer um desconto. Nesse caso, você pode criar uma variável.
Criar uma variável para criar variáveis não vale a pena.
Redefinição não óbvia de variáveis
Suponha que tenhamos criado uma variável de
element
, a partir do nome da qual não está claro o que é. Escrevemos um
element
DOM, escrevemos sua substituição em uma matriz por algum motivo e saímos:
let element = document.getElementById('someId'); arr.forEach(item => {
Tudo está bem, foi esquecido. Após Petya, que trabalha em nossa equipe, entrou e adicionou o bloco
if
. O que é isso? Apenas redefiniu a variável novamente e saiu. E o escopo já é diferente. Quando o próximo desenvolvedor tentar entender esse código, especialmente se o método for grande, ele aguardará
someId
ou
someItem
, e não existe. Este é um lugar onde você pode perder muito tempo procurando qual é o problema. Vamos escrever um
debugger
, colocar um
brake point
, ver o que está lá - em geral, não escreva assim.
Divisão em métodos
Consideramos brevemente a divisão em métodos e passamos suavemente às abstrações.
Os métodos devem ter
funcionalidade atômica :
um método - uma ação . Se você tiver uma ação de uma linha, não a misture ainda, simplesmente porque o método é muito pequeno. O método não deve ter
mais que 10 linhas. Esta declaração provoca um holivar e agora também "dispara", então escreva para mim ou nos comentários, e explicarei por que escrevi essa regra.
Modularidade do código
A modularidade
melhora a legibilidade do código dividindo-se em abstrações,
ajuda a "ocultar" códigos difíceis de ler, é
mais fácil de testar e mais fácil de
corrigir erros . Vou explicar com mais detalhes.
Escondendo-se atrás de abstrações
Por exemplo, existe um código que cria um botão, atribui um ID a ele, uma classe e clica nele - tudo é simples.
const element = document.createElement('button'); element.id = 'id_button'; element.classList = 'red'; document.body.appendChild(element); element.click();
Você pode adicionar uma função ao código do botão, quebrá-lo e usar a função
createButton
ao criar o botão:
const.button = createButton('id_button'); button.click(); function createButton((id') { element = document.createElement('button'); element.id = id; element.classList = 'red'; document.body.appendChild(element); return element; }
Pelo nome "falante", fica claro o que a função faz e esse ID é passado. Se queremos criar um botão e não entender como ele é criado e por que, escrevemos código usando essa função.
button.component.js let button = createButton('id_button'); button.click();
Em seguida, escrevemos
helper , que mais tarde é usado por outros desenvolvedores. Se eles querem entender como os ovos mexidos são fritos ou querem mudar a receita - adicione ou remova sal, eles aparecerão e reverenciarão.
button.helpers.js function createButton(id) { let button = document.createElement('button'); button.id = id; button.classList = 'red'; document.body.appendChild(element); return button; }
Digitação
Não vou falar sobre digitação por um longo tempo - há vários relatórios. Eu escrevo em TypeScript, gosto, mas ainda há fluxo e outras ferramentas. Se você não tiver digitado seu projeto, é hora de implementá-lo. Isso ajuda a depurar vários erros.
O cheiro do código
Os odores de código estão muito entrelaçados com o meu tema, porque escrever código de baixa qualidade gera os mesmos odores. Olhe o
relatório legal de Alexei Okhrimenko , ele aborda esse tópico em detalhes.
Estilo do código
Este é o conjunto de regras de equipe, projeto ou empresa às quais os desenvolvedores aderem. Um bom
estilo de código contém exemplos de código bom e ruim. Pode ser escrito em qualquer ferramenta e local conveniente. Temos este Wiki e, para uma pequena empresa, um arquivo no Word é suficiente. Você também pode usar o estilo de código pronto, que já é usado por outras empresas:
JQuery ,
Google ou
Airbnb - o estilo de código mais popular.
Se você usa uma tecnologia ou estrutura específica, elas geralmente também têm seu próprio estilo de código, que vale a pena dar uma olhada. Por exemplo, em Angular, este é o
Guia de Estilo Angular ou o
Guia de Estilo React / JSX do Airbnb.
Este é um exemplo do nosso estilo de código.

Aqui está uma seção para criar variáveis e descreve como não fazer e como fazer.
Dívida técnica
Esse é um tipo de pagamento pelo fato de que em algum momento cortamos a grama. Freqüentemente nasce uma dívida técnica quando não temos tempo para concluir uma tarefa e escrevemos um lembrete para retornar a ela mais tarde. Nos casos que não estão relacionados às funcionalidades de negócios, isso é, por exemplo, atualizando a estrutura.
A dívida tecnológica gera muletas e um código de baixa qualidade.
Por causa da dívida técnica, escrevo códigos ruins e muletas. O próximo desenvolvedor analisará isso, só isso, verá as ombreiras e acrescentará outra muleta: "Se ainda houver algum suporte, não há nada errado". A dívida tecnológica gera muletas, a qualidade é perdida, o que gera novamente muletas e elas aumentam ainda mais a dívida tecnológica.
Existe uma
teoria de "janelas quebradas" . Se uma janela quebrada aparecer no edifício e não for alterada, depois de um tempo uma segunda janela quebrada aparecerá, um terceiro, grafite. As pessoas vêem que ninguém está seguindo o prédio e a punição por janelas quebradas não deveria estar. Então é o código. Muitas vezes, em projetos herdados, o código é cercado de muletas, porque os condicionais Petit e Vasya veem muletas e pensam: "Está tudo bem, eu serei o primeiro". Portanto, em empresas normais, a dívida técnica recebe tempo suficiente - elas recebem uma cota ou um sprint técnico que resolverá o problema. Se você é um líder, ou de alguma forma influencia os processos de construção de sprints e a lista de tarefas que são levadas para o trabalho, preste atenção à dívida técnica - isso é importante.
Repositório
Vamos discutir as mensagens confirmadas. A figura mostra exemplos de mensagens reais que eu vi em diferentes projetos. Qual deles você acha que é informativo?

Resposta corretaAs mensagens informativas estão em blocos, mas “adicionou um recurso”, “corrigiu um bug” - não são informativas.
Confirmar mensagens
Eu escrevo no
WebStorm e adoro. Nele, você pode configurar o realce dos números das tarefas, a transição quando você clica no Rastreador de tarefas é legal. Se alguém não usa o WebStorm, é hora de, porque com ele, as mensagens de confirmação são obtidas em alta qualidade. O que é uma mensagem de confirmação de qualidade? Este é um commit, no qual há
um número de tarefa e uma declaração curta, mas sucinta, da
essência das mudanças : "fez um novo módulo", "adicionou um botão", "adicionou um recurso em que o componente é criado" e não um "recurso adicionado" sem rosto. Ao visualizar confirmações, ficará claro onde o componente foi adicionado e onde o bug foi corrigido. Mesmo nas mensagens de confirmação, é necessário indicar o
tipo de alterações : recurso, correção de bug, para que fique claro dentro do qual as alterações ocorreram.
Gitflow
Vou falar brevemente sobre o Gitflow. Descrição detalhada na
tradução do artigo de Vincent Driessen . O Gitflow é um dos modelos de gerenciamento de repositório mais populares e muito bem-sucedidos, com as
principais ramificações - desenvolvimento, master, pré-produção, prod e
ramificações temporárias : recurso, bug, lançamento. Quando iniciamos a tarefa, desviamos o ramo do recurso do ramo de desenvolvimento. Depois de passar a revisão de código na ramificação do recurso, a colocamos de volta no desenvolvimento. No final, coletamos os lançamentos de desenvolvimento e lançamento no master.

As ferramentas
O primeiro é o
Commitizen . Este é um utilitário de software sobre o qual aprendi há pouco tempo - parecia, senti e gostei. Ele permite padronizar confirmações de mensagens, possui uma ótima interface de console na qual você pode selecionar recursos. Se você tem o compromisso de "corrigir um recurso" ou "consertar um bug", é hora de mostrar o Commitizen para seus colegas, para que eles possam usá-lo pelo menos para começar, e então você pode escrevê-lo na cabeça.
Linters é uma ferramenta indispensável em todos os projetos. Existem muitas configurações prontas em linters, mas você pode
escrever suas próprias regras . Se você possui suas próprias regras, o linter deve adotar essas regras - você precisará escrever as regras para o seu estilo de código.
Links úteis no linter:
Um parágrafo separado alocou o
sonarJS . Esta é uma ferramenta que permite integrar a validação de código no CI. Por exemplo, fazemos uma solicitação de recebimento e, em seguida, o sonarJS escreve para solicitar solicitações sobre nossas escolas, e chateadas ou não. Isso é legal - eu gostei. Mesmo que o condicional Vasya pense que ninguém notará sua hipocrisia - ele notará o sonarJS.
A ferramenta se integra facilmente ao Jenkins. Nossos caras construíram rápido o suficiente. Provavelmente, ele se integra a outros sistemas, mas ainda não o tentamos. O SonarJS ainda está verificando o código quanto ao cheiro do código. Para ser sincero, não sei se o linter comum faz isso.
Formatadores ou estilistas são uma ferramenta que formata o código de acordo com uma configuração, por exemplo,
Prettier . Você pode configurá-lo para
gancho pré-push e obter um estilo de código uniforme no repositório. Petya da nossa equipe pode colocar 500 espaços ou não escrever um ponto e vírgula - tudo ficará limpo e bonito no repositório.
O formatador permite manter o código em um único estilo.
Eu queria contar uma história que aconteceu conosco. Implementamos o Prettier em um projeto que escreveu muitas tarefas e decidimos não executar o projeto inteiro por ele, mas apenas trechos de código com recursos. Pareceu-nos que gradualmente sairíamos e não estragaríamos a história dos commits: na anotação veremos quem manda por último. Essa foi uma péssima decisão. Quando realizamos a tarefa e solicitamos pull, e alteramos algumas linhas, Prettier formatou o arquivo inteiro e, quando assistimos ao pedido pull - apenas duas linhas! Isso levou muito tempo para revisar o código. Portanto,
se você deseja implementar o Prettier, execute o projeto inteiro .
—
error tracking runtime. , , . - — . Error tracking DOM-, , .
Sentry ,
TrackJS , .
Sentry, . Porque , Sentry. .

.

, , : «, iOS, , , Android — , iOS».
Sentry
StackTrace — , .

Sentry. , — : . , , : « , ?». — . , . , — «» .
-
- , .
- .
- — .
- .
- . , frontend-developer Tinkoff.ru.
- Code style, . — .
- — . , , .
- Git Flow — , . — .
- — , . , .
. « » , — , . . , , , . , , .
:
Twitter Facebook .
— Frontend Conf . ? Frontend Conf ++ : , , .
— FrontendConf ++. — , . , 27