Transformando bases de código JavaScript em escala: aproveitando ChatGPT e CodeMods

Apr 19 2023
Combinando ChatGPT e JSCodeShift
Exploraremos o poder dos Codemods e do ChatGPT na refatoração de aplicativos React em grande escala. Esta postagem foi motivada por minha experiência recente, na qual tive que modificar centenas de componentes usados ​​por várias equipes em uma grande base de código monolítica.
Foto de Andrew Neel no Unsplash

Exploraremos o poder dos Codemods e do ChatGPT na refatoração de aplicativos React em grande escala. Esta postagem foi motivada por minha experiência recente, na qual tive que modificar centenas de componentes usados ​​por várias equipes em uma grande base de código monolítica.

Descreverei como você pode aproveitar Codemods e ChatGPT para migrar um componente artificial em escala dos primeiros princípios. As técnicas que discutiremos são generalizáveis ​​e podem ser facilmente adaptadas aos seus próprios casos de uso. Antes de nos aprofundarmos, vamos esclarecer algumas definições.

  • Codemods são transformações automatizadas que modificam o código-fonte, tornando mais fácil padronizar e refatorar o código entre as bases de código. Com seus recursos "mais inteligentes" de localizar e substituir, os Codemods são uma ferramenta poderosa para aumentar a produtividade no desenvolvimento de software.
  • O ChatGPT é uma ferramenta de processamento de linguagem natural que utiliza a tecnologia de IA para permitir conversas semelhantes às humanas. No nosso caso, pediremos ao ChatGPT para escrever Codemods para nós.

Monorepos oferece a vantagem de poder refatorar módulos usados ​​em toda a base de código com um único commit. Isso é particularmente útil em contextos front-end do React, onde os componentes compartilhados podem ser modificados para todos os projetos do consumidor simultaneamente.

No entanto, a desvantagem dessas refatorações é que elas nem sempre são tão simples quanto uma operação de “localizar e substituir”. À medida que a base de código cresce, torna-se cada vez mais difícil fazer alterações abrangentes com rapidez e confiança, independentemente de quão proficiente seja um editor de texto. Alterar dezenas de milhares de linhas de código pode ser uma tarefa demorada e assustadora.

Por exemplo, vamos considerar o caso em que uma equipe deseja usar um novo botão com uma API ligeiramente diferente do botão antigo. Como alguém pode substituir o botão antigo pelo novo quando o anterior é usado milhares de vezes em diferentes aplicativos? Essa não é uma substituição um-para-um direta, pois a API do novo botão difere daquela do botão antigo, conforme mostrado no seguinte cenário:

Existem várias abordagens possíveis que você pode adotar:

  • Deixe os dois botões coexistirem na base de código e gradualmente elimine o antigo
  • Crie o novo componente e reescreva o componente antigo para ser um proxy para o novo
  • Use Codemods para refatorar automaticamente os usos do componente antigo para utilizar o novo no lugar.

Seu primeiro CodeMod

O Facebook lançou o JSCodeShift há alguns anos, que é um kit de ferramentas para executar codemods em bases de código JavaScript e é a ferramenta que usaremos. JSCodeShift pega um arquivo, aplica uma transformação ao código e retorna o mesmo arquivo:

Essas transformações funcionam analisando o código em uma Abstract Syntax Tree (AST) e transformando-a em outra AST.

Você pode ver um exemplo disso online emhttps://astexplorer.net/onde você pode inserir algum código JavaScript à esquerda e ver como isso é analisado em uma árvore AST (um pouco intimidante) à direita.

Um transformador de exemplo é anotado abaixo. Esta é uma função JavaScript que usa a API do tipo jQuery do JSCodeShift para navegar (usando funções como find) e transformar o AST (usando funções como replaceWith). Não se preocupe em memorizar esta API por enquanto.

Se você tiver o seguinte arquivo

import React from "react";
import OldButton from 'old-button'

export const Example = () => {
  return <OldButton color="primary" variant="text">Old</OldButton>;
};

jscodeshift -t rename-button.js example.jsx

Um codemod mais complexo usando ChatGPT

Em nosso exemplo anterior, escrevemos um Codemod simples que pode ser facilmente realizado usando a funcionalidade localizar e substituir do seu IDE. No entanto, tarefas de refatoração mais complexas exigem uma compreensão mais profunda das funções de transformação e da API do CodeShift.

Então, vamos fazer uma refatoração mais complicada:

Escreva-me um codemod jscodeshift que renomeie os elementos de abertura e fechamento de um componente de OldButton para NewButton. Importe NewButton de 'new-button' e remova a importação antiga. Renomeie o tipo de propriedade para variante.

Observe como eu expressei isso como um prompt. Entendo como o JSCodeShift funciona, só não gosto de solucionar problemas de travessia do nó ou desenterrar trechos antigos que construí anteriormente. Este é exatamente o tipo de trabalho que assistentes inteligentes (IA) como o ChatGPT podem fazer por nós:

A execução deste script no meu arquivo fornece a seguinte diferença:

Observe como você pode aplicar isso em toda uma base de código (não apenas em um único arquivo) e a transformação será aplicada a todos os arquivos.

Veja a saída completa gerada abaixo e tente entender como cada transformação opera enquanto explora a árvore AST. Com um pouco de prática, exploração da documentação e assistência do ChatGPT, você poderá criar e executar a maioria das transformações. Isso libera você do trabalho enfadonho e permite que você se concentre nos belos e criativos aspectos da Engenharia de Software.

module.exports = function(file, api) {
  const j = api.jscodeshift;
  const rootSource = j(file.source);


  // Find all import declarations for OldButton and replace with NewButton
  const importDeclaration = rootSource
    .find(j.ImportDeclaration, {
      source: {
        value: 'old-button'
      }
    })
    // Replace these import declarations with a new import declaration 
    .replaceWith(
      j.importDeclaration(
        [j.importSpecifier(j.identifier('NewButton'))],
        j.stringLiteral('new-button')
      )
    )
    .size() > 0;

  // Find all JSXElements with the name "OldButton" and rename them to "NewButton"
  const elementRename = rootSource
    .find(j.JSXOpeningElement, {
      name: {
        name: 'OldButton'
      }
    })
    .replaceWith(({ node }) => {
      const { attributes } = node;
      const variantAttribute = attributes.find(
        attribute => attribute.name.name === 'type'
      );
      // If the node has the desired attribute, replace it
      if (variantAttribute) {
        variantAttribute.name.name = 'variant';
      }
      return j.jsxOpeningElement(j.jsxIdentifier('NewButton'), attributes);
    })
    .size() > 0;

  // Find all JSXClosingElements with the name "OldButton" and rename them to "NewButton"
  const closingElementRename = rootSource
    .find(j.JSXClosingElement, {
      name: {
        name: 'OldButton'
      }
    })
    .replaceWith(({ node }) => j.jsxClosingElement(j.jsxIdentifier('NewButton')))
    .size() > 0;

  return rootSource.toSource()
};

Em conclusão, a combinação de ChatGPT e JSCodeShift fornece uma solução eficaz para refatorar código legado em escala, permitindo que as equipes economizem tempo e se concentrem em engenharia e inovação. No entanto, é importante ter cuidado e não confiar cegamente no ChatGPT para gerar codemodinstruções. Em vez disso, reserve um tempo para entender como o JSCodeShift funciona e use o ChatGPT como um assistente poderoso para aumentar seu fluxo de trabalho.

Além disso, é recomendável seguir as práticas recomendadas, como escrever testes de unidade para todos os transformadores e implementar outras estratégias de teste para garantir que as modificações não introduzam erros inesperados em sua base de código. Ao adotar essas medidas, você pode aproveitar todo o potencial do ChatGPT e do JSCodeShift para simplificar seu processo de refatoração e melhorar a qualidade do seu código.