Configurações importantes para projetos grandes em Angular
Projeto grande? Time grande? Será mantido por anos? Conheça algumas configurações para evitar que o projeto se torne uma nhaca

Há alguns anos atrás começamos a criar um projeto grande na empresa onde trabalho, uma intranet com Front-End escrito em Angular 2 (hoje migrado para Angular 7.2.0). O time inicial era gigante e acabamos criando alguns problemas por não termos como controlar a qualidade de todo conteúdo entregue (mesmo com uma estrutura de code review).
Hoje temos um time de cerca de vinte pessoas e já são bem mais amadurecidos do que a dois anos atrás quando começamos todos juntos na jornada que o sistema representava, nossos processos de code review também amadureceram, mas hoje temos alguns pontos do software que não nos orgulhamos e que poderiam ter sido evitados na época se tivéssemos o conhecimento de como ajudar o software a se blindar.
TypeScript no modo Strict
O TypeScript por si só é incrível. Ele acolhe o caos e liberdade do JavaScript que foi evoluído em 25 anos de linguagem com a ordem, padrões e conceitos evoluídos de linguagens fortemente tipadas no mesmo período de tempo.
Por consequência, o TypeScript se resguarda de certas características exclusivas, entre estas características estão os Union Types, isto é, tipos de dados compostos por outros tipos de dados:


Quando você ativa o modo Strict do TypeScript (não confundir com Strict do JavaScript), os valores null e undefined passam a se tornar tipos de dado independentes, dentro de sua aplicação, e isso te proíbem de efetuar o acesso de dados em um objeto que seja possivelmente nulo ou não definido, evitando desta forma os tão comuns Runtime Exception e impondo aos desenvolvedores envolvidos o uso de técnicas de programação defensiva.


É importante que no desenvolvimento em modo strict, as validações de existência dos dados sejam separados em classes de acesso comum para que não haja validações repetidas distribuídas pelo sistema e, no caso de métodos que consomem apis (dados de estrutura não garantida pela linguagem), a validação destes seja feita antes da entrega do dado.

A configuração do TypeScript strict é simples: duas flags com o valor true devem ser inclusas no arquivo tsconfig.json, são elas: strict e skipLibCheck. Por algum motivo os criadores da linguagem acharam que seria uma ótima ideia efetuar, por padrão, a validação do modo strict nas bibliotecas de dependência de um projeto strict. Isso não faz absoluto sentido e para evitar com diversos problemas que você não possa resolver, habilite a segunda flag citada.
Linter opressor
O linter do TypeScript / Angular é realmente amplo e existem algumas configurações que não vem por padrão em um projeto Angular e algumas dessas configurações são essenciais para evitar perder tempo com defeitos simples ou com manutenções de códigos confusos. Todas as regras citadas são regras do linter e para ativá-las você deve incluí-las nos arquivo tslint.json, dentro do atributo rules.
Imports desnecessários

Imports desnecessários e variáveis declaradas não utilizadas só existem para gerar confusão no momento de manutenção, essas duas configurações vão nos ajudar a anular isso. Evitar também métodos criados e não implementados também é importante.
Métodos com retorno implícito

A inferência de tipo é algo que desburocratiza muito a tipagem e tem sido amplamente adotada em outras linguagens, como no Java 10. A ideia é que você não precisa indicar o tipo explicitamente depois de já tê-lo sinalizado de alguma forma do corpo do código, e sendo assim, o TypeScript irá garantir que aquele tipo subentendido seja obedecido para sempre naquela variável.
Apesar de resolver um problema antigo de limpeza de código, a inferência de tipo pode ser um problema em alguns cenários, o mais comum destes é a não explicitação do tipo no retorno de um método.

O tipo do dado retornado pelo método no exemplo ao lado é de string | undefined implicitado, se o meu desejo era um retorno string eu posso estar gerando uma falha dentro de minha aplicação. É bem possível que depois de uma manutenção ou outra o método fique um pouco maior e acabe entrando em fluxos que seu desenho original não previa e, portanto, retornado uma assinatura inesperada. Para ajudar o TypeScript a conduzir os desenvolvedores naquilo que está sendo construído, impor tipo explícito para retorno de funções é conveniente e pode ser feito da seguinte forma:

Número mágicos

O nome é pejorativo. Se refere a números que foram jogados no corpo do código sem nenhum contexto.
Pode ser que no momento de desenvolvimento aquele número faça absoluto sentido para você, mas acredite, não fará tanto daqui alguns anos e associar um nome a este número é de extrema conveniência.
Pessoalmente, eu acredito que dar nomes para condições grandes contidas em ifs também ajudam na manutenção futura, mas não cheguei a buscar uma forma de impor isso via linter e nem sei se seria realmente saudável.

Esta configuração de números mágicos eu ignoro o número um e zero, por que são comumente utilizados para acesso de primeiro registro de array, incremento, decrementos, valor padrão entre outros usos de dia a dia que são claros a qualquer desenvolvedor.
Complexidade ciclomática

Por último e o mais importante de todos é o controlador de complexidade ciclomática (o do meu projeto está em 18, mas eu mudei rapidinho para 7 só para tirar o print).
Complexidade ciclomática é a medição da complexidade de um método, isto é, quantos loops e verificações existem dentro dele de forma que deixe sua manutenção complexa. Métodos são nomes de trechos de lógica, quanto mais claro e enxuto o nome, melhor será a qualidade de vida para quem for dar manutenção (provavelmente você mesmo).

Pode ser tarde demais quando você rodar o linter de descobrir que seu método ficou complexo demais, então é sempre importante ter instalado uma extensão em seu Visual Studio para medir a complexidade ciclomática do método em tempo real. Eu uso o CodeMetrics, que me dá comentários e cores sobre a qualidade do que estou construindo.
Outros
Vasculhar a documentação do tslint é importante no começo de um projeto. Isso vai ajudar a te dizer para que lado você deseja que a manutenção deste siga.
Há outras possibilidades de lint que não estão funcionando hoje no tslint e que seria necessário a inclusão de um Sonar ou de outro lint no projeto, coisas como tamanho de classes, métodos e medição de complexidade dentro de templates.
Processo de desenvolvimento
O processo de desenvolvimento faz uso de ferramentes que auxiliam a lidar com equipes grandes.
Revisão de código e Git Flow
O processo de git flow pode ser um pouco complicado no começo, mas ele é importante para auxiliar o desenvolvimento de softwares compostos por uma equipe grande.
O Git Flow é um processo de mercado onde você tem tipos de branchs (branchs de correção de defeitos, branchs para pequenas tarefas, branchs para funcionalidade, branchs de entrega de versão, entre outros tipos) e você tem um porquê de cada branch nascer em um local e morrer após um merge específico. No processo de Git Flow, cada solicitação de entrega de código pode ter revisores associados, isto é, pessoas que vão analisar e comentar o código de forma que ele possa ser melhorado.
Em meu ambiente de trabalho são necessárias três aprovações de outros desenvolvedores sobre todos os códigos que eu desenvolvo, mesmo que não estejam relacionados com meu projeto.
Pode parecer muita coisa a aprovação de três entidades técnicas, mas nossa entrega é de pequenos pedaços de código que são fáceis de serem analisados e toda pequena entrega é sinalizada em um canal de Slack que temos na empresa. Um canal exclusivamente para isso.
No começo vai haver muito achismo e revisões que podem ser consideradas bobas demais, ou até mesmo implicação dispensável sobre nomes de variáveis, mas com o tempo as pessoas trabalhando na empresa que estão envolvidas com esta tecnologia em questão vão assumindo certos valores e objetivos que desejam para o software, no nosso caso já registramos um documento com as regras retiradas das solicitações de código mais comum.
Code Review, além de melhorar a qualidade do sistema também ajuda no nivelamento de conhecimento dos profissionais envolvidos e isso ajuda na manutenção.
Integração continua
O processo de integração continua é basicamente um conjunto de scripts que são executados para validação de cada versão gerada de seu software, ou mesmo cada pequena atividade executada por cada desenvolvedor.
O processo de integração continua pode ser executado por um Jenkins ou um Travis. É importante que a validação da build, nos testes unitários e do linter com frequência e de forma automatizada.
Conclusão
Desenvolver software é trabalhar com pessoas, e desejamos sempre trabalhar com qualidade de vida e desejamos sempre que nossas construções sejam elogiadas no futuro. Erro humano é natural, ter como construir um software que irá se blindar dos erros mais bobos e ter uma estrutura que permita que as pessoas da equipe aprendam uma com as outras é de suma importância para software que terá um longo tempo de vida.
