Este post faz parte de uma série de posts no blog que descrevem as mudanças que estamos fazendo na arquitetura do DevTools e como ele é criado.
Continuando nossa migração para módulos JavaScript e migração para componentes da Web, hoje continuamos nossa série de postagens no blog sobre as mudanças que estamos fazendo na arquitetura do DevTools e como ela é criada. Se você ainda não viu, postamos um vídeo sobre nosso trabalho de atualizar a arquitetura do DevTools para a Web moderna, com 14 dicas sobre como melhorar seus projetos da Web.
Neste post, vamos descrever nossa jornada de 13 meses para migrar do verificador de tipo do Closure Compiler para o TypeScript.
Introdução
Considerando o tamanho do código-base do DevTools e a necessidade de dar confiança aos engenheiros que trabalham nele, usar um verificador de tipo é uma necessidade. Para isso, as DevTools adotaram o Closure Compiler em 2013. A adoção do Closure permitiu que os engenheiros do DevTools fizessem mudanças com confiança. O compilador do Closure realizava verificações de tipo para garantir que todas as integrações do sistema fossem bem digitadas.
No entanto, com o passar do tempo, os verificadores de tipo alternativos se tornaram populares no desenvolvimento moderno da Web. Dois exemplos importantes são o TypeScript e o Flow. Além disso, o TypeScript se tornou uma linguagem de programação oficial no Google. Embora esses novos verificadores de tipo tenham aumentado a popularidade, também notamos que estávamos enviando regressões que deveriam ter sido detectadas por um verificador de tipo. Portanto, decidimos reavaliar nossa escolha de verificador de tipo e descobrir as próximas etapas para o desenvolvimento nas DevTools.
Como avaliar verificadores de tipo
Como o DevTools já estava usando um verificador de tipo, a pergunta que precisamos responder foi:
Continuamos usando o Closure Compiler ou migramos para um novo verificador de tipos?
Para responder a essa pergunta, avaliamos os verificadores de tipo em várias características. Como o uso de um verificador de tipos se concentra na confiança do engenheiro, o aspecto mais importante para nós é a correção do tipo. Em outras palavras: Qual a confiabilidade de um verificador de tipos para descobrir problemas reais?
Nossa avaliação se concentrou nas regressões que enviamos e em determinar quais seriam as causas raiz delas. A suposição aqui é que, como já estávamos usando o Closure Compiler, o Closure não teria detectado esses problemas. Portanto, teríamos que determinar se algum outro verificador de tipo teria sido capaz de fazer isso.
Correção de tipo no TypeScript
Como o TypeScript era uma linguagem de programação oficialmente aceita pelo Google e estava ganhando popularidade rapidamente, decidimos avaliar o TypeScript primeiro. O TypeScript foi uma escolha interessante, já que a própria equipe do TypeScript usa o DevTools como um dos projetos de teste para acompanhar a compatibilidade com a verificação de tipo do JavaScript. A saída do teste de referência de referência mostrou que o TypeScript estava detectando uma grande quantidade de problemas de tipo, problemas que o Closure Compiler não estava necessariamente detectando. Muitos desses problemas provavelmente eram a causa raiz das regressões que estávamos enviando. Isso, por sua vez, nos fez acreditar que o TypeScript poderia ser uma opção viável para as DevTools.
Durante a migração para módulos JavaScript, já havíamos descoberto que o Closure Compiler estava revelando mais problemas do que antes. A mudança para um formato de módulo padrão aumentou a capacidade do Closure de entender nossa base de código e, portanto, aumentou a eficácia dos verificadores de tipo. No entanto, a equipe do TypeScript estava usando uma versão de referência das Ferramentas do desenvolvedor anterior à migração dos módulos JavaScript. Portanto, tivemos que descobrir se a migração para módulos JavaScript também reduziu a quantidade de erros que o compilador TypeScript detectaria.
Como avaliar o TypeScript
O DevTools existe há mais de uma década, e cresceu para se tornar um aplicativo da Web com recursos e tamanho consideráveis. No momento em que esta postagem do blog foi escrita, o DevTools continha aproximadamente 150.000 linhas de código JavaScript próprio. Quando executamos o compilador TypeScript no nosso código-fonte, o volume de erros foi enorme. Descobrimos que, embora o compilador do TypeScript estivesse emitindo menos erros relacionados à resolução de código (cerca de 2.000 erros), ainda havia outros 6.000 erros presentes na nossa base de código relacionados à compatibilidade de tipos.
Isso mostrou que, embora o TypeScript fosse capaz de entender como resolver tipos, ele encontrou uma quantidade significativa de incompatibilidades de tipo na nossa base de código.
Uma análise manual desses erros mostrou que o TypeScript estava (na maioria das vezes) correto.
O TypeScript conseguiu detectar esses erros, mas o Closure não, porque o compilador do Closure deduz que um tipo é um Any
, enquanto o TypeScript realiza a inferência de tipo com base nas atribuições e deduz um tipo mais preciso.
Assim, o TypeScript foi melhor na compreensão da estrutura dos nossos objetos e descobriu usos problemáticos.
Uma observação importante é que o uso do Closure Compiler no DevTools incluiu o uso frequente de @unrestricted
.
A anotação de uma classe com @unrestricted
desativa as verificações de propriedade estrita do Closure Compiler para essa classe específica, o que significa que um desenvolvedor pode aumentar uma definição de classe à vontade sem segurança de tipo.
Não encontramos nenhum contexto histórico sobre por que o uso de @unrestricted
era prevalente na base de código do DevTools, mas ele resultou na execução do Closure Compiler em um modo de operação menos seguro para grandes partes da base de código.
Uma análise cruzada das nossas regressões com os erros de tipo descobertos pelo TypeScript também mostrou uma sobreposição, o que nos levou a acreditar que o TypeScript poderia ter evitado esses problemas (desde que os tipos estivessem corretos).
Fazer uma chamada any
Nesse ponto, tivemos que decidir entre melhorar o uso do Closure Compiler ou migrar para o TypeScript. Como o Flow não era compatível com o Google nem com o Chromium, tivemos que abrir mão dessa opção. Com base nas discussões e recomendações dos engenheiros do Google que trabalham com as ferramentas JavaScript/TypeScript, optamos pelo compilador TypeScript. Também publicamos recentemente uma postagem no blog sobre como migrar o Puppeteer para o TypeScript.
As principais razões para o compilador TypeScript foram a melhoria da correção de tipos, enquanto outras vantagens incluíram o suporte das equipes do TypeScript internamente no Google e os recursos da linguagem TypeScript, como interfaces
(em vez de typedefs
no JSDoc).
A escolha do compilador TypeScript exigiu um investimento significativo no código-base do DevTools e na arquitetura interna. Por isso, estimamos que precisaríamos de pelo menos um ano para migrar para o TypeScript (meta para o terceiro trimestre de 2020).
Como executar a migração
A maior dúvida que ficou: como vamos migrar para o TypeScript? Temos 150.000 linhas de código e não podemos migrar tudo de uma vez. Também sabíamos que executar o TypeScript na nossa base de código revelaria milhares de erros.
Avaliamos várias opções:
- Receba todos os erros do TypeScript e compare-os com uma saída "ideal". Essa abordagem seria semelhante à da equipe do TypeScript. A maior desvantagem dessa abordagem é a alta ocorrência de conflitos de mesclagem, já que dezenas de engenheiros trabalham na mesma base de código.
- Defina todos os tipos problemáticos como
any
. Isso basicamente faria com que o TypeScript suprimisse erros. Não escolhemos essa opção, porque nosso objetivo com a migração era a correção do tipo, que a supressão prejudicaria. - Corrija todos os erros do TypeScript manualmente. Isso envolveria corrigir milhares de erros, o que leva tempo.
Apesar do grande esforço esperado, optamos pela opção 3. Também escolhemos essa opção por outros motivos: por exemplo, ela nos permitiu auditar todo o código e fazer uma revisão de todas as funcionalidades, incluindo a implementação. Do ponto de vista comercial, não estávamos oferecendo um novo valor, mas mantendo o status quo. Isso dificultou a justificativa da opção 3 como a escolha correta.
No entanto, ao adotar o TypeScript, acreditamos que poderíamos evitar problemas futuros, principalmente em relação a regressões. Assim, o argumento era menos "estamos adicionando um novo valor de negócios" e mais "estamos garantindo que não perdemos o valor de negócios recebido".
Suporte a JavaScript do compilador TypeScript
Depois de garantir o buy-in e desenvolver um plano para executar o Closure e o compilador TypeScript no mesmo código JavaScript, começamos com alguns arquivos pequenos. Nossa abordagem foi basicamente de baixo para cima: começamos com o código principal e avançamos na arquitetura até chegarmos aos painéis de alto nível.
Conseguimos paralelizar nosso trabalho adicionando @ts-nocheck
a todos os arquivos no DevTools. O processo de "corrigir o TypeScript" seria remover a anotação @ts-nocheck
e resolver os erros encontrados pelo TypeScript. Isso significa que tínhamos certeza de que cada arquivo foi verificado e que o maior número possível de problemas de tipo foi resolvido.
Em geral, essa abordagem funcionou com alguns problemas. Encontramos vários bugs no compilador TypeScript, mas a maioria deles era obscura:
- Um parâmetro opcional com um tipo de função que retorna
any
é tratado como obrigatório: #38551 - Uma atribuição de propriedade a um método estático de uma classe quebra a declaração: #38553
- A declaração de uma subclasse com um construtor sem argumentos e uma superclasse com um construtor de argumentos omite o construtor filho: #41397
Esses bugs destacam que, para o caso de 99%, o compilador TypeScript é uma base sólida para criar. Sim, esses bugs obscuros às vezes causavam problemas para as DevTools, mas na maioria das vezes eram obscuros o suficiente para que pudéssemos contorná-los com facilidade.
O único problema que causou alguma confusão foi a saída não determinística de arquivos .tsbuildinfo
: #37156.
No Chromium, exigimos que dois builds do mesmo commit do Chromium resultem na mesma saída.
Infelizmente, nossos engenheiros de build do Chromium descobriram que a saída de .tsbuildinfo
não era determinística: crbug.com/1054494.
Para contornar esse problema, tivemos que fazer um monkey-patch no arquivo .tsbuildinfo
, que contém JSON, e processá-lo para retornar uma saída determinística: https://crrev.com/c/2091448.
Felizmente, a equipe do TypeScript resolveu o problema upstream, e logo conseguimos remover nossa solução alternativa. Agradeço à equipe do TypeScript por receber os relatórios de bugs e corrigir esses problemas rapidamente.
No geral, estamos satisfeitos com a correção (de tipo) do compilador TypeScript. Esperamos que o Devtools, como um grande projeto JavaScript de código aberto, tenha ajudado a consolidar o suporte a JavaScript no TypeScript.
Como analisar as consequências
Conseguimos resolver esses erros de tipo e aumentar lentamente a quantidade de código verificado pelo TypeScript. No entanto, em agosto de 2020 (nove meses após o início da migração), fizemos um check-in e descobrimos que não atingiríamos o prazo com o ritmo atual. Um dos nossos engenheiros criou um gráfico de análise para mostrar o progresso da "TypeScriptification" (o nome que demos a essa migração).
Progresso da migração do TypeScript: rastreamento de linhas de código restantes que precisam ser migradas
As estimativas de quando chegaríamos a zero linhas restantes variaram de julho a dezembro de 2021, quase um ano após o prazo. Depois de conversar com a gerência e outros engenheiros, concordamos em aumentar a quantidade de engenheiros que trabalham na migração para o suporte ao compilador TypeScript. Isso foi possível porque projetamos a migração para ser paralela, de modo que vários engenheiros trabalhando em vários arquivos diferentes não entrassem em conflito.
Nesse ponto, o processo de TypeScriptificação se tornou um esforço de toda a equipe. Com a ajuda extra, conseguimos concluir a migração no final de novembro de 2020, 13 meses depois do início, e mais de um ano antes da estimativa inicial.
No total, foram 771 listas de mudanças (semelhantes a uma solicitação de pull) enviadas por 18 engenheiros. Nosso bug de rastreamento (https://crbug.com/1011811) tem mais de 1.200 comentários, quase todos postagens automatizadas de listas de mudanças. Nossa planilha de acompanhamento tinha mais de 500 linhas para todos os arquivos a serem tipificados, o atribuído e em qual lista de mudanças eles foram "tipificados".
Mitigar o impacto do desempenho do compilador TypeScript
O maior problema que estamos enfrentando hoje é a lentidão do compilador TypeScript. Considerando o número de engenheiros que desenvolvem o Chromium e o DevTools, esse gargalo é caro. Infelizmente, não conseguimos identificar esse risco antes da migração e só descobrimos um aumento significativo no tempo gasto nos builds do Chromium quando migramos a maioria dos arquivos para o TypeScript: https://crbug.com/1139220
Informamos o problema à equipe do compilador TypeScript da Microsoft, mas eles determinaram que esse comportamento foi intencional. Esperamos que eles reconsiderem esse problema, mas, enquanto isso, estamos trabalhando para reduzir o impacto da lentidão no Chromium.
Infelizmente, as soluções disponíveis hoje não são sempre adequadas para colaboradores que não são funcionários do Google. Como as contribuições de código aberto para o Chromium são muito importantes, especialmente as da equipe do Microsoft Edge, estamos procurando ativamente alternativas que funcionem para todos os colaboradores. No entanto, no momento, não encontramos uma solução alternativa adequada.
Estado atual do TypeScript no DevTools
No momento, removemos o verificador de tipo do compilador Closure da nossa base de código e dependemos apenas do compilador TypeScript. Podemos escrever arquivos criados em TypeScript e usar recursos específicos do TypeScript (como interfaces, genéricos etc.), o que nos ajuda diariamente. Temos mais confiança de que o compilador TypeScript vai detectar erros de tipo e regressões, o que era o que esperávamos quando começamos a trabalhar nessa migração. Essa migração, como muitas outras, foi lenta, complexa e desafiadora, mas, à medida que os benefícios aparecem, acreditamos que valeu a pena.
Fazer o download dos canais de visualização
Use o Chrome Canary, Dev ou Beta como navegador de desenvolvimento padrão. Esses canais de visualização dão acesso aos recursos mais recentes do DevTools, permitem testar APIs de plataforma da Web de última geração e ajudam a encontrar problemas no seu site antes dos usuários.
Entre em contato com a equipe do Chrome DevTools
Use as opções a seguir para discutir os novos recursos, atualizações ou qualquer outra coisa relacionada ao DevTools.
- Envie feedback e solicitações de recursos para crbug.com.
- Informe um problema do DevTools usando o Mais opções > Ajuda > Informar um problema do DevTools no DevTools.
- Envie um tweet para @ChromeDevTools.
- Deixe comentários nos vídeos Novidades do DevTools no YouTube ou Dicas do DevTools no YouTube.