Explorando a IA Generativa


TDD com GitHub Copilot

por Paulo Sobocinski

O advento de assistentes de codificação de IA como o GitHub Copilot significará que não precisaremos de testes? O TDD se tornará obsoleto? Para responder a isso, vamos examinar duas maneiras pelas quais o TDD ajuda no desenvolvimento de software program: fornecendo um bom suggestions e um meio de “dividir e conquistar” ao resolver problemas.

TDD para um bom suggestions

Um bom suggestions é rápido e preciso. Em ambos os aspectos, nada supera começar com um teste de unidade bem escrito. Não testes manuais, nem documentação, nem revisão de código e, sim, nem mesmo IA generativa. Na verdade, os LLMs fornecem informações irrelevantes e até mesmo alucinar. O TDD é especialmente necessário ao usar assistentes de codificação de IA. Pelos mesmos motivos pelos quais precisamos de suggestions rápido e preciso sobre o código que escrevemos, precisamos de suggestions rápido e preciso sobre o código que nosso assistente de codificação de IA escreve.

TDD para problemas de divisão e conquista

A resolução de problemas through dividir e conquistar significa que problemas menores podem ser resolvidos mais cedo do que os maiores. Isso permite Integração Contínua, Desenvolvimento Baseado em Tronco e, finalmente, Entrega Contínua. Mas realmente precisamos de tudo isso se os assistentes de IA fazem a codificação para nós?

Sim. Os LLMs raramente fornecem a funcionalidade exata de que precisamos após um único immediate. Portanto, o desenvolvimento iterativo ainda não vai embora. Além disso, os LLMs parecem “elicitar o raciocínio” (veja o estudo vinculado) quando resolvem problemas incrementalmente por meio de estímulo à cadeia de pensamento. Assistentes de codificação de IA baseados em LLM têm melhor desempenho quando dividem e conquistam problemas, e o TDD é como fazemos isso para o desenvolvimento de software program.

Dicas de TDD para GitHub Copilot

Na Thoughtworks, usamos o GitHub Copilot com TDD desde o início do ano. Nosso objetivo tem sido experimentar, avaliar e desenvolver uma série de práticas eficazes em torno do uso da ferramenta.

0. Começando

Explorando a IA Generativa

Começar com um arquivo de teste em branco não significa começar com um contexto em branco. Frequentemente começamos com uma história de usuário com algumas notas brutas. Também conversamos sobre um ponto de partida com nosso parceiro de pareamento.

Este é todo o contexto que o Copilot não “vê” até que o coloquemos em um arquivo aberto (por exemplo, o topo do nosso arquivo de teste). O Copilot pode trabalhar com erros de digitação, forma de pontos, gramática ruim — o que você quiser. Mas ele não pode trabalhar com um arquivo em branco.

Alguns exemplos de contexto inicial que funcionaram para nós:

  • Maquete de arte ASCII
  • Critérios de aceitação
  • Suposições orientadoras, tais como:
    • “Não é necessária interface gráfica de usuário”
    • “Use Programação Orientada a Objetos” (vs. Programação Funcional)

O Copilot usa arquivos abertos para contexto, portanto, manter o arquivo de teste e o de implementação abertos (por exemplo, lado a lado) melhora muito a capacidade de conclusão de código do Copilot.

1. Vermelho

TDD representado como uma roda de três partes com a parte 'Vermelha' destacada no terço superior esquerdo

Começamos escrevendo um nome de exemplo de teste descritivo. Quanto mais descritivo o nome, melhor o desempenho do code completion do Copilot.

Nós descobrimos que um Dado-Quando-Então estrutura ajuda de três maneiras. Primeiro, ela nos lembra de fornecer contexto de negócios. Segundo, ela permite que o Copilot forneça recomendações de nomenclatura ricas e expressivas para exemplos de teste. Terceiro, ela revela a “compreensão” do problema pelo Copilot a partir do contexto do topo do arquivo (descrito na seção anterior).

Por exemplo, se estivermos trabalhando em código de backend e o Copilot estiver completando o código, nosso nome de exemplo de teste será, “dado o usuário… clica no botão comprarisso nos diz que devemos atualizar o contexto do topo do arquivo para especificar, “assumir nenhuma GUI” ou, “este conjunto de testes faz interface com os pontos de extremidade da API de um aplicativo Python Flask”.

Mais “pegadinhas” para ficar atento:

  • O Copilot pode completar vários testes de uma vez. Esses testes são frequentemente inúteis (nós os excluímos).
  • À medida que adicionamos mais testes, o Copilot completará o código de várias linhas em vez de uma linha por vez. Ele frequentemente inferirá as etapas corretas de “prepare” e “act” a partir dos nomes dos testes.
    • Aqui está o problema: ele infere a etapa correta de “afirmar” com menos frequência, por isso somos especialmente cuidadosos aqui para que o novo teste seja falhando corretamente antes de passar para a etapa “verde”.

2. Verde

TDD representado como uma roda de três partes com a parte 'Verde' destacada no terço superior direito

Agora estamos prontos para o Copilot ajudar com a implementação. Um conjunto de testes já existente, expressivo e legível maximiza o potencial do Copilot nesta etapa.

Dito isso, o Copilot frequentemente falha em dar “pequenos passos”. Por exemplo, ao adicionar um novo método, o “pequeno passo” significa retornar um valor codificado que passa no teste. Até o momento, não conseguimos persuadir o Copilot a adotar essa abordagem.

Testes de preenchimento

Em vez de dar “pequenos passos”, o Copilot salta à frente e fornece funcionalidades que, embora frequentemente relevantes, ainda não foram testadas. Como solução alternativa, “preenchemos” os testes ausentes. Embora isso seja diferente do fluxo TDD padrão, ainda não vimos nenhum problema sério com nossa solução alternativa.

Excluir e regenerar

Para código de implementação que precisa de atualização, a maneira mais eficaz de envolver o Copilot é excluir a implementação e fazer com que ele regenere o código do zero. Se isso falhar, excluir o conteúdo do método e escrever a abordagem passo a passo usando comentários de código pode ajudar. Caso isso falhe, a melhor maneira de seguir em frente pode ser simplesmente desligar o Copilot momentaneamente e codificar a solução manualmente.

3. Refatorar

TDD representado como uma roda de três partes com a parte 'Refactor' destacada no terço inferior

Refatoração em TDD significa fazer mudanças incrementais que melhoram a manutenibilidade e a extensibilidade da base de código, tudo isso realizado preservando o comportamento (e uma base de código funcional).

Para isso, descobrimos que a habilidade do Copilot é limitada. Considere dois cenários:

  1. “Eu sei qual movimento de refatoração quero tentar”: Atalhos de refatoração do IDE e recursos como seleção de múltiplos cursores nos levam aonde queremos ir mais rápido que o Copilot.
  2. “Não sei qual movimento de refatoração tomar”: A conclusão de código do Copilot não pode nos guiar por uma refatoração. No entanto, o Copilot Chat pode fazer sugestões de melhoria de código diretamente no IDE. Começamos a explorar esse recurso e vemos a promessa de fazer sugestões úteis em um escopo pequeno e localizado. Mas ainda não tivemos muito sucesso para sugestões de refatoração em larga escala (ou seja, além de um único método/função).

Às vezes, sabemos o movimento de refatoração, mas não sabemos a sintaxe necessária para executá-lo. Por exemplo, criar um teste simulado que nos permitiria injetar uma dependência. Para essas situações, o Copilot pode ajudar a fornecer uma resposta em linha quando solicitado por meio de um comentário de código. Isso nos poupa de alternar o contexto para documentação ou pesquisa na net.

Conclusão

O ditado comum, “lixo entra, lixo sai” se aplica tanto à Engenharia de Dados quanto à IA Generativa e LLMs. Em outras palavras: entradas de maior qualidade permitem que a capacidade dos LLMs seja melhor aproveitada. No nosso caso, o TDD mantém um alto nível de qualidade do código. Essa entrada de alta qualidade leva a um melhor desempenho do Copilot do que seria possível de outra forma.

Portanto, recomendamos usar o Copilot com TDD e esperamos que as dicas acima sejam úteis para isso.

Graças à equipe “Ensembling with Copilot” iniciada na Thoughtworks Canada; eles são a principal fonte das descobertas abordadas neste memorando: Om, Vivian, Nenad, Rishi, Zack, Eren, Janice, Yada, Geet e Matthew.


Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *