Contexto
Recentemente, deparei-me com uma aplicação frontend de médio porte escrita em Angular 6 (sim, que teve seu EOL em 2019).
A tarefa era atualizar o projeto para uma versão LTS (Long Term Support) do Angular. Optei, como primeiro movimento, por atualizar para a primeira versão LTS disponível, a versão 16.2.X
. No entanto, não sou especializado em frontend e na stack Node.js, e precisaria de bastante tempo ou do apoio de outro desenvolvedor com mais conhecimento no framework.
O projeto possuía 40 módulos e 175 componentes, distribuídos conforme a tabela a seguir:
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 6 334 271 20 43
HTML 180 11903 10206 693 1004
JavaScript 4 78 49 19 10
JSON 2 22 22 0 0
Sass 247 12575 9431 1284 1860
SVG 2 4 4 0 0
TypeScript 370 36123 27318 2904 5901
===============================================================================
Total 811 61039 47301 4920 8818
===============================================================================
A opção de contar com o apoio de outro desenvolvedor não era viável, e eu também não podia gastar semanas no projeto de migração. Então, resolvi adotar uma abordagem que vi em canais no YouTube recentemente: experimentar o uso de AI Code Assist
na IDE para me auxiliar. Embora eu seja usuário do JetBrains IDEA e do GitHub Copilot para projetos de backend, neste caso, queria experimentar algo novo e mais focado em meu objetivo: apoiar-me no meu conhecimento elementar de frontend e no framework Angular, e trabalhar em um estilo de pair programming tipo Driver and Navigator [1], alternando entre momentos em que a IA gerava o código e eu revisava, e o inverso.
Ferramentas
Para essa tarefa, optei por experimentar o editor de código Cursor [2], que possui uma integração nativa com IA e promete aumento de produtividade com uma série de funcionalidades. Abaixo, destaco as que usei, embora existam outras avançadas descritas na documentação [3].
Tab
: para sugerir ou editar código a partir de um trecho existente ou uma descrição;Cursor Prediction
: após uma edição realizada comTab
, o Cursor prevê trechos adicionais que podem ser corrigidos da mesma forma e salta automaticamente para o próximo trecho com um novotab ⇥
;
Chat
: para interagir com a IA e entender um trecho de código ou pedir uma sugestão de implementação;AI Fix in Chat
: onde um erro capturado pelolinter
é destacado, e a IA abre um popup sugerindo uma solução;Apply
: para aplicar as sugestões da IA diretamente no editor;
Para usar o Cursor, não é necessário ter uma assinatura premium, embora o uso seja limitado [4]. No meu caso, optei pela assinatura Pro para garantir o uso ilimitado das funções
completions
einstant apply
.
Com o Cursor, o node
na versão 20.11
e o Angular CLI
na versão desejada instalados, comecei a trabalhar.
Interagindo com a IA do Cursor
A primeira coisa que fiz foi gerar um novo projeto com a versão 16 do Angular CLI e comparar as diferenças entre o projeto antigo e o novo. Notei que alguns arquivos não eram mais necessários, como o angular-cli.json
, enquanto outros precisariam ser criados ou ajustados, como o angular.json
, o tsconfig.json
, o tslint.json
e o tsconfig.app.json
. Deleguei à IA a tarefa de corrigir esses arquivos por meio de interações no seguinte formato:
Atualize o arquivo angular-cli.json (no contexto) para a versão 16 do Angular.
Com o seguinte resultado:
E o resultado das alterações sugeridas pela IA eram aplicadas diretamente no arquivo desejado:
Uma vez atualizados os arquivos-base do projeto, parti para as dependências. Obviamente, muitas já não existiam mais, ou tiveram seus nomes alterados. Portanto, precisei corrigir manualmente algumas dependências a partir de buscas no npm.js, como por exemplo o angular-http
que foi incoporado no angular-common
, ou o angular2-fontawesome
que foi descontinuado e substituído pelo @fortawesome/angular-fontawesome
.
Uma vez atualizadas as dependências, e após muitos npm install
foi hora de tentar rodar o ng build
e começar a me deparar com os primeiros erros.
Fase de build do backend
Projetos Angular são divididos em duas grandes partes: o backend, com componentes, módulos e serviços; e o frontend, composto por templates, diretivas, pipes e módulos de interface do usuário.
Em cinco anos, muita coisa muda, e tratando-se de projetos frontend, as mudanças foram ainda maiores.
Minha abordagem foi realizar o ng build
para compilar o backend inicialmente, e, como esperado, muitos erros de build ocorreram, especialmente por conta da configuração strict
do tsconfig.json
, que agora exigia que todas as variáveis fossem tipadas. Embora eu pudesse desabilitar essa configuração, preferi, com a ajuda da IA, corrigir os erros de tipagem em cada arquivo, usando as funcionalidades listadas no início do artigo. Além disso, muitas dependências tiveram namespaces alterados, sendo um dos principais exemplos o desmembramento dos componentes do Angular Material, que antes estavam sob o namespace @angular/material
.
Centenas de Tabs
depois, o ng build
finalmente compilou sem erros, e pude seguir para a fase de build do frontend.
Fase de build do frontend
Agora o ng serve
precisava compilar o frontend. Obviamente, muitas diretivas foram alteradas, como o binding de propriedades e eventos, que precisaram ser corrigidas.
A explicação dada pela IA do Cursor para um erro durante a migração do binding de evento foi a seguinte:
A mudança que você mencionou na diretiva do Angular de maxlength='100' para [maxLength]='100' reflete uma atualização na forma como as propriedades são vinculadas em Angular, especialmente em relação ao uso de binding de propriedades.
Explicação da Mudança:
- Binding de Propriedades:
- Antes (Angular 6): A diretiva usava a sintaxe de atributo HTML padrão, onde maxlength era definido como um atributo HTML com um valor literal. Isso significa que o valor era tratado como uma string.
- Agora (Angular 16): A nova sintaxe [maxLength]='100' utiliza o property binding do Angular. Isso permite que o valor seja tratado como uma expressão Angular, o que significa que você pode passar variáveis, expressões ou valores dinâmicos.
A vantagem de contar com a IA fornecendo tanto a correção quanto a explicação da mudança foi que eu poderia usar o recurso de Search All and Replace para corrigir todos os trechos semelhantes que precisavam ser alterados de uma só vez.
Outro erro frequente era a falta de tratamento para valores null
ao acessar propriedades de objetos, o que o TypeScript não aceitava e precisava ser corrigido. Esse é um problema recorrente em projetos legados, e a IA foi capaz de corrigir a partir de sugestões de Tab
.
Depois de algum tempo, o ng serve
finalmente compilou sem erros, e pude acessar a aplicação no navegador.
Publicando a aplicação e criando o Pull Request
Com a aplicação funcionando, restava apenas atualizar o arquivo de pipeline para usar uma versão mais recente do Node.js e criar o Pull Request no repositório.
Eis o resultado final da branch enhancement/bump-up-angular-16
:
397 files changed, 20290 insertions(+), 19788 deletions(-)
Conclusão
O termo "Programação Assistida/Guiada por IA", e congêneres estarão cada vez mais presentes no dia a dia dos times de engenharia de software. Estudos da Gartner preveem que, até 2028, 75% dos desenvolvedores usarão ferramentas de IA para escrever código [5]. Esse movimento já é perceptível na indústria de software; o Mercado Livre, por exemplo, já utiliza ferramentas de IA para apoiar seus mais de 9.000 desenvolvedores [6].
Neste breve relato, pude observar que, embora eu não fosse um profundo conhecedor de uma stack específica, a produtividade na execução da tarefa foi significativamente aumentada. Um esforço que, antes, exigiria semanas foi reduzido para poucos dias. No entanto, o conhecimento e a experiência do desenvolvedor são indispensáveis, pois, como demonstrei, muitas vezes cabe ao próprio desenvolvedor decidir qual abordagem é mais adequada para resolver o problema. Assim, o senso crítico, a sensibilidade e a experiência do desenvolvedor foram essenciais e não substituídos.