Programar não é codar: O impacto das LLMs na programação
TL;DR
- O entendimento é, e continuará sendo, o ativo mais valioso no desenvolvimento de software.
- Escrever é uma das formas mais eficazes de construir e solidificar esse entendimento.
- LLMs podem produzir código, mas ao fazê-lo, frequentemente reduzem o entendimento que adquiriríamos de outra forma.
- Uma das habilidades chave na era da IA é saber quando o entendimento é mais importante do que ter algo que só funciona.
- A indústria está sistematicamente superestimando o valor do código que só funciona e subestimando o custo da falta de entendimento.
Ressalvas
- LLMs são ferramentas úteis. Elas já melhoram o trabalho diário de muitos programadores (incluindo eu), e esse impacto só tende a crescer. Este texto não é um argumento contra o uso de IA, nem contra pessoas que dependem dela.
- Eu também não sou afiliado, nem patrocinado, por nenhuma organização explicitamente pró- ou anti-IA.
O que realmente é programação?
“Codificar está para programar assim como digitar está para escrever.” - Leslie Lamport (tradução livre)
Programação, em sua essência, é o ato de definir um conjunto de instruções a ser executado por um computador. Este conjunto de instruções é o que chamamos de programa.
Acredito ser óbvio que uma pessoa que não sabe cozinhar não pode produzir uma receita adequada, mesmo que fale português perfeitamente. O mesmo se aplica à programação: pode-se saber tudo o que há para saber sobre uma linguagem de programação e ainda ser completamente incapaz de resolver um problema com ela.
Em suma, saber C não te torna Linus Torvalds, assim como saber português não te torna Machado de Assis.
Para programar, é preciso não apenas dominar a linguagem em que o programa está escrito, mas também entender profundamente o problema que está sendo resolvido e suas restrições. Esse entendimento deve ser profundo o suficiente para ser traduzido em uma sequência concreta de passos que um computador real pode executar.
Programar, portanto, não é apenas codificar. E seu resultado não é apenas o código que funciona, mas também o entendimento do problema, dos tradeoffs e das restrições acumuladas ao longo do caminho.
Por que o entendimento é importante? (e sempre será)
“Não tive tempo de escrever uma carta curta, então escrevi uma longa.” - Mark Twain (tradução livre)
Quando você entende algo, você consegue distinguir o que realmente importa do que é ruído. Isso permite que você expresse soluções de forma mais clara e eficiente.
Na programação, a falta de entendimento frequentemente se manifesta como lógica desnecessária expressa como código ruim que roda muito lentamente porque usa as abstrações erradas e causa mais problemas do que resolve.
Essa ineficiência se propaga em muitas dimensões: recursos computacionais desperdiçados, aumento do esforço para adicionar funcionalidades, dificuldade em manter a compatibilidade com versões anteriores e maior carga cognitiva para qualquer um que tente entender o código mais tarde.
Mas mesmo que você consiga construir um sistema perfeito, ele é perfeito apenas para um contexto específico. Eventualmente, os requisitos mudam, as restrições se alteram ou as suposições se tornam inválidas.
Quando você entende um sistema, pode adaptá-lo incrementalmente. Você não precisa demolir a casa só porque quer pintar uma parede.
Na programação, a falta de entendimento frequentemente leva ao clássico cenário de "reescrever do zero". Como não podemos raciocinar sobre a estrutura existente, a única solução aparente é jogar tudo fora e começar de novo.
A necessidade de eficiência e a presença constante de mudanças garantem que o entendimento sempre será importante na programação.
Impactos da geração de código por LLMs no entendimento
“Escrever é a maneira que a natureza tem de te deixar saber o quão desleixado é o seu pensamento.” - Richard Guindon (tradução livre)
É fato bem estabelecido que escrever é uma das formas mais eficazes de consolidar conhecimento e construir entendimento. Programar é uma forma particularmente rigorosa de escrita: ela usa uma linguagem extremamente precisa e fornece feedback rápido e não ambíguo quando seu modelo mental está errado.
Quando um LLM traduz um prompt vago ou parcialmente formado em código funcional, ele remove grande parte dessa fricção. Mesmo que o código gerado seja idêntico ao que você teria escrito, o entendimento que você ganha com o processo é significativamente reduzido.
O entendimento é construído através da fricção: lidar com ideias conflitantes, notar que uma abstração está vazando, antecipar um bug enquanto escreve a linha que o introduz, reescrever a mesma lógica várias vezes antes de reconhecer um padrão comum. Esse processo força a clareza.
Ao delegar a maior parte da escrita a um LLM, essa fricção é fortemente diluída ou totalmente removida.
Usar o prompt ou não usar o prompt?
"Depende." - Algum Desenvolvedor Sênior
Isso tudo naturalmente levanta a questão: quando devemos usar LLMs e o quanto devemos depender delas?
Curiosamente, essa pergunta é anterior aos LLMs. É essencialmente a mesma pergunta que fazemos ao decidir se devemos usar uma biblioteca ou um serviço externo: o quanto disso eu preciso entender?
Idealmente, entenderíamos tudo o que executamos. Na realidade, delegamos o entendimento através de interfaces e APIs bem definidas, confiando em outros para implementar os detalhes.
Bibliotecas são úteis? Com certeza. Tudo deveria ser uma biblioteca? Provavelmente não. Nesse sentido, o "vibe coding" pode ser visto como uma forma extrema de dependency hell: grandes quantidades de comportamento são introduzidas no sistema sem um entendimento claro de como ou por que funcionam.
Uma das habilidades mais valiosas na era dos LLMs é saber qual fricção é produtiva e escolher conscientemente não evitá-la.
Mas as empresas só querem que o código que funcione
"Qualidade é grátis. São as coisas sem qualidade que custam dinheiro." - Philip Crosby (tradução livre)
Boas empresas se preocupam com o que importa e, às vezes, o código que funciona é bom o suficiente, mesmo sem um entendimento completo.
Costumávamos chamar esses projetos de protótipos: soluções rápidas construídas para explorar um espaço de problemas, coletar feedback ou validar suposições. Nesses casos, otimizar pela velocidade em detrimento do entendimento pode ser totalmente razoável.
Há outros casos legítimos também: restrições apertadas de time to market, tarefas únicas ou ferramentas internas descartáveis.
Os problemas causados pela falta de entendimento se tornam visíveis quando as coisas mudam: Problemas em produção, incidentes de segurança, gargalos de desempenho, requisitos regulatórios, rotatividade de equipe ou migrações de sistema. Quando uma mudança significativa ocorre, sistemas construídos sem entendimento rapidamente atingem o estágio de "demolir a casa para pintar a parede".
Boas empresas sabem quando otimizar pela velocidade e quando investir em entendimento. Falhar em fazer isso leva a ser superado por concorrentes ou a colapsar sob a complexidade acumulada.
Conclusão
"Não há caminho nobre para a geometria." - Euclid (tradução livre)
O entendimento é fundamental para a programação, porque dar instruções corretas exige saber o que essas instruções significam e por que elas existem.
LLMs são ferramentas poderosas, mas nos permitem gerar grandes quantidades de código que não entendemos. Com o tempo, a relação entre sinal e ruído da base de código se degrada, tornando o raciocínio e a mudança progressivamente mais difíceis.
Otimizar pela velocidade nem sempre é a decisão de negócios correta. O entendimento leva tempo e fricção, mas essa fricção é frequentemente o que possibilita o progresso a longo prazo.
Algumas pessoas veem a fricção como inerentemente ruim. Mas sem fricção, como Newton já demonstrou, não temos tração e, portanto, não podemos mudar de direção nem pegar impulso.