Há algo fascinante acontecendo na engenharia de software.

Durante décadas, desenvolvemos práticas para tornar o software mais previsível, sustentável e confiável. Modularidade. Separação de responsabilidades. Contratos. Testes. Integração contínua. Revisão de código. Arquitetura.

Não porque engenheiros adoram criar processos. Bem, alguns adoram. Infelizmente.

Desenvolvemos essas práticas porque aprendemos, normalmente da forma mais dolorosa, o que acontece quando as ignoramos.

Então chegaram os LLMs, e de repente o discurso de vendas ficou muito mais conveniente.

Basta escrever um prompt.

Adicione alguns arquivos Markdown explicando como a IA deve se comportar. Talvez algumas skills. Dê ao agente acesso ao repositório e deixe-o trabalhar.

Parabéns. Agora você é uma empresa de software.

Requisitos, arquitetura, verificação, manutenção? Em algum lugar no meio. O agente resolve.

É uma quantidade impressionante de responsabilidade escondida dentro da palavra “resolve”.

Eu uso LLMs. Acredito que podemos usá-los em toda a engenharia de software, dos requisitos e design aos testes, implementação, documentação e manutenção.

O que rejeito é a ideia de que podemos colocar um prompt em uma caixa-preta, receber um sistema e tratar a confiança do agente como evidência de que a engenharia foi feita.

Especialmente quando as pessoas aprovando esse processo deveriam saber mais.

Engenharia de Software Não É Magia

Vamos começar com algo básico.

Podemos definir o que um componente deve fazer: suas entradas, saídas, estado relevante, restrições e efeitos colaterais permitidos. Podemos estabelecer contratos e verificar se uma implementação os satisfaz.

Pode haver várias formas de implementar o comportamento. Um LLM pode explorar essas alternativas e gerar candidatos.

Ótimo. Isso é útil.

Mas quem decide se o resultado é aceitável?

Quem controla os testes? Quem determina se uma dependência é permitida? Quem decide se uma verificação que falhou pode ser ignorada?

Um agente pode executar testes, investigar falhas e propor mudanças. Ele não precisa de autoridade para dispensar as condições de aceitação do próprio trabalho.

Já temos software capaz de aplicar verificações obrigatórias e transições predefinidas.

Chama-se software.

Não precisamos transformar cada decisão em uma conversa com um modelo de linguagem só porque o modelo está disponível.

Entregamos o Gabarito ao Gerador

LLMs geram saídas de forma probabilística. Isso não torna automaticamente o código incorreto, assim como o determinismo não torna um programa correto.

A engenharia humana também sempre envolveu incerteza.

O erro grave é construir um processo cujos controles dependem de o gerador decidir, consistentemente, respeitá-los.

Considere este workflow.

Você descreve uma funcionalidade. O agente interpreta os requisitos, explora o repositório, altera o código, escreve os testes, executa-os, avalia os resultados e anuncia que tudo está pronto.

Então outro LLM revisa as mudanças.

LGTM!

Maravilhoso.

O mesmo sistema que pode ter entendido o requisito de forma errada implementou esse mal-entendido e produziu testes que o confirmam. Um segundo sistema, trabalhando com o mesmo contexto incompleto, pode repetir a mesma suposição.

Duas opiniões. Um erro.

O agente pode modificar componentes não relacionados. Introduzir uma abstração porque não encontrou uma existente. Adicionar uma dependência porque gerar uma integração foi mais fácil do que entender o que já estava ali.

Ou alterar um teste que falhou para combiná-lo com sua implementação.

A implementação falhou?

Sem problema. Atualize a expectativa.

Verde outra vez.

A produtividade da engenharia nunca pareceu tão boa.

Às vezes um teste está realmente errado. Às vezes um requisito precisa mudar. O agente deve poder propor essa correção.

Mas alterar o critério de aceitação é uma decisão separada de produzir uma implementação que o satisfaça. Um processo que não consegue distinguir essas decisões tem um problema de arquitetura.

Adicionar “tenha cuidado” ao prompt não vai resolvê-lo.

Nem chamar o segundo agente de “Principal Engineer”.

Quem Testa os Testes?

As pessoas frequentemente respondem às preocupações sobre código gerado com: “Temos testes.”

Ótimo. O que eles demonstram?

Se os testes foram gerados a partir do mesmo mal-entendido da implementação, um resultado positivo nos diz que os dois concordam.

Concordância é uma conquista bastante modesta quando os dois lados copiaram o mesmo erro.

Os testes de aceitação precisam de um comportamento esperado estabelecido independentemente de qualquer implementação que o agente produza. Essas expectativas podem vir de requisitos revisados, exemplos validados ou casos de regressão conhecidos.

Também precisamos de evidências de que os testes conseguem detectar defeitos relevantes.

Suponha que o requisito diga explicitamente que uma função retorna True quando uma idade inteira é pelo menos 18:

def is_adult(age: int) -> bool:
          return age >= 18

Agora introduza um defeito:

def is_adult(age: int) -> bool:
          return age > 18

Se os testes ainda passam, eles não cobriram um limite importante.

Essa é a ideia por trás do teste de mutação: introduzir falhas e verificar se os testes as detectam.

Nenhum framework revolucionário de agentes é necessário.

Testes gerados podem ser avaliados contra exemplos aprovados, condições de limite, falhas conhecidas e implementações defeituosas representativas. Implementações corretas devem passar; as falhas relevantes devem ser detectadas.

Isso fornece evidências sobre a eficácia dos testes. Não prova que os requisitos estão completos nem que todos os defeitos foram eliminados.

E um dataset de avaliação só é uma referência independente se seus resultados esperados tiverem uma base independente. Gerar a implementação, os testes e as respostas de avaliação a partir da mesma suposição apenas dá mais papelada ao erro.

“Evals” é um termo útil. Não é um feitiço.

O agente de implementação não deve poder reescrever os critérios de aceitação aprovados porque não gostou dos resultados. Correções propostas devem seguir um caminho explícito de revisão.

Caso contrário, poderíamos economizar tempo e simplesmente perguntar ao modelo se o código funciona.

Tenho certeza de que ele dará uma resposta confiante.

Alguns Arquivos Markdown e Agora Todo Mundo É uma Empresa de Software

Outra ideia fascinante é que experiência em engenharia pode ser substituída por instruções suficientemente detalhadas.

Adicione um arquivo AGENTS.md. Crie algumas skills. Explique a arquitetura. Descreva a estratégia de testes.

E finalmente, a instrução mais importante:

Agora você é um engenheiro de software sênior.

Perfeito.

Décadas de experiência em engenharia transferidas em uma frase.

O RH vai adorar.

Instruções e documentação de contexto são úteis. Mas escrever “siga a arquitetura” não demonstra que o agente a entendeu, e escrever “modifique apenas o necessário” não demonstra que ele fez isso.

Se uma fronteira arquitetural importa, aplique as partes que podem ser verificadas. Se dependências são restritas, valide-as. Se os testes de aceitação precisam ser protegidos, execute as versões aprovadas fora do controle do agente de implementação.

Proteja também a configuração de verificação. Um teste protegido não protege muita coisa se o agente pode desativar o job que o executa.

A documentação comunica a regra. Permissões e verificações executáveis a aplicam.

Quando alguém apresenta um diretório de arquivos Markdown como substituto desses controles, a engenharia ausente não desapareceu.

Ela foi atribuída a quem descobrir o problema mais tarde.

Mas Vamos Supor que o Hype Esteja Certo

Vamos levar a sério a versão mais entusiasmada do discurso.

Qualquer pessoa pode descrever o que precisa, fornecer alguns arquivos e skills e receber um sistema confiável, sustentável e pronto para produção.

Sem conhecimento significativo de engenharia. Sem decisões arquiteturais difíceis. Sem uma carga substancial de manutenção.

Apenas prompts.

Então por que comprar software?

Por que pagar por um CRM se você pode gerar o seu? Por que pagar por software de gestão de projetos? Por que pagar por uma plataforma de analytics?

E se seu concorrente cria uma funcionalidade útil, o que impede você de gerar a mesma funcionalidade amanhã?

Se criar e manter o produto realmente se tornou tão fácil, o que exatamente continua difícil de copiar?

Antes de anunciar que todos são uma empresa de software, talvez seja bom explicar o que acontece com a proposta de valor da empresa de software fazendo o anúncio.

É claro que compradores ainda pagam por conveniência, operações, segurança, integrações, suporte, conhecimento de domínio e manutenção contínua. Geração barata de código não elimina automaticamente essas razões para comprar.

Mas então coloque esse trabalho de volta no discurso de vendas.

Ele não pode desaparecer na hora de vender criação sem esforço e reaparecer na hora de explicar por que o produto ainda custa dinheiro.

Se essas responsabilidades exigem experiência, diga isso. Explique qual trabalho ficou mais barato e qual permanece.

Gerar código e assumir a responsabilidade por um produto confiável são compromissos diferentes.

A demonstração normalmente termina antes que o segundo se torne inconveniente.

Mova-se Rápido Agora. Fique Mais Lento do que Antes.

A indústria de tecnologia tem um talento extraordinário para repetir seus erros com um vocabulário novo.

Lembra do Agile?

Entregar software funcionando com frequência. Colaborar com clientes. Responder a mudanças. Todas ideias razoáveis.

Os princípios originais também enfatizam explicitamente excelência técnica, bom design e simplicidade.

Essas partes se tornaram estranhamente fáceis de ignorar quando “ser Agile” foi usado para justificar pular o trabalho de engenharia.

Arquitetura? Descobrimos durante o sprint.

Documentação? Precisamos entregar.

Testes? Adicionamos depois.

Dívida técnica? Coloque no backlog.

E então o backlog virou um cemitério.

Por algum tempo, os atalhos podem parecer produtivos. Funcionalidades chegam. A atividade aumenta. A gestão tem números para apresentar.

Então uma mudança simples exige alterações em componentes não relacionados. Ninguém lembra por que um contorno existe. Os testes deixam de ser confiáveis. Deploys exigem mais coordenação. Cada nova funcionalidade leva mais tempo.

O time que deveria se mover mais rápido fica mais lento do que antes.

A solução proposta?

Ser mais Agile.

Mais cerimônias. Mais métricas. Talvez uma reunião para entender por que as reuniões anteriores não melhoraram a entrega.

Qualquer coisa, menos admitir que o trabalho de engenharia adiado se tornou trabalho de engenharia caro.

Isso foi uma distorção do Agile. A lição é sobre transformar uma ideia útil em permissão para parar de pensar.

Agora o mesmo atalho tem orçamento de IA.

Podemos substituir story points por pull requests gerados e chamar o dashboard de programa de transformação.

A conta da manutenção continuará pouco impressionada.

A Fábrica de Dívida Técnica

Dívida técnica não é simplesmente código ruim. Ela inclui decisões que tornam mudanças futuras mais difíceis.

Às vezes aceitar esse custo é uma escolha razoável. Mas alguém deve entender a decisão, assumir a responsabilidade por ela e contabilizar as consequências.

O que acontece quando ninguém entende as decisões tomadas?

Um agente introduz uma abstração. Outro constrói sobre ela. Um terceiro reorganiza o resultado em um framework que ninguém pediu.

Um quarto revisa as mudanças.

LGTM!

Cada mudança parece pequena e razoável. Cada uma passa pelas verificações disponíveis. Juntas, elas podem tornar o sistema mais difícil de entender e mais caro de alterar.

Nesse ponto, estamos gerando dívida técnica sem que ninguém tenha aceitado conscientemente o trade-off.

O código existe. Os testes estão verdes. Os agentes estão confiantes.

Então a produção quebra, e as pessoas responsáveis não conseguem explicar como o sistema chegou àquele estado.

Pergunte a outro LLM, imagino.

Em algum momento, alguém terá de entender o sistema o bastante para limpar a bagunça.

Provavelmente um engenheiro de software experiente.

Supondo que a empresa ainda não tenha concluído que engenheiros de software são desnecessários.

As pessoas comemorando a primeira entrega também devem contabilizar o custo da próxima mudança, do próximo incidente e da limpeza futura.

Caso contrário, “produtividade” se torna um rótulo conveniente para empurrar trabalho para o futuro de outra pessoa.

E Onde Estão os Líderes Técnicos?

Essa é a parte que considero especialmente decepcionante.

Entendo por que executivos ficam entusiasmados com demonstrações em que uma aplicação aparece em minutos. Investigar formas de melhorar a entrega e reduzir custos faz parte do trabalho deles.

Mas uma demonstração deveria iniciar uma investigação. Não encerrá-la.

Onde estão os arquitetos, principal engineers e engineering managers perguntando o que a demonstração deixou de fora?

As pessoas que passaram anos explicando por que arquitetura, testes e manutenibilidade importam deveriam conseguir explicar por que esses requisitos continuam válidos quando gerar código fica barato.

“Todo mundo está adotando” não é um argumento de engenharia.

Novo agente? Integre.

Novo benchmark? Reorganize o departamento.

Um concorrente anunciou uma estratégia de IA? Anuncie a nossa antes que alguém pergunte o que ela faz.

Quando copiar a próxima empresa se torna o processo de decisão, a liderança técnica virou um serviço de encaminhamento.

Com orçamento.

Experimente. Meça. Questione suposições. Isso faz parte do trabalho.

Mas, se você aprova um processo, deve conseguir explicar seus controles. Se afirma que a produtividade melhorou, explique a medição. Se reduz a capacidade de engenharia, explique quem assume o entendimento, a verificação e a manutenção que continuam necessários.

Quanto trabalho útil foi entregue? Quanto retrabalho veio depois? Quais defeitos escaparam? O que aconteceu com o custo de mudar o sistema?

Que evidência faria você reverter a decisão?

Se a resposta é outro benchmark do fornecedor, a avaliação ainda está faltando.

Linhas de código geradas e pull requests concluídos descrevem saída. Sozinhos, não demonstram aumento de produtividade.

Mais código também pode significar mais código para entender, proteger, operar e manter.

Contar a saída ignorando esses custos é uma contabilidade muito conveniente.

Uma organização pode comemorar uma demonstração impressionante e ainda exigir evidências antes de escalar o processo. O que ela não pode fazer de forma razoável é remover os controles, ignorar os custos posteriores e depois descrever os problemas resultantes como uma surpresa imprevisível.

Se uma decisão de liderança cria as condições para a falha, “o agente fez isso” é uma explicação extraordinariamente fraca.

O agente não aprovou o modelo operacional.

Alguém com um cargo aprovou.

Mova a Revisão de Código Uma Etapa para Cima

Há uma direção melhor disponível.

Mova mais esforço humano para especificações, contratos, decisões arquiteturais e as evidências necessárias para aceitar uma mudança.

Uma pessoa e um LLM podem definir o trabalho juntos. O modelo pode identificar ambiguidades, sugerir interfaces, explorar casos extremos e propor testes.

A pessoa revisa as definições. Estamos resolvendo o problema correto? As responsabilidades estão claras? Isso se encaixa no sistema existente? Estamos introduzindo uma complexidade que o requisito não justifica?

E, ocasionalmente:

Precisamos mesmo construir isso?

Depois que as definições relevantes são aprovadas, a implementação se torna um problema restrito. O agente gera candidatos. Verificações independentes avaliam o candidato final contra os critérios aprovados.

Cada problema recorrente encontrado em revisão humana se torna candidato a teste, restrição ou avaliação.

O objetivo deve ser eliminar o trabalho rotineiro de revisão quando verificações confiáveis podem fazê-lo, em vez de pagar outro modelo para repetir os mesmos comentários indefinidamente.

Mas uma pontuação de complexidade não decide se uma abstração pertence ao sistema. Passar em testes funcionais não demonstra que um design continuará fácil de alterar.

A aceitação automatizada deve, portanto, se aplicar a mudanças delimitadas em que as evidências disponíveis sejam suficientes. Mantenha revisão humana direcionada para propriedades importantes fora dessas verificações.

Expanda a automação conforme sua cobertura demonstrada melhore.

Declarar a revisão obsoleta antes de fazer esse trabalho é apenas outro atalho com uma apresentação melhor.

Um Workflow com Limites Reais

A estrutura básica de controle é simples:

Pessoa + LLM definem uma pequena mudança
                        |
                        v
      Revisão de requisitos, contratos, testes e restrições
                        |
                        v
      Proteção dos critérios de aceitação e da configuração de verificação
                        |
                        v
      LLM propõe uma implementação
                        |
                        v
      Verificação independente do candidato final
                        |
               +--------+--------+
               |                 |
             PASSA              FALHA
               |                 |
               v                 v
      Política de aceitação   Feedback e nova tentativa limitada
               |                 |
               v                 v
      Aceitar mudanças       Verificar o próximo candidato;
      elegíveis; revisar     escalar falhas repetidas ou
      exceções               problemas de especificação

O orquestrador aplica as transições e verificações obrigatórias. O modelo pode escolher como investigar uma falha; não pode decidir silenciosamente que a verificação que falhou deixou de importar.

Os testes aprovados e a configuração de verificação devem ser aplicados fora do controle do agente de implementação. A aceitação deve avaliar o candidato realmente entregue, não uma versão anterior ou uma mensagem dizendo que tudo passou.

Se a especificação precisa mudar, volte à definição e à revisão. Se tentativas repetidas falham, investigue em vez de executar um loop ilimitado até que alguma coisa pareça verde.

Isso pode acontecer em pequenos incrementos. Não precisamos de uma especificação congelada para um produto inteiro. Precisamos de decisões explícitas sobre a mudança em andamento e de um processo visível para revisá-las.

Deploy, observação e recuperação ainda precisam de responsáveis. Passar nas verificações de aceitação não aposenta as responsabilidades operacionais do sistema.

É claro que construir isso exige mais esforço do que digitar um prompt.

Assim como quase tudo que vale a pena manter.

O custo de preparação é visível imediatamente. O custo de ignorá-lo tem o hábito inconveniente de chegar depois da comemoração.

A Oportunidade Real É Maior que Geração de Código

Se os LLMs tornam a implementação mais barata, temos a oportunidade de investir mais esforço em entender o problema e verificar o resultado.

Podemos melhorar especificações. Gerar candidatos a testes e avaliá-los. Explorar alternativas. Manter a documentação. Investigar inconsistências arquiteturais antes de construir sobre elas.

Podemos tornar a boa engenharia mais barata e acessível.

Essa é uma ambição muito mais interessante do que encher repositórios mais rápido.

A parte difícil da engenharia de software nunca foi simplesmente digitar código. Era entender o problema, definir o comportamento, escolher limites, lidar com falhas e manter o sistema compreensível enquanto ele mudava.

Remover essas responsabilidades de uma apresentação não as remove do produto.

Use LLMs onde quer que agreguem valor. Use verificações determinísticas onde as regras relevantes puderem ser aplicadas. Mantenha as especificações explícitas, a verificação independente e a responsabilidade visível.

Deixe os modelos proporem soluções.

Não deixe o agente de implementação redefinir os critérios para aceitá-las.

Não precisamos eliminar a incerteza da geração. Precisamos controlar o que acontece com o resultado gerado.

E, antes de adotar a próxima metodologia revolucionária de desenvolvimento, pergunte se ela torna o software mais fácil de mudar ou apenas mais fácil de produzir.

Já usamos a linguagem da agilidade para justificar atalhos de engenharia. A IA oferece a esses atalhos uma linha de produção mais rápida.

Se vamos repetir o mesmo erro, pelo menos devemos parar de apresentá-lo como uma ideia nova.

Mover-se rápido é útil.

Mover-se rápido em direção a uma parede não é inovação.