Como foi passar um mês usando o GitHub Copilot e por que pretendo não utilizá-lo (no próxino mês)
Tags: githubcopilot, ai, coding, productivity
Published: 2023-06-13 11:58:57

Provavelmente você já deve conhecer o [GitHub Copilot](https://github.com/features/copilot/?ref=heydesigner): uma ferramenta criada pela Microsoft para auxiliar pessoas desenvolvedoras, fornecendo sugestões de código em tempo real. Embora ferramentas como ChatGPT também sejam capazes de gerar código, o GitHub Copilot tem como vantagem ter uma forte integração com o editor de código.

Existem várias formas de utilizá-lo: você pode escrever uma funcionalidade (até mesmo em português) em formato de comentário e a ferramenta é capaz de gerar uma função, método ou mesmo uma classe inteira completa para contemplar o que descreveu. Ou pode escrever o nome de uma função ou método e esperar que o _autocomplete_ tente prever o código correspondente. Ou pode ainda utilizá-la apenas como um _autocomplete_ um pouco mais "inteligente" para partes menores do seu código.

A primeira forma (utilizando apenas comentários) eu não confio e nunca recomendaria. A segunda, escrevendo apenas o nome da função ou método, costuma funcionar surpreendentemente bem para trechos de código com escopo reduzido e fechado, como por exemplo a criação de uma função que irá receber uma estrutura de dados e fazer alguma operação simples de transformação ou filtro. Já a última forma, como um _autocomplete_ mais "eficiente", tive resultados mistos. Às vezes acertava em cheio! 🎯 Mas não era incomum precisar de ajustes ou até mesmo apresentar uma solução simplesmente errada. 

Ao longo do mês de maio de 2023, utilizei esta ferramenta para programar em [Clojure](https://clojure.org) e [Elm](https://elm-lang.org). Neste artigo conto um pouco sobre o que achei desta experiência.

# Por que decidi experimentar

Kent Beck postou em abril de 2023 [a seguinte mensagem no Twitter](https://twitter.com/KentBeck/status/1648413998025707520?s=20):

> _"Estava relutante em experimentar o ChatGPT. Hoje superei essa relutância. Agora entendo por que estava hesitante.
O valor de 90% das minhas habilidades acabou de cair para $0. A importância dos 10% restantes aumentou mil vezes. Preciso me reajustar."_ - Kent Beck

Este twit fez tanto sucesso que ele acabou [escrevendo um post](https://tidyfirst.substack.com/p/90-of-my-skills-are-now-worth-0) para explicar melhor sua opinião.

Para quem não conhece, Kent Beck é um dos meus autores de livros técnicos favoritos, tendo escrito livros como [eXtreme Programming Explained](https://www.goodreads.com/book/show/67833.Extreme_Programming_Explained), [Planning Extreme Programming](https://www.goodreads.com/book/show/67839.Planning_Extreme_Programming), [Test-Driven Development: By Example](https://www.goodreads.com/book/show/387190.Test_Driven_Development), entre [outros](https://www.goodreads.com/author/list/25211.Kent_Beck). 

E em abril de 2023 eu também estava relutante em experimentar o ChatGPT, mas depois de ler este twit, o abri imediatamente e comecei a testá-lo. Em pouco tempo ficou claro que estava diante de algo muito diferente do que já havia experimentado antes. Fiquei muito impressionado e passei horas fazendo diversos experimentos, incluindo geração de códigos.

Depois de um dia testando o ChatGPT, uma coisa não saía da minha cabeça: se ele consegue gerar códigos tão bem sendo que nem foi criado para esta finalidade, imagine como deve ser usar o GitHub Copilot!

Para minha alegria, lembrei que a empresa onde trabalho estava iniciando um projeto justamente para testar esta ferramenta, em parceria com a Microsoft. Corri e consegui me incluir no grupo de pessoas que iriam testá-la no mês seguinte. Mal podia esperar para começar! 🎉

# Primeiras impressões

Inicialmente não sabia (e, confesso, ainda não sei) exatamente como utilizar este tipo de tecnologia. Como relatei no início do artigo, não existe uma fórmula, cada pessoa pode adaptar para sua forma de trabalho. Mas logo fiquei impressionado positivamente. Comecei pequeno, escrevendo funções em Clojure (backend) e Elm (frontend) em contextos bem definidos e parecia que ela podia ler minha mente! Escrevia o nome da função e... aparecia a implementação! 🧙🪄

Sentia que estava diante de uma grande revolução e que a forma como iria programar nunca mais seria a mesma. Me **sentia** mais produtivo. Como se não perdesse mais tempo com _boilerplates_ e códigos repetitivos em geral. Agora poderia me focar no que realmente importa!

Mas aos poucos fui observando algumas coisas não tão legais assim, tanto no código gerado quanto em meu comportamento.

# Códigos confusos e despadronizados

Utilizei o Copilot em uma aplicação web que já estava em produção há mais de 6 meses, com muitas telas e serviços já prontos e que já tinha todo um padrão de como o código estava sendo estruturado. E embora o Copilot consiga se adaptar, existem limitações. De repente comecei a observar que a construção de algumas funções estavam bastante fora do padrão, adotando mecanismos que eu nem mesmo conhecia.

Quando percebi isso, tentei ver pelo lado positivo: _nossa! não sabia que dava pra fazer isso. Que legal, além de tudo estou aprendendo coisas novas!_ E, por um lado, era verdade. Realmente aprendi coisas novas por conta do Copilot. E claro, bastava não aceitar aquelas sugestões que estivessem fora do padrão e reescrevê-las como eu queria.

Por outro lado, é muito fácil se deixar levar. Aos poucos comecei a me sentir cada vez mais confortável em aceitar as sugestões propostas. Analisava a solução, mas de forma mais abrangente e menos criteriosa. Em alguns momentos ele chegou a gerar códigos que não entendia perfeitamente. Não eram necessariamente complexos, mas usavam algum artifício da linguagem que desconhecia. Em alguns destes casos, fazia alguns testes (automatizados e manuais) e funcionava. O que devo fazer  nestas situações? Tenho um código funcionando plenamente, mas que não entendo direito como funciona. Cheguei a aceitar a solução algumas vezes, dizendo para mim mesmo que iria voltar depois para estudar melhor aquela funcionalidade da linguagem (mas, claro, nunca tinha tempo e disposição para fazê-lo).

Ao mesmo tempo sentia que o fato de estar conseguindo escrever códigos mais rapidamente valia muito a pena. 

Seria este o futuro? Escrever códigos que não entendemos direito? Talvez devesse criar mais testes automatizados e confiar que o código está correto, já que os testes estão passando? Mas eu não iria usar esta mesma tecnologia para escrever os testes? Será que eu quero programar desta forma? 🤔

Muitas perguntas começaram a passar pela minha cabeça. Até que o pior aconteceu.

# Ops, coloquei um bug em produção e nem percebi! 🐛

Quando estou programando em Elm, não é incomum começar escrevendo códigos não muito organizados, com objetivo de testar minhas ideias. Depois que tenho a tela pronta, já exibindo tudo como quero, reorganizo o código. Normalmente isso funciona muito bem, em parte por ser uma linguagem estaticamente tipada com um compilador incrível. Consigo alterar o código e deixo o compilador me guiar e, em geral, quando o código compila, minha aplicação continua tendo o comportamento esperado.

Para alguns cenários crio testes automatizados, mas para esta parte da aplicação que estava desenvolvendo, haviam poucas regras complexas. Bastava fazer uma chamada REST para o backend e exibir algumas informações formatadas na tela. Então a minha cobertura de testes estava baixa.

E foi durante uma dessas alterações que algo estranho aconteceu. 

## Ententendo o problema que estava resolvendo

Não vou entrar em detalhes de negócio, mas estava trabalhando em uma nova funcionalidade de uma página web de apoio (uma espécie de _dashboard_), onde é possível consultar vários relatórios sobre um determinado sistema. Este sistema provê uma série de serviços para outros sistemas de terceiros e precisava expor as métricas de uso destes serviços através de uma página, com tabelas, gráficos, etc.

Internamente existe uma tabela de identificação destes sistemas de terceiros, contendo (entre outras coisas) um identificador (um número inteiro) e uma descrição (uma string). 

Para fazer isso, construi um novo _endpoint_ no backend que retornava um _json_ contendo 2 listas: uma delas era bem simples, do tipo chave-valor, contendo o código de sistema consumidor e seu respectivo nome/descrição. 

A outra lista continha algumas métricas sobre os acessos feitos por estes sistemas, com informações agrupadas por mês.

De forma simplificada, o _json_ retornado era algo como:

```json
{
    "quantidades": [
        {
            "anoMes": 1633057200000, // data no formato POSIX
            "codigoSistemaConsumidor": 10,
            "quantidade": 61
        },
        {
            "anoMes": 1635735600000,
            "codigoSistemaConsumidor": 10,
            "quantidade": 220
        },
        {
            "anoMes": 1635735600000,
            "codigoSistemaConsumidor": 11,
            "quantidade": 34032
        },
        {
            "anoMes": 1638327600000,
            "codigoSistemaConsumidor": 11,
            "quantidade": 179301
        }
    ],

    "codigosSistemasConsumidores": [
        {
            "id": 10,
            "descricao": "Sistema XPTO"
        },
        {
            "id": 11,
            "descricao": "Sistema Foo"
        },
        {
            "id": 12,
            "descricao": "Outro Sistema"
        } 
    ]
}
```

Note que neste exemplo a primeira lista contém informações sobre sistemas com identificadores 10 e 11, enquanto a lista de `codigosSistemasConsumidores` contém identificadores com os números 10, 11 e **12**. Isso é importante para entender o que deu errado no meu código.

## Como resolvi o problema (sem ajuda do Copilot)

Ainda não estava utilizando o Copilot durante a construção da primeira iteração desta tela. Ela continha um relatório geral, com um resumo juntando todos os sistemas, seguida de um relatório mais detalhado, separado por sessões (uma para cada sistema).

Para implementar isso é necessário partir de uma das duas listas: `quantidades` ou `codigosSistemasConsumidores`. Uma pessoa mais desatenta talvez começasse a partir da `codigosSistemasConsumidores`, já que parece ser mais fácil, afinal seria apenas fazer um _map_ em cima dela e gerar uma sessão na tela para cada um dos sistemas. Ou, melhor, poderia gerar alguma estrutura de dados intermediária primeiro, para que pudesse ordenar da forma que quisesse e fazer qualquer outro tipo de transformação antes de partir para a camada de visualização.

Depois de refletir um pouco, optei por iniciar pela lista de  `quantidades`, por duas razões principais: 

1- Não existe nenhuma restrição que impossibilite que a lista `quantidades` tenha uma entrada com um `id` que não esteja presente na lista `codigosSistemasConsumidores`. E queria ter absoluta certeza que todas as entradas seriam exibidas na tela e, caso esta situação inesperada aconteça, que fosse criada uma sessão nova na tela, com o título "Sistema Desconhecido (ID: x)" (uma sessão para cada identificador desconhecido).

2- E o contrário também pode ocorrer: a lista `codigosSistemasConsumidores` pode conter sistemas que não tenham nenhum dado na lista `quantidades`, então estaria exibindo sessões com informações vazias.

Existem várias formas de contornar estes problemas. A forma que optei por fazer foi iniciar o processamento pela lista `quantidades`.

Parti então para o código que, confesso, ficou mais complexo do que imaginei inicialmente. Tive que criar algumas funções auxiliares para obter os dados que precisava, mas no final funcionou adequadamente.

## Reorganizando com a ajuda do Copilot

Estava muito ansioso para ver os resultados na tela e o código que escrevi refletiu isso: estava confuso e um pouco desorganizado. Mas felizmente havia escrito em Elm! E o compilador estava ali, pronto para me ajudar na reorganização. E desta vez já estava utilizando o Copilot.

Quando comecei a alterar o design da parte do código que fazia esta lógica que citei no item anterior, o Copilot tentou realizar o processamento pela forma mais simples possível, através da lista `codigosSistemasConsumidores`. Minha mente não estava mais tão ligada nos potenciais problemas deste caminho e... deixei o Copilot me guiar. De repente estava super sorridente, apagando várias funções auxiliares que deixavam de ser necessárias. E, infelizmente, não havia nenhum teste automatizado para me ajudar nesta parte do redesign. Abri então o sistema, apontando para o meu ambiente de desenvolvimento e... tudo certo! Todos os relatórios continuavam aparecendo como esperado. 

Estava muito feliz! Lembro que mandei uma mensagem para um colega, celebrando como o Copilot tinha me ajudado a reduzir drasticamente a complexidade do meu código! 🥳

Hora de fazer _push_ e deixar a _pipeline_ publicar em produção.

Assim que abri a página em produção, percebi que algo estava errado. 🪲🐞🪳 Várias sessões com informações zeradas! Em desenvolvimento isso não acontecia, já que a massa de dados estava diferente. Para minha sorte, embora estivesse no ambiente de produção, não havia disponibilizado o link no menu de funcionalidades ainda, então ninguém mais tinha acesso à esta página e eu tinha tempo para analisar melhor o que estava acontecendo. Minha primeira reação foi criar um filtro, para retirar as entradas vazias. Mas isso não acontecia antes! O que mudou? Foi quando percebi a origem do problema. E não era apenas filtrar as entradas zeradas, já que fazer apenas isso não resolveria o problema 1 que citei mais acima.

Mais uma vez precisava alterar o código, desta vez voltando para uma lógica mais próxima à original. E mais uma vez, o Copilot insistia em iniciar o processo através da lista `codigosSistemasConsumidores`. Todos os _autocompletes_ me levavam para o caminho errado e - acredite - quando vi, estava quase implementando novamente a lógica de forma errada! Mesmo sabendo o que precisava ser feito, era difícil não deixar o Copilot me guiar para o caminho errado. 🤦

Depois de alguma luta, venci! Mas esta batalha deixou um gosto amargo na boca. Passei horas pensando: será que teria incluído este bug se não fosse o Copilot? Provavelmente não. Teria conseguido evitar isso através de mais testes automatizados? Provavelmente sim. E se estivesse fazendo _mob_ ou _pair programming_? Talvez alguém percebesse o problema. E uma revisão de código assíncrona, pegaria esta situação? Acho pouco provável, mas talvez. 

Mas este foi apenas um dos cenários onde algo deste tipo aconteceu. Não foram muitos e este foi o mais chato, mas foram suficientes para começar a me causar certo desconforto.

## Mais códigos estranhos

Em algum momento estava revendo o código e percebi algo MUITO estranho: uma função estava retornando uma função anônima, sem necessidade nenhuma. Não sei exatamente quando aquilo aconteceu, mas minha hipótese é que havia criado uma nova função e esquecido de incluir um dos parâmetros para fazer o que precisava. Qualquer pessoa desenvolvedora perceberia isso na hora e iria alterar a assinatura da função, mas o Copilot não é capaz de **alterar** o código, apenas **sugerir novos códigos**. Para resolver o problema da falta de um dos parâmetros, acredito que ele tenha criado uma função anônima com o parâmetro que estava faltando! O restando do código estava como esperado e fazia a coisa certa e, talvez por isso, aceitei a sugestão em algum momento sem perceber a existência da função anônima. 😳

Fiquei imaginando como seria se outra pessoa tivesse encontrado este problema! O que ela iria pensar? Aquele código não fazia o menor sentido! Ainda mais em Elm, onde toda função possui [currying](https://pt.wikipedia.org/wiki/Currying) por padrão! Se não fosse meu código, talvez tivesse até receio de alterá-lo, afinal, nenhum ser humano escreveria algo assim sem ter uma boa razão. Poderia passar horas olhando aquele código sem entender o motivo daquilo.

# Confiar ou desconfiar do _autocomplete_?

Em algum momento percebi que, mesmo sendo bastante cético no começo e entendendo as limitações de uma ferramenta de IA, ela acertava quantidade suficiente de vezes para, aos poucos, ganhar minha confiança. E isso precisava mudar.

Desde que comecei a trabalhar profissionalmente como desenvolvedor de software, uso algum tipo de ferramenta de _autocomplete_ de código. Em geral, coisas simples, como o nome de uma função ou método. Uma das vantagens dos _autocompletes_ das IDEs e editores de código é que **eu posso confiar neles**. Se minha IDE está me dizendo que este objeto tem um determinado método ou se um determinado módulo tem uma certa função, eu aprendi a acreditar nesta informação. Quando não lembro se a função se chama `obterSituacaoContribuinte` ou `consultarSituacaoContribuinte` e aparece a primeira opção no meu editor, eu **sei** que este é o nome correto e **confio** nisso.

Mas a partir do momento que meu _autocomplete_ passa a **alucinar** e **inventar** nomes para as funções ou métodos, preciso ficar mais atento. Claro que o _autocomplete_ da minha IDE (ou editor) e o _autocomplete_ do Copilot são exibidos de forma bastante distintas visualmente, mas preciso "desligar" uma chave do meu cérebro, que já está acostumado a confiar em todo _autocomplete_. Agora preciso sempre me questionar: quem está dizendo isso está se baseando em **fatos** ou isso foi gerado por um _plugin_ que está apenas inventando coisas baseado em **probabilidade** e **estatística**?

# E agora?

O Copilot me surpreendeu positivamente em muitos aspectos. Estudei sobre IA durante [meu mestrado](https://dspace.mackenzie.br/handle/10899/24422) e não esperava que fosse possível existir uma ferramenta tão avançada como esta tão cedo.

E nos primeiro dias foi só alegria, mas depois de um mês e alguns bugs não triviais de detectar, o uso desta ferramenta  começou a me gerar algum estresse e ansiedade. E algo que deveria diminuir minha carga cognitiva, começou a aumentá-la.

Passei a desconfiar de tudo que era gerado e aquela sensação gostosa dos primeiros dias começou a ficar menos comum.

Isso não significa que considere o GitHub Copilot ruim ou que não o recomende. Pelo contrário. Foi uma experiência muito legal! E recomendo que toda pessoa desenvolvedora experimente e tire suas próprias conclusões. Acho provável que estejamos caminhando para um momento em que este tipo de tecnologia vai passar a fazer parte do dia a dia de (quase) toda pessoa desenvolvedora. Se isso vai ser bom ou ruim, só o tempo dirá.

Mas, ainda assim, optei por não utilizá-la mais. Pelo menos por enquanto. 

Embora não tenha feito nenhum tipo de medição, desligar esta ferramenta aparentemente diminuiu a minha velocidade. Escrever um filtro, _parsing_ e _boilerplaites_ em geral tornaram-se processos mais onerosos. Mas isso não é algo necessariamente ruim. Ter um tempo para refletir sobre o que estou fazendo tirou um peso das minhas costas. 

O slogan do Copilot é: _"Your AI pair programmer"_, que poderia traduzir em algo como "seu programador em par com IA". E realmente sentia como se estivesse uma pessoa programando comigo o tempo todo - mas uma pessoa chata, que não calava a boca um segundo. 📣 Era como fazer _pair programming_, mas com alguém que não faz ideia do que significa trabalhar de forma pareada. Que não entende absolutamente nada sobre o problema que quero resolver. E que quando algo dá errado, coloca toda culpa em mim.

Nas primeiras horas sem Copilot senti muita falta! Já estava acostumado com a ideia de digitar o nome de uma função e ver o código sendo gerado. Mas logo me readaptei à forma que programava antes. E me senti mais no controle. Mais relaxado.

Ainda não sei se (ou quando) vou voltar a utilizar uma tecnologia como esta, mas por enquanto, estou bem sem ela. Quando preciso de algo muito específico, converso com o ChatGPT e isso tem sido suficiente para mim.

---

E você? Como já experimentou o Copilot? O que tem achado? Deixe sua opinião nos comentários.

---

Gostou deste texto? Conheça meus outros artigos, podcasts e vídeos acessando: https://segunda.tech.

E se quiser, me segue lá no [twitter](https://twitter.com/marciofrayze) e [blue sky](https://staging.bsky.app/profile/segunda.tech).