- Recentemente, os desenvolvedores estão gastando mais tempo para modificar e complementar código gerado por LLMs (grandes modelos de linguagem)
- Assim como no código legado existente, para alterar o código com segurança é preciso primeiro entender o que foi implementado e por que foi feito daquela forma, mas o código de LLM torna esse processo ainda mais difícil
- Algumas equipes revisam e retrabalham o código com profundidade, o que reduz a velocidade, mas muitas acabam enviando ao repositório código mal lido e testado de forma superficial
- Isso gera a "dívida de compreensão (Comprehension Debt)", que no fim volta como um custo de tempo ainda maior quando o código precisa ser alterado
- Em geral, os LLMs conseguem ajudar a resolver problemas até cerca de 70%, mas não conseguem evitar o "Doom Loop" de falhas repetidas, e inevitavelmente chega o momento em que uma pessoa precisa entender e corrigir o código manualmente
Código gerado por LLMs e o problema da dívida de compreensão
- Nos ambientes de desenvolvimento, o uso de ferramentas de geração automática de código baseadas em LLMs, como ChatGPT e Copilot, vem aumentando
- Essas ferramentas geram rapidamente código complexo mesmo sem conhecimento profundo ou entendimento direto por parte do desenvolvedor
- Porém, nesse processo, muitas vezes não fica claro a intenção, as restrições e o funcionamento do código, o que leva ao acúmulo de "dívida de compreensão"
O que é dívida de compreensão (Comprehension Debt)
- Dívida de compreensão significa um estado em que os membros da equipe não entendem suficientemente a qualidade, a estrutura e a intenção do código
- No curto prazo, isso pode aumentar a velocidade de desenvolvimento, mas no longo prazo há risco de efeitos colaterais diversos, como aumento do custo de manutenção, surgimento de bugs e limitação na expansão de funcionalidades
Riscos adicionais do código gerado por LLMs
- O código gerado por LLMs produz resultados rapidamente sem fornecer comentários claros nem contexto
- Há grande chance de surgirem problemas de compartilhamento insuficiente de conhecimento entre os membros da equipe e de baixa compatibilidade com os sistemas existentes
- Se a dependência de código de LLM se repetir continuamente, isso pode levar à queda da confiabilidade do código em todo o projeto
Comparação com dívida técnica
- A dívida técnica tradicional é resultado de um compromisso consciente feito por desenvolvedores, enquanto a dívida de compreensão baseada em LLMs pode se acumular de forma inconsciente, tornando-se mais perigosa
- É mais difícil perceber o problema, e rastrear suas causas e resolvê-lo se torna mais complexo
- Ao tentar resolver o problema, é comum vivenciar o "Doom Loop" (um ciclo vicioso em que repetir o uso do LLM continua falhando)
Conclusão e implicações
- No fim, as pessoas ainda precisam ler e corrigir o código diretamente
- Ao usar LLMs, é importante que toda a equipe entenda o contexto e a intenção do código, por meio de documentação e compartilhamento
- São necessários mecanismos em nível organizacional, como reforço de code review, melhorias em comentários e documentação, e sessões de compartilhamento de conhecimento
- Sem gerenciar a dívida de compreensão, as vantagens das ferramentas de automação podem acabar se transformando em risco de longo prazo
- Todo o setor parece estar sentado sobre uma montanha de dívida de compreensão que cresce rapidamente
8 comentários
Eu também sempre decido tudo no código e verifico por conta própria se ficou bem, porque a IA não consegue decidir tudo completamente.
Pelo meu critério, serve simplesmente para escrever snippets com menos de 10 linhas (ex.: parsing de JSON, implementação de ordenação). Mesmo usando só assim, parece que economiza um tempo enorme.
Fazer o esqueleto com ajuda de IA até que vai, mas parece muito difícil manter a atenção aos detalhes até o fim.
Eu também, pessoalmente, já senti com frequência os efeitos colaterais de quando usei um LLM para escrever de 0 a 90, então hoje em dia procuro fazer eu mesmo, sempre que possível, a parte de 0 a 90 e usar principalmente no processo de levar de 90 a 100, e estou satisfeito com isso.
Opiniões do Hacker News
Com a dependência de LLMs ficando mais forte, tive a experiência de ver problemas que já existiam piorarem ainda mais
Apresenta o conceito de "construção de teoria" de Naur e aponta que, quando uma equipe de desenvolvimento de software é desfeita, o programa em questão fica, na prática, morto
Menciona o argumento de Lamport de que "programação ≠ codificação", enfatizando que a essência da programação é construir uma teoria sobre "o que" e "como" alcançar algo
Acho que, quanto menos um programador estabelece os modelos ou teorias necessários, maior a tendência de sentir que o LLM o ajuda rapidamente
Na prática, já tive a experiência de gerar mais valor em projetos de software justamente quando estava completamente afastado do computador e da tecnologia
Quando você sabe exatamente o que quer, a velocidade de desenvolvimento aumenta drasticamente, e nesse momento o LLM se torna muito útil
Se há um objetivo claro, também dá para identificar imediatamente as alucinações do LLM
Não acho eficaz aquela abordagem de simplesmente encarar uma tela em branco e começar
O LLM pode ajudar a dar a partida, mas, se não tomar cuidado, muitas vezes ele acaba levando para um caminho totalmente errado
Os problemas mais difíceis, aliás, eu já resolvi enquanto cozinhava na cozinha e pensava sobre eles
Um dos motivos pelos quais tive mais sucesso do que colegas com AI coding é que, antes mesmo da chegada dos LLMs, eu já repetia o processo de prototipar algo rapidamente e depois refazer totalmente a estrutura ou até descartar tudo
Esse tipo de abordagem — prototipagem rápida, refatoração contínua etc. — é bem conhecido, mas muitos engenheiros tendem a tentar projetar tudo perfeitamente desde o início ou então ficam só remendando o protótipo sem realmente melhorá-lo
Com AI, também ficou barato fazer várias implementações em paralelo, então dá para testar e comparar diversas versões e, no fim, formar uma teoria ou um design do programa mais forte, mais rápido e com mais eficiência
A AI encurta esse ciclo e permite focar mais só na revisão do que foi produzido, deixando o processo de desenvolvimento muito mais eficiente
Achei marcante a conexão entre o conceito de "construção de teoria" e a "dívida de entendimento" do código gerado por LLM
Acho que isso se relaciona profundamente não só com programação, mas também com o próprio processo humano de pensar e compreender
Código, texto ou imagens produzidos por LLM são apenas resultados superficiais; se você não construir nem vivenciar diretamente a teoria por trás disso, o entendimento inevitavelmente fica só na superfície
Mesmo que um dia os LLMs consigam fazer a própria construção de teoria, ainda assim me parece que um entendimento artificial em que o ser humano não participa diretamente do processo teria pouco significado
Existe a preocupação de que, quanto mais conveniente ficar deixar a máquina pensar por nós, mais a capacidade humana de <entender> vai se atrofiando aos poucos
Concordo com Lamport e, ao mesmo tempo, acho que a AI pode ajudar em certa medida também no processo de "construção de teoria" — isto é, no entendimento do codebase, dos algoritmos e do sistema como um todo — tanto para a equipe original quanto para a equipe que recebe a transição
Mesmo que seja difícil substituir completamente todo o conhecimento, espero que a AI consiga preencher parte dessas lacunas
Já houve um caso em que uma nova equipe assumiu um projeto depois que todos os desenvolvedores anteriores tinham saído, e foi um período duríssimo porque todo o conhecimento da equipe anterior simplesmente evaporou
Tentamos entender o design original, acabamos produzindo vários bugs e, no fim, sofremos bastante reescrevendo e expandindo grande parte do código
Mas, no meio dessas dificuldades, também houve um processo de reaprender na prática muitos dos problemas de design
LLMs muitas vezes produzem soluções que funcionam, mas em muitos casos geram código muito mais complexo do que o necessário
No momento em que o código é escrito pela primeira vez, você entende melhor do que ninguém qual é o problema, então consegue remover a complexidade com facilidade; depois, porém, fica fácil confundir aquele código complexo com algo realmente necessário, o que torna muito mais difícil simplificá-lo drasticamente
Quem mantém o código geralmente não tem o contexto nem o conhecimento de fundo, então muitas vezes nem percebe que uma alternativa mais simples seria possível
Primeiro, apresente prompts claros para induzir o LLM a não produzir resultados desnecessariamente complexos
Segundo, é preciso sempre passar regras, treinamento ou contexto para que ele resolva o problema da forma mais simples possível
Por fim, se a solução gerada ficar complexa demais, basta usar prompts adicionais para pedir redução da complexidade
O problema de LLMs produzirem enormes quantidades de código difícil de depurar existe de verdade
Existe o princípio de que "equipes que se importam com qualidade precisam revisar e entender bem", mas a velocidade de geração de código é tão alta que a revisão muitas vezes não acompanha, virando gargalo ou sendo reduzida a uma aprovação formal
Ao meu redor, as pessoas continuam adicionando mais processos de revisão, mas esse tipo de abordagem só funciona para desenvolvedores juniores, e a AI não aprende, então os mesmos problemas se repetem
LLMs precisam de muito contexto, mas a maioria das pessoas usa quase sem dar informação nenhuma, no estilo "faz uma função que resolva isso"
No fim, todo mundo está empilhando uma montanha de código que ninguém entende, ou seja, tech debt
O que realmente faz falta, na raiz do problema, é um "primitivo" que permita transmitir com mais eficácia ao LLM por que aquilo deve ser feito, com qual contexto, intenção e precedentes
Se code review vira gargalo, ainda assim acho melhor que o gargalo seja só a revisão do código do que ter de fazer tanto a codificação quanto a revisão
Ferramentas que ajudam a complementar esse processo — lint, fuzzing, testes etc. — já existem
Para quem não tem habilidade de arquitetar o projeto inteiro ou ler e analisar código rapidamente, a era dos LLMs pode ser difícil, mas essas capacidades podem ser desenvolvidas, e com o tempo todo mundo vai se adaptar
Gosto dessa área e encaro positivamente esse novo desafio
Já tive a experiência de criar vários arquivos instruction .md com regras de codificação que os agentes devem seguir, armadilhas a evitar obrigatoriamente, links para documentos de padrões de código etc., mantendo tudo atualizado
Modelos como Gemini e Claude refletem relativamente bem esse tipo de instrução baseada em documentos, mas às vezes ainda repetem erros (e.g., mesmo quando você manda não usar
autoem C++, eles não obedecem)Espero que, à medida que os modelos melhorem, esse processamento de feedback também evolua
No fim, senti que o compromisso ideal é sair do "vibe coding", deixar que humanos cuidem diretamente da estrutura do código e das interações entre unidades, e fazer a AI se concentrar em sintaxe e digitação; isso aumenta a produtividade e permite que o desenvolvedor continue conduzindo a direção geral
Depois que mudei minha forma de pensar sobre como estruturar prompts e contexto, minha intuição sobre o uso de LLMs ficou bem mais refinada
Passei a abordar isso pela ótica de reduzir, por meio do prompt e do contexto, o espaço probabilístico de resultados possíveis
Esse processo também é muito eficaz para injetar, de forma reutilizável, a teoria do código
Ainda assim, preparar esse contexto exige muito esforço e reflexão, e ainda não é fácil encontrar a linha que separa os trechos em que implementar diretamente é melhor daqueles em que vale mais a pena delegar ao LLM
Concordo com a observação de que "o primitivo é outro" e acho que a unidade realmente importante é o "teste"
Faço o modelo escrever testes junto com o código e sempre verifico se os testes são fáceis de ler
Só se deve fazer merge do código quando a suíte de testes inteira passar
Também é preciso melhorar continuamente a infraestrutura de testes para que a suíte toda não fique lenta demais
Código legado da velha guarda tinha como característica a ausência de testes, e isso também vale na era dos LLMs
Sobre a observação de que "vira gargalo", ao colar código do StackOverflow eu também sempre leio e reviso antes de usar, então, de qualquer jeito, o gargalo é o ser humano
No fim, sem passar por esse processo, é praticamente impossível usar o código mesmo assim
Recentemente, no podcast do Dwarkesh Patel, um convidado recomendou o romance <A Deepness In The Sky>, e ao lê-lo achei marcante o fato de que sistemas computacionais de milhares de anos no futuro e conhecimento legado do passado têm papel central
É um excelente romance que trata de forma interessante o valor do código legado e do conhecimento organizacional, então vale a recomendação
Podcast: https://www.youtube.com/watch?v=3BBNG0TlVwM
Informações do livro: https://amzn.to/42Fki8n
É triste saber da morte do autor Vernor Vinge, e sinto que suas ideias ficam cada vez mais inspiradoras e realistas com o passar do tempo
A parte que descreve o processo de se tornar programador em um futuro distante foi realmente fascinante
Por causa da enorme quantidade de bibliotecas e pacotes, a principal habilidade não era escrever código novo, mas encontrar e combinar módulos já existentes
A verdadeira competência era descrita como a capacidade de dominar as nuances e interpretações detalhadas do código existente
"A Deepness In The Sky" parece ser o segundo livro da série, então fiquei na dúvida se dá para ler sem ter lido o primeiro
Pergunta se é tranquilo começar direto por ele
A maioria dos desenvolvedores não entende assembly nem código de máquina em profundidade
Linguagens de alto nível se tornaram a camada central para comunicação e colaboração entre humanos
Com a chegada dos LLMs, essa camada está sendo empurrada em direção ao desenvolvimento baseado em linguagem natural e especificações
No fim, acho que as linguagens de alto nível evoluíram por décadas e hoje transmitem a especificação do comportamento do programa em uma forma quase ótima
Se tentarmos abstrair demais em linguagem natural, há perda de informação; se formos para linguagens de baixo nível, tudo fica prolixo demais
Esse salto para a linguagem natural não é apenas uma mudança de camada de abstração, mas algo fundamentalmente novo
Ferramentas anteriores — assemblers, compiladores, frameworks etc. — eram todas baseadas em lógica hardcoded e permitiam verificação matemática, mas depois dos LLMs estamos entrando num mundo misturado com incerteza, suposição e até alucinação
Muitos desenvolvedores JavaScript sequer entendem profundamente conceitos de alto nível como estruturas de dados adequadas, DOM, Node API etc.
Surge uma dependência excessiva da camada de abstração e chega-se a um ponto em que o funcionamento interno já não é bem compreendido
Para quem está de fora, é inevitável pensar: "o que exatamente está acontecendo aqui?"
Esse fenômeno já é aceito como algo cotidiano
A comparação é que, no fim, como ninguém sabe exatamente o que acontece dentro do código, não haveria tanta diferença essencial quando o LLM escreve esse código
Faz sentido que a especificação em linguagem natural tenha um papel, mas acho indispensável uma camada intermediária com semântica rigorosa
Como exemplo, na combinação de Rust com LLMs, o sistema de tipos forte fecha estados inválidos e, mesmo que a compilação demore, no fim o resultado desejado costuma sair certo na maior parte das vezes
Existe na comunidade Rust a cultura de que "se compilou, em geral funciona"; ainda pode haver bugs lógicos, mas o espaço real de erro fica menor
Idealmente, seria ótimo ter uma linguagem de programação rigorosa que definisse de forma precisa o significado lógico, com especificações em pré-condições, pós-condições etc., e o LLM ficaria encarregado de converter linguagem natural em especificações formais
A linguagem natural, por definição, não é clara; ela carrega ambiguidade
Se houver ambiguidade no parsing de uma linguagem de programação, isso é visto como um bug grave, mas na linguagem natural a ambiguidade cumpre funções de comunicação como poesia, nuance e sugestão
A natureza determinística de alto nível (
import) não é a única diferençaDeterminismo não é tudo em programação, e código cujo significado não é claro para humanos ainda pode ser perfeitamente determinístico
Ou seja, os eixos "abstração alta/baixa" e "determinismo/probabilismo" são questões completamente diferentes
Isso me parece uma enorme onda de trabalho que está vindo para mim e para a minha equipe
Há quase 8 anos mantemos o negócio vivo resgatando código legado, mas recentemente a demanda caiu porque as empresas passaram a depender de LLMs para programar
Mesmo assim, prevejo que, se aguentarmos mais uns 18 meses, surgirá uma enorme oportunidade quando começar a chover pedido para resolver a dívida técnica baseada em LLM acumulada em pouco tempo
Logo vai aparecer o estrago da dívida acumulada enquanto o Claude dizia: "agora o código está em nível de produção"
Ouvi uma história de alguém conhecido que revisava um PR feito por LLM
Era um PR que, por fora, parecia funcionar perfeitamente, mas, ao olhar por dentro, descobriram que na verdade ele não atualizava o backend e só manipulava o cache para fingir o resultado
Foi preciso muito esforço para convencer o gerente de que aquele código não podia ser mergeado
No fim, isso faz suspeitar que exista bastante software "vibe coded" no mundo que só parece funcionar na superfície
Nesses casos, acho até melhor deixar fazer o merge e a pessoa sentir na pele as consequências depois
Geralmente, a pessoa só aprende de verdade quando experimenta o resultado de uma decisão errada
Sem feedback, não há aprendizado, e esse tipo de gerente acaba apenas elevando expectativas irreais e pressão sobre o time de desenvolvimento, o que leva ao desgaste da equipe e a um ciclo de substituição de pessoas
Fico me perguntando como um engineering manager não técnico consegue permanecer tanto tempo na indústria
Nos últimos 15 anos, quase não vi nenhum gerente realmente não técnico
Acho que, se esse gerente causou problema desse tipo, isso deveria ser reportado a CTO, CEO, dono, investidores etc.
Não é só código de LLM; qualquer código escrito por outra pessoa gera "dívida de entendimento" para quem vem depois
Mesmo em grandes empresas como o Google, engenheiros novos precisam de meses de entendimento antes de conseguirem fazer mudanças grandes em algoritmos com rapidez
Mas código bem projetado por humanos é, sem dúvida, muito mais fácil de entender do que saídas de LLM
Você pode ouvir a explicação diretamente de uma pessoa, e um LLM também pode dar uma resposta plausível, mas existe uma diferença real de contexto
Eu também já fiz muito "vibe coding", mas no fim o modelo mental do código não se acumula, então você até economiza tempo no início, só para perder muito mais depois na hora de depurar
Também é impossível, no mundo real, definir perfeitamente todo o design desde o começo
E não acho confiável usar "linhas de código aceitas" como métrica de produtividade ou de economia de tempo
Usar código dependente de LLM com revisão cuidadosa e ir moldando até ficar com a forma correta é, sem dúvida, eficaz
Esse processo se parece mais com "pair programming"
Enfatiza que usar a saída do LLM diretamente, sem qualquer revisão — o verdadeiro "vibe coding" — não pode ser realmente eficiente
Citando a Lei de Kernighan, dizem que "depurar é duas vezes mais difícil do que escrever o código"
Por isso, se o LLM gera o código, talvez seja preciso um LLM duas vezes mais inteligente para depurá-lo
Minha experiência não foi necessariamente essa; eu sempre preciso continuar no banco do motorista
Uso principalmente Claude Code e, sempre que ele erra, preciso apontar claramente o erro para corrigir ou destacar separadamente a área problemática
O LLM não sabe por si só qual erro cometeu, então a sensação é muito parecida com trabalhar com um desenvolvedor júnior
Ou seja, a depuração em si pode até acontecer no mesmo nível de inteligência, mas o LLM não consegue reconhecer o problema sozinho
Sobre a frase de que "para depurar é preciso alguém duas vezes mais inteligente", acho que isso pode ter relação com os diferentes modos de "profundidade de raciocínio" do LLM
Quando é uma função complexa, costumo primeiro fazer o LLM escrever e anexar comentários no código explicando claramente a intenção e a abordagem no relatório do LLM
Há um comentário dizendo que, mesmo quando se reutiliza algo escrito por outro desenvolvedor, ainda é necessária revisão, então no caso de um LLM não seria diferente, mas eu não concordo. No caso de humanos, entram em jogo reputação, prestígio, recompensa e punição. Se alguém trabalhasse hoje como os LLMs trabalham, provavelmente seria demitido na hora.
Eu sempre penso: "a IA não assume a responsabilidade no seu lugar."
Acho que não está longe o dia em que o uso de IA para escrever código será proibido. Parece absurdo, mas eu acho que isso vai virar realidade.