Enable javascript in your browser for better experience. Need to know to enable it? Go here.

Ainda precisamos projetar código para humanos?

Há poucas semanas, participei do evento Future of Software Engineering em Engelberg, na Suíça. Como seria de se esperar de um evento realizado em meados de 2026, muitas conversas giraram em torno das práticas de engenharia de IA e dos agentes de codificação.

 

Encontrei uma grande variedade de pontos de vista no evento. Alguns disseram preferir manter os agentes sob controle estrito, revisando seu resultado linha por linha para garantir que o código continue sustentável, bem particionado e com tudo mais que associamos ao bom artesanato de software (software craftsmanship).


Outros afirmaram que preferem delegar a maior parte do ciclo de vida de desenvolvimento de software para os agentes. Esse grupo, de modo geral, não estava particularmente preocupado com o código que os agentes realmente produzem; para eles, o tempo é mais bem investido na melhoria das especificações (specs) e das verificações determinísticas que guiam o agente, deixando que ele cuide completamente do código (desde que o build continue verde, é claro).

 

Os humanos nunca mais precisarão olhar para o código?

 

O extremo da "autonomia total" nesse espectro é o que mais me faz refletir. Isso me lembrou de uma ideia maior que vejo circular com frequência nas minhas redes sociais e nos meus feeds:

 

Em breve, nenhum ser humano voltará a olhar ou manter código. Será que o bom design de software ainda vai importar daqui a alguns anos?*

 

O argumento é mais ou menos o seguinte: à medida que os fluxos de trabalho com agentes evoluem, no fim das contas tudo o que os humanos terão de produzir serão especificações em Markdown, árvores de contexto ou qualquer novo padrão de interação que seja popular daqui a alguns meses. Essa especificação poderá ser jogada por cima do muro para uma equipe de agentes de IA que irá gerar, testar, revisar, implantar e, posteriormente, evoluir o código. Eventualmente, o código pode se tornar pouco mais do que um detalhe de implementação para a maioria das empresas; podemos até ter agentes de plantão (on-call), respondendo a incidentes em produção às 2h da manhã.

 

Algumas vertentes do desenvolvimento orientado a especificações (spec-driven development) já defendem o uso de specs como a nova lingua franca do desenvolvimento de software, tornando o código um artefato efêmero.

 

A segunda linha do Manifesto Ágil afirma: "Software em funcionamento mais do que documentação abrangente". Deveríamos refraseá-la para: "Software em funcionamento como documentação abrangente"? Esta é, inegavelmente, uma pergunta desafiadora para pessoas como eu, que se orgulham profissionalmente de criar um código limpo e legível. E sim, admito que há um certo conflito de interesses: este é, afinal, um artigo escrito por um programador explicando por que a programação ainda é necessária.

 

No entanto, o que se segue é uma coleção das minhas reflexões pessoais sobre o tema, fundamentadas tanto nas conversas que tive no evento quanto na minha própria experiência mantendo sistemas em produção com a ajuda de agentes.

 

Assim, ao longo do restante deste artigo, vou explicar por que acredito que o bom design ainda importa (mesmo com os agentes fazendo a maioria das alterações), por que os humanos precisam continuar no processo (human in the loop) para produzir softwares bem projetados e por que esses mesmos humanos precisam continuar atuando no nível do código para alcançar isso.

 

Mas antes de entrar nisso, vale a pena entender o argumento de que estamos apenas lidando com mais uma camada de abstração. Na minha visão, não é o caso, e a crença de que seja é justamente o ponto central dessa questão.

As specs são realmente a próxima camada de abstração?

 

Não é segredo para ninguém que o software parece ter evoluído gradativamente de uma camada de abstração para a seguinte. Passamos do binário e dos cartões perfurados para o assembly, e do assembly para linguagens de alto nível. Nós (em grande parte) paramos de alocar e desalocar memória manualmente. Servidores foram substituídos por máquinas virtuais, que depois foram substituídas por contêineres, e assim por diante. Não é que essas abstrações tenham feito as camadas inferiores desaparecerem, mas com certeza mudaram a frequência com que precisamos interagir com elas.

 

Desenvolvedores de aplicação como eu raramente pensam em assembly hoje em dia, porque a abstração acima dele é quase sempre suficiente, mesmo que possa ser incompleta. De fato, a maioria dos desenvolvedores opera no topo de uma torre de abstrações empilhadas umas sobre as outras. Fazemos isso conscientemente porque é mais produtivo, e simplesmente aceitamos que perdemos de vista os detalhes de baixo nível.

 

É tentador prever que a programação baseada em agentes possa ser o próximo tijolo colocado no topo dessa torre, e que as specs se tornarão para o código-fonte o que o C foi para o assembly

 

Em vez de expressar a intenção no código, agora podemos expressá-la em linguagem natural. O agente traduz a prosa em código-fonte, assim como um compilador traduz C em um binário. Daqui a alguns anos, argumentam alguns, saber ler código pode se tornar algo tão nichado quanto saber ler cartões perfurados. O papel do "programador" mudaria radicalmente para produzir specs específicas o suficiente ou, talvez, até desapareceria por completo.

 

Isso tornaria as preocupações tradicionais de design de software obsoletas: por que criar abstrações elegantes quando nenhum humano vai vê-las? Por que refatorar duplicações, code smells e nomes ambíguos quando o agente dá conta do recado de qualquer maneira?

O bom design também importa para os agentes

 

Há um bom motivo para ter cautela antes de declarar o design orientado a humanos como obsoleto: afinal de contas, os agentes de codificação de hoje foram treinados em softwares escritos por humanos.

O código com o qual eles aprenderam não é uma bagunça aleatória e minificada que apenas produz exatamente o resultado descrito em sua documentação. Os agentes aprenderam com códigos que foram minuciosamente particionados em funções e classes com nomes significativos, repleto de abstrações e padrões de pensamento humano por toda parte. Os dados de treinamento de cada modelo refletem décadas de modelos mentais humanos, padrões de design e uma linguagem compartilhada.

 

Como resultado, muitas das práticas que associamos ao "bom design" não são úteis apenas para leitores humanos. Elas também são os padrões nos quais os agentes aprenderam a confiar ao tentar inferir o que precisam fazer a seguir em uma base de código existente. Não importa se o código anterior foi escrito por um humano ou não: se ele for emaranhado e incompreensível para uma pessoa, há uma grande chance de que o agente também tenha dificuldades para modificá-lo.

 

É por isso que (e isso é algo que observei na prática) quando um agente encontra um sistema fortemente acoplado, nomes inconsistentes, lógica de negócios duplicada ou a clássica "grande bola de lama" (big ball of mud), seu resultado se torna menos confiável. Já vi agentes fazerem muito mais suposições incorretas e erros crassos ao lidar com bases de código menos legíveis, o que acaba exigindo intervenção humana. Eles também consomem volumes cada vez maiores de contexto para entender adequadamente como o sistema funciona e podem se perder em detalhes desnecessários ao tentar depurar comportamentos inesperados. Isso significa gastar mais tokens e aumentar a probabilidade de falhas.

 

O design ruim não deixa de importar só porque é uma IA que está escrevendo e lendo o código. O custo simplesmente muda de lugar. Em vez de pagar em esforço cognitivo humano, também pagamos em inchaço de contexto, queda de confiabilidade e, claro, dinheiro real em tokens. Na verdade, pode ser a primeira vez na história do software em que é possível quantificar o custo exato do débito técnico: tudo o que precisamos fazer é dar uma olhada na fatura mensal do Claude da equipe.

 

Em outras palavras, a forma como os agentes falham ao encontrar um design ruim me sugere que o bom design continua sendo relevante para a economia de manutenção de produtos de software.

 

Mas os agentes não poderiam simplesmente ser encarregados de criar códigos que sejam fáceis de alterar por eles mesmos? Vamos falar novamente sobre o fator humano no processo (humans in the loop).

Por que os humanos ainda são necessários para produzir softwares 'bem projetados'

 

Acredito que existam vários motivos pelos quais o design continua sendo uma responsabilidade humana, e não algo que possamos delegar completamente, todos eles indo muito além de simplesmente repetir o argumento de que "a IA ainda não é boa o suficiente".

 

Em vez disso, quero refletir sobre três problemas que considero limitações inerentes aos fluxos de trabalho puramente baseados em agentes produzindo código sem assistência.

 

Problema 1: O código gerado por agentes se degrada com o tempo, e os agentes vão deixar isso acontecer

 

Uma das coisas mais impressionantes sobre os agentes é a rapidez com que conseguem produzir um software funcional a partir de uma tela em branco. No entanto, eles ainda são muito recentes para que a maioria das empresas experimente o que acontece em uma base de código gerada por agentes após a centésima funcionalidade, ou no segundo ano em produção.

 

O software "se deteriorar" não é um fenômeno novo. Quando isso é provocado por humanos, chamamos de entropia de software ou débito técnico, e tentamos (com sucessos variados) explicar o motivo aos nossos stakeholders quando nos perguntam por que nossa velocidade diminuiu.

 

Lembra daquele gráfico que destacava a relação entre débito técnico e velocidade? (Existem muitas versões disso circulando ao longo dos anos, e Martin Fowler tem uma bem parecida neste artigo).

Acredito que a maioria dos produtos de software gerados por agentes na indústria esteja vivenciando essa curva de "economia inicial" neste exato momento.

 

Os agentes são ainda piores do que os humanos em seguir a Regra do Escoteiro™ e tendem a deixar o código pior do que o encontraram. Mesmo com os melhores linters ou ferramentas sofisticadas como o ArchUnit, eles encontrarão maneiras surpreendentes de "burlar o sistema" só para obter um build verde. E as inconsistências tendem a se acumular à medida que mais agentes são acionados para adicionar cada vez mais funcionalidades. Todas essas exceções e otimizações locais, uma a uma, contribuem para a complexidade geral do sistema e se tornam "apenas mais uma coisa para se lembrar" para o próximo agente. Isso é a definição de livro-texto de débito técnico.

 

No entanto, a diferença entre o débito técnico gerado por humanos e o gerado por agentes é que os agentes conseguem introduzir essas alterações muito mais rápido do que os humanos jamais conseguiriam, fazendo com que os efeitos se acumulem mais cedo. Sem que alguém dê um passo atrás periodicamente, reconheça os problemas de design emergentes e reestruture o sistema deliberadamente, a base de código se tornará uma grande bola de lama ainda mais rápido.

 

Também não nos esqueçamos de que, mesmo em um sistema desenvolvido 100% por humanos, conforme novas funcionalidades são introduzidas, a arquitetura original se torna uma representação cada vez menos adequada dos requisitos em constante evolução. O design precisa evoluir com o sistema, com humanos ponderando sobre as formas como os novos requisitos podem entrar em conflito com o design atual.

 

Contudo, os agentes não conseguem questionar uma spec ou dar um passo atrás para reavaliar a arquitetura da forma como os humanos fazem. Eles não conseguem escalar preocupações arquitetônicas para o restante da equipe e para os stakeholders. Se conseguirmos automatizar isso, aí sim considerarei dar um passo atrás na indústria e buscar outras opções de carreira.

Valentina Servile, Thoughtworks
Se o código deve continuar sem transparência só porque um agente consegue fazê-lo mais rápido, os programadores terão apenas que aumentar a estrutura de suas specs. Em determinado momento, não estaremos mais substituindo a programação, mas simplesmente programando em uma linguagem de programação pior e mais ambígua.
Valentina Servile
Principal Software Engineer, Thoughtworks
Se o código deve continuar sem transparência só porque um agente consegue fazê-lo mais rápido, os programadores terão apenas que aumentar a estrutura de suas specs. Em determinado momento, não estaremos mais substituindo a programação, mas simplesmente programando em uma linguagem de programação pior e mais ambígua.
Valentina Servile
Principal Software Engineer, Thoughtworks

Problema 2: Uma spec específica o suficiente equivale ao próprio código

 

Uma forma óbvia de tentar resolver o primeiro problema é escrever especificações de design extremamente sólidas que os agentes devem respeitar antes de realizar qualquer alteração.

 

Em vez de pedir a um agente que implemente a funcionalidade X, poderíamos especificar a arquitetura, os limites dos módulos, o tratamento de erros, as convenções de nomenclatura, a estratégia de testes, os requisitos de desempenho e todas as restrições de design importantes. Poderíamos até pedir ao agente que escrevesse todo tipo de estrutura de testes determinísticos que falharão no build caso essas restrições não sejam respeitadas.

 

Claro que tudo isso depende fortemente do contexto, do que a aplicação faz e para quem ela é feita. Não podemos simplesmente criar um livro universal de Clean Code para Agentes e carregá-lo no contexto de cada agente de forma indiscriminada. Nossa spec de design precisa ser ajustada exatamente ao tipo de sistema que está sendo construído, e instruções ainda mais específicas devem ser adicionadas para certas funcionalidades que possam exigir a atualização das convenções existentes. Talvez cada nova funcionalidade exija suas próprias restrições de design e exceções.

 

Espero que você consiga ver onde quero chegar com isso, mas para deixar claro: à medida que as especificações se tornam cada vez mais precisas, elas começam a se parecer suspeitamente com código.

 

Sim, a linguagem natural é expressiva e mais acessível do que o código, mas também é extremamente ambígua. Eliminar essa ambiguidade costumava exigir que os programadores traduzissem os requisitos do seu Product Owner (PO) ou Product Manager (PM) e preenchessem as lacunas onde necessário, para que pudessem escrever um código sem ambiguidades e com um design apropriado. Mas se o código deve continuar inacessível e fechado só porque um agente aparentemente consegue fazê-lo mais rápido, os programadores terão apenas que aumentar a estrutura de suas specs. Em determinado momento, não estaremos mais substituindo a programação, mas simplesmente programando em uma linguagem de programação pior e mais ambígua.

 

Vale a pena voltar até 1978 para resgatar algumas palavras esclarecedoras sobre isso:

"A virtude dos textos formais é que suas manipulações, para serem legítimas, precisam satisfazer apenas algumas poucas regras simples; eles são, quando se para para pensar, uma ferramenta incrivelmente eficaz para descartar todo tipo de absurdo que, ao usarmos nossas línguas maternas, é quase impossível de evitar.

 

Em vez de considerar a obrigação de usar símbolos formais como um fardo, deveríamos considerar a conveniência de usá-los como um privilégio [...]. No fim das contas, a 'naturalidade' com que usamos nossas línguas maternas se resume à facilidade com que podemos usá-las para fazer declarações cujo absurdo não é evidente."

 

— Edsger W. Dijkstra, On the foolishness of 'natural language programming (1978)

Pessoalmente, prefiro projetar o código em parceria com o agente em vez de simplesmente jogar specs em linguagem natural para ele. Fazer isso permite que o trabalho de resolver ambiguidades em um conjunto bem organizado de regras seja feito de forma incremental, à medida que o código é escrito. Isso não apenas economiza o tempo gasto em idas e vindas, mas também nos permite aproveitar o código como um vocabulário compartilhado e não ambíguo para um bom design enquanto fazemos programação em par (pairing) com o agente.

Problema 3: Os agentes não são determinísticos

 

Este é meu ponto final e, embora possa parecer óbvio, precisa ser reforçado. A maior diferença entre a programação baseada em agentes e as camadas de abstração anteriores é que os agentes não são determinísticos, enquanto as camadas anteriores foram, historicamente, todas determinísticas.

 

Compiladores não são sistemas mágicos sem transparência onde você joga código de um lado e sai um binário do outro, esperando que esse binário faça o que você escreveu sem introduzir casos de borda alucinados; compiladores seguem regras. Diante da mesma entrada, eles produzirão com precisão o mesmo resultado. Por outro lado, se você pedir a um agente para implementar a mesma funcionalidade cinco vezes, é muito provável que receba cinco soluções diferentes.

 

Como os agentes fazem escolhas de codificação alimentando um mecanismo sem transparência de dados de treinamento desconhecidos com contexto e suposições, alguém ainda precisa estar do outro lado para determinar se essas escolhas são apropriadas. Precisamos decidir quais trade-offs fazem sentido para o sistema e determinar se a implementação se ajusta à visão técnica.

 

Para mim, essa é uma tarefa de julgamento humano que vai muito além da simples geração de código.

Um bom design exige olhar para as especificações do código

 

Os problemas que pontei me levam à mesma conclusão fundamental: um bom design de software não é algo que você possa especificar uma única vez em um arquivo Markdown ou em uma estrutura de testes, para depois esquecer o assunto e deixar que os agentes assumam o controle indefinidamente. Nossa indústria tem até um nome para a prática de produzir grandes especificações arquitetônicas e passar os anos seguintes delegando sua implementação: Waterfall (ou Modelo em Cascata).

 

Em vez disso, me alinho à visão do Extreme Programming (XP) de que o design emerge por meio de feedback e ajustes contínuos, sendo renegociado constantemente à medida que o contexto evolui.

 

Uma afirmação que quero acrescentar é que acredito que esse julgamento precisa acontecer em relação ao próprio código, e não apenas em relação a documentos de design, estruturas de testes ou diagramas de arquitetura. Você não consegue decidir de forma consistente se uma responsabilidade pertence a um módulo ou a outro, se uma abstração se tornou genérica demais ou se dois conceitos deveriam ser fundidos sem olhar para a implementação atual. O código é onde o acoplamento, a coesão, a duplicação e a complexidade se tornam visíveis; para mim, ele é o teste definitivo (litmus test) para qualquer suposição de design.

""O design do software não está completo até que tenha sido codificado e testado [...] O design estrutural de alto nível não é um design de software completo; é apenas uma estrutura básica para o design detalhado [...] O design detalhado acabará por influenciar (ou deveria ter permissão para influenciar) o design de alto nível tanto quanto outros fatores.

 

Aprimorar todos os aspectos de um design é um processo que deve ocorrer ao longo de todo o seu ciclo de vida. Se qualquer aspecto for congelado e excluído desse processo de 

— Jack W. Reeves, What is Software Design? (1992)

Mesmo que os agentes se tornem responsáveis por produzir a maior parte do código, os humanos ainda precisarão descer a esse nível mais baixo de abstração para avaliar a saúde do sistema. E, pelo menos na minha experiência com agentes, tive que fazer isso quase todos os dias, especialmente ao trabalhar em sistemas críticos nos quais transações de clientes estavam em jogo.

 

Portanto, minha previsão geral é que o código de produção não desaparecerá como um artefato de engenharia. Ele continuará sendo um objeto que inspecionamos, criticamos e melhoramos continuamente, quer as alterações em si sejam digitadas por humanos ou geradas por agentes.

A IA não é o próximo nível de abstração

 

Em resumo, o design de software ainda importa porque a capacidade de o software mudar ainda importa. E o julgamento humano sobre o código continua sendo crítico para o processo de design de software.

 

Não acho que deixaremos de precisar de humanos capazes de ler código e, como corolário disso, ainda precisaremos construir um código bem projetado para que os humanos consigam lê-lo. Talvez tenhamos que ler menos código coletivamente, mas não creio que deixaremos de precisar de programadores tão cedo se quisermos a) produzir software funcional e b) mantê-lo funcionando por pelo menos alguns anos.

 

A maior mudança, para mim, é que os humanos agora não são o único público para um bom código e um bom design. Precisamos considerar a produção de softwares que sejam fáceis de alterar tanto por humanos quanto por agentes. O público se expandiu de "humanos" para "humanos e agentes", e é provável que aprendamos que alguns princípios de design evoluem enquanto outros permanecem estáveis. Podemos passar menos tempo escrevendo código repetitivo (boilerplate) e mais tempo revisando implementações geradas, avaliando trade-offs e alterando o direcionamento arquitetônico. Isso ainda muda significativamente a descrição do trabalho de um programador, mas não é o mesmo que transformar o código em um "sistema fechado e sem transparência".

 

Em vez de pensar nas specs como substitutas das linguagens de programação, um colega meu ofereceu uma interpretação alternativa: "pense delas como algo que muda o padrão de interação".

 

Em vez de digitar cada linha nós mesmos, estamos colaborando com sistemas que podem gerar e modificar um primeiro rascunho do código com base em linguagem natural. No entanto, isso não nos exime de entender o que, em última análise, colocamos em produção.

 

Se serve de algo, isso eleva a régua para saber como é um bom design, porque agora temos que construir sistemas que permaneçam fáceis de alterar para ambos os tipos de colaboradores. Acho que a discussão se torna mais interessante quando paramos de perguntar se humanos ou agentes escreverão o código e, em vez disso, perguntamos o que "bom design" realmente significa em um mundo onde ambos participam.

*Nota: Uso "bom código" e "bom design de software" de forma intercambiável neste artigo porque acredito que são termos amplamente equivalentes: para mim, um código limpo é a prova mais concreta de que o design de uma aplicação é sensato e sustentável.

 

Agradeço ao meu colega Chris Ford por seus comentários nos primeiros rascunhos e por nossas discussões sobre engenharia baseada em agentes.

Explore a snapshot of today's tech landscape