As verdades de software de Joel Spolsky ainda ajudam quando a IA escreve código rápido. Saiba como manter testes, contratação e simplicidade focados na corretude.

A IA pode produzir código que parece funcionar em minutos. Isso muda o ritmo do projeto, mas não muda o que faz o software ter sucesso. As lições das “verdades de software” do Joel Spolsky nunca foram sobre velocidade de digitação. Elas tratam de julgamento, ciclos de feedback e evitar complexidade auto-infligida.
O que mudou é o custo de criar código. Você pode pedir três abordagens, cinco variações ou uma reescrita completa e obter algo de volta instantaneamente. O que não mudou é o custo de escolher a abordagem certa, verificá-la e conviver com ela por meses. O tempo economizado na escrita muitas vezes se transforma em tempo gasto decidindo o que você quis dizer, validando casos-limite e garantindo que a vitória rápida de hoje não vire o imposto de manutenção de amanhã.
Corretude, segurança e manutenibilidade ainda demandam tempo real porque dependem de prova, não de confiança. Um fluxo de login não termina quando compila. Termina quando rejeita entradas inválidas de forma confiável, lida com estados estranhos e não vaza dados. A IA pode soar certa e ainda perder um detalhe crucial, como uma verificação de permissões em um endpoint ou uma condição de corrida numa atualização de pagamento.
A IA é mais forte quando você a trata como uma máquina de rascunhos rápida. Ela brilha em boilerplate, padrões repetitivos, refatorações rápidas e em explorar opções para você comparar lado a lado. Usada corretamente, comprime a fase do “quadro em branco”.
A IA faz mais mal quando você lhe dá objetivos vagos e aceita a saída sem questionar. Os mesmos padrões de falha aparecem repetidamente: pressupostos ocultos (regras de negócio não declaradas), caminhos sem testes (tratamento de erros, retries, estados vazios), erros confiantes (código plausível que está sutilmente errado) e soluções “criativas” difíceis de explicar depois.
Se o código fica barato, o novo recurso escasso é confiança. Essas verdades importam porque protegem essa confiança: com os usuários, com os colegas e com você no futuro.
Quando a IA pode gerar uma feature em minutos, dá vontade de tratar testes como a parte lenta que você precisa eliminar. O ponto do Spolsky ainda vale: a parte lenta é onde mora a verdade. Código é fácil de produzir. Comportamento correto não é.
Uma mudança útil é tratar testes como requisitos que você pode executar. Se você não consegue descrever o comportamento esperado de forma verificável, você ainda não terminou de pensar. No trabalho assistido por IA, isso importa mais, não menos, porque o modelo pode produzir algo que parece certo mas está ligeiramente errado.
Comece testando o que mais doer se quebrar. Para a maioria dos produtos, isso é fluxos centrais (cadastro, checkout, salvar, exportar), permissões (quem pode ver, editar, excluir) e integridade de dados (sem duplicatas, totais corretos, migrações seguras). Depois cubra as bordas que costumam causar incidentes noturnos: entradas vazias, textos longos, fusos horários, retries e limites externos instáveis como pagamentos, emails e uploads de arquivos.
A IA é ótima em propor casos de teste, mas não sabe o que você realmente prometeu aos usuários. Use-a como parceiro de brainstorm: peça casos de borda faltantes, cenários de abuso e combinações de permissões. Depois faça o trabalho humano: ajuste a cobertura às suas regras reais e remova testes que só “testam a implementação” em vez do comportamento.
Faça com que falhas sejam acionáveis. Um teste que falha deve dizer o que quebrou, não mandar você numa caça ao tesouro. Mantenha testes pequenos, nomeie-os como frases e torne as mensagens de erro específicas.
Suponha que você construa um app simples de “notas de time” com ajuda de IA. Telas CRUD surgem rápido. O risco de corretude não está na UI. Está no controle de acesso e nas regras de dados: um usuário não deve ver notas de outro time, edições não devem sobrescrever mudanças mais novas, e excluir uma nota não deve deixar anexos órfãos. Testes que travam essas regras vão parecer o gargalo, mas também são sua rede de segurança.
Quando o teste é o gargalo, ele força clareza. Essa clareza é o que impede que código rápido vire bug rápido.
Uma das verdades mais duradouras é que código simples vence código esperto. A IA torna tentador aceitar abstrações sofisticadas porque elas chegam polidas e rápidas. O custo aparece depois: mais lugares para bugs se esconderem, mais arquivos para vasculhar e mais momentos de “o que isso faz mesmo?”.
Quando o código é barato, complexidade é o que você paga. Um design pequeno e sem graça é mais fácil de testar, mais fácil de mudar e mais fácil de explicar. Isso importa ainda mais quando o rascunho inicial veio de um modelo que pode soar confiante e ainda estar sutilmente errado.
Uma regra prática é manter funções, componentes e módulos pequenos o suficiente para que um colega consiga revisá-los em minutos, não horas. Se um componente React precisa de vários hooks customizados, uma máquina de estados local e uma camada genérica de “render inteligente”, pare e pergunte se você está resolvendo um problema real ou apenas aceitando arquitetura porque a IA ofereceu.
Alguns “testes de simplicidade” ajudam a resistir:
Prompts importam aqui. Se você pede “a melhor arquitetura”, frequentemente recebe uma arquitetura superdesenvolvida. Peça restrições que empurrem para menos peças móveis. Por exemplo: use a abordagem mais simples com menos arquivos; evite novas abstrações a menos que removam duplicação em três ou mais lugares; prefira código explícito em vez de helpers genéricos.
Um exemplo concreto: você pede à IA para adicionar controle de acesso baseado em função a uma página de admin. A versão esperta introduz um framework de permissões, decoradores e uma DSL de configuração. A versão simples verifica o papel do usuário num único lugar, protege rotas em outro e registra acessos negados. A versão simples é mais fácil de revisar, testar e interpretar.
Se você está construindo numa ferramenta baseada em chat como Koder.ai, simplicidade também torna snapshots e rollback mais valiosos. Mudanças pequenas e óbvias são mais fáceis de comparar, manter ou reverter.
Quando o código é fácil de produzir, a habilidade rara é escolher o que deve existir e garantir que esteja correto. O conselho antigo de “contrate grandes programadores” ainda vale, mas o trabalho muda. Você não está contratando alguém para digitar mais rápido. Está contratando alguém para julgar, refinar e defender o produto.
As pessoas mais valiosas no desenvolvimento assistido por IA tendem a compartilhar quatro traços: julgamento (o que importa), gosto técnico (o que é bom), habilidade de debugging (encontrar a causa real) e comunicação (deixar trade-offs claros). Elas conseguem pegar uma feature gerada pela IA que “mais ou menos funciona” e transformá-la em algo confiável.
Ao invés de pedir uma solução perfeita do zero, dê ao candidato um pull request gerado por IA (ou um diff colado) com alguns problemas realistas: nomes confusos, um caso-limite escondido, testes faltantes e um pequeno erro de segurança.
Peça que expliquem o que o código tenta fazer em linguagem simples, encontrem as partes de maior risco, proponham correções e adicionem (ou descrevam) testes que pegariam regressões. Se quiser um sinal mais forte, pergunte também como mudariam as instruções para que a próxima tentativa da IA fosse melhor.
Isso revela como pensam em condições reais: código imperfeito, tempo limitado e a necessidade de escolher prioridades.
A IA frequentemente soa confiante. Bons contratados são confortáveis em resistir. Eles sabem dizer não a uma feature que aumenta complexidade, não a uma mudança que enfraquece segurança e não a um deploy sem provas.
Um sinal concreto é como respondem a “Você faria merge disto?” Candidatos fortes não respondem com um palpite. Eles dão uma decisão e uma lista curta de mudanças necessárias.
Exemplo: você pede uma atualização de controle de acesso “rápida” e a IA sugere espalhar checks pelos handlers. Um candidato forte rejeita essa abordagem e propõe uma camada clara de autorização, mais testes para caminhos admin e non-admin.
Finalmente, estabeleça padrões compartilhados para que a equipe edite a saída da IA de forma consistente. Mantenha simples: uma definição de pronto, expectativas de revisão consistentes e uma linha base de testes.
Quando a IA pode gerar muito código em minutos, dá vontade de pular o pensamento e apenas iterar. Isso funciona para demos. Falha quando você precisa de correção, comportamento previsível e menos surpresas.
Um bom prompt é geralmente uma especificação curta disfarçada. Antes de pedir código, transforme o objetivo vago em alguns critérios de aceitação e não-objetivos explícitos. Isso impede que a IA (e sua equipe) expanda o escopo silenciosamente.
Mantenha a especificação pequena, porém específica. Você não está escrevendo um romance. Está definindo limites sobre:
Defina “pronto” antes da geração, não depois. “Pronto” deve ser mais do que “compila” ou “a UI parece correta.” Inclua expectativas de teste, compatibilidade retroativa e o que será monitorado após o release.
Exemplo: você quer “adicionar reset de senha.” Uma especificação mais clara poderia dizer: usuários solicitam reset por email; links expiram em 15 minutos; a mesma mensagem aparece se o email existe ou não; rate limit por IP; registrar tentativas de reset sem armazenar tokens em texto puro. Não-objetivo: não redesenhar a página de login. Agora seu prompt tem guardrails e as revisões ficam mais simples.
Mantenha um changelog leve de decisões. Um parágrafo por decisão já basta. Anote por que escolheu uma abordagem e por que rejeitou alternativas. Quando alguém perguntar “por que está assim?” duas semanas depois, você terá uma resposta.
A maior mudança com IA é que produzir código fica fácil. A parte difícil é decidir o que o código deve fazer e provar que faz.
Comece escrevendo o objetivo e as restrições em linguagem simples. Inclua o que jamais deve acontecer, o que pode ser lento e o que está fora do escopo. Uma boa restrição é testável: “Nenhum usuário deve ver dados de outro usuário”, ou “Totais devem bater com a exportação financeira centavo a centavo”.
Antes de pedir código, peça um design simples e os trade-offs. Você quer que a IA mostre seu raciocínio de um jeito que você possa julgar: o que vai armazenar, o que vai validar e o que vai logar. Se ela propuser algo esperto, pressione e peça a versão mais simples que ainda cumpra as restrições.
Um loop repetível é assim:
Aqui vai um cenário pequeno: você adiciona “status de reembolso” numa tela de pedido. A IA pode gerar a UI rápido, mas a corretude vive nas bordas. E se o reembolso for parcial? E se o provedor de pagamento reenviar um webhook? Escreva esses casos primeiro, depois implemente um pedaço (coluna no banco + validação) e verifique com testes antes de seguir.
Se você usa Koder.ai, recursos como modo de planejamento, snapshots e rollback encaixam naturalmente nesse loop: planeje primeiro, gere em fatias e capture um ponto de restauração para cada mudança significativa.
Quando a geração de código é rápida, dá vontade de tratar código como produto final. Não é. O produto é comportamento: o app faz a coisa certa, mesmo quando algo dá errado.
A IA costuma soar segura, mesmo quando está chutando. O erro é pular a parte chata: rodar testes, checar casos-limite e validar entradas reais.
Um hábito simples ajuda: antes de aceitar uma mudança, pergunte “Como sabemos que isso está correto?” Se a resposta for “parece certo”, você está apostando.
A IA adora adicionar extras: cache, retries, mais configurações, mais endpoints, uma UI mais bonita. Algumas ideias são boas, mas aumentam risco. Muitos bugs nascem de features “legais de ter” que ninguém pediu.
Mantenha um limite rígido: resolva o problema que você propôs e pare. Se uma sugestão for valiosa, capture-a como tarefa separada com testes próprios.
Um grande commit gerado por IA pode esconder uma dúzia de decisões não relacionadas. A revisão vira carimbo porque ninguém consegue segurar tudo na cabeça.
Trate a saída do chat como um rascunho. Divida em mudanças pequenas que você pode ler, rodar e reverter. Snapshots e rollback só ajudam se você os fizer em pontos sensatos.
Alguns limites simples evitam a maior parte do problema: uma feature por change set, uma migração por change set, uma área de alto risco por vez (auth, pagamentos, exclusão de dados), testes atualizados no mesmo change e uma nota clara de “como verificar”.
A IA pode reproduzir padrões dos dados de treino ou sugerir dependências que você não entende. Mesmo quando a licença está ok, o risco maior é segurança: segredos hard-coded, manejo fraco de tokens ou operações de arquivo/consulta inseguras.
Se você não consegue explicar o que um trecho faz, não mande para produção. Peça uma versão mais simples ou reescreva você mesmo.
Muitos bugs “funciona na minha máquina” são realmente bugs de dados e escala. A IA pode criar mudanças de esquema sem pensar nas linhas existentes, em tabelas grandes ou em downtime.
Um exemplo realista: o modelo adiciona uma coluna NOT NULL em uma tabela PostgreSQL e faz backfill num loop lento. Em produção, isso pode travar a tabela e quebrar a app. Considere sempre o que acontece com um milhão de linhas, uma rede lenta ou um deploy que falha no meio.
Imagine um rastreador de requisições interno: pessoas submetem pedidos, gestores aprovam ou rejeitam e o financeiro marca itens como pagos. Parece simples e, com ajuda da IA, você gera telas e endpoints rápido. O que te desacelera é a mesma verdade de sempre: as regras, não a digitação.
Comece anotando o mínimo que deve estar correto. Se você não consegue explicar em palavras simples, não dá para testar.
Uma definição enxuta de primeira versão costuma ser: campos (title, requester, department, amount, reason, status, timestamps); papéis (requester, approver, finance, admin); statuses (draft, submitted, approved, rejected, paid). Depois declare as transições que importam: somente um approver pode mover submitted para approved ou rejected; somente finance pode mover approved para paid.
Use a IA numa ordem controlada para pegar erros cedo:
submit, approve, reject, pay), não uma rota genérica de “atualize qualquer coisa”.Os testes de maior valor não são “a página carrega.” São checagens de permissão e transições de estado. Prove, por exemplo, que um requester não pode aprovar seu próprio pedido, um approver não pode marcar como pago, pedidos rejeitados não podem ser pagos e (se for regra) valores não podem ser editados após submissão.
O que demora é esclarecer casos-limite. Um approver pode mudar de ideia após rejeitar? E se dois approvers clicarem approve ao mesmo tempo? E se o financeiro precisa pagar parcialmente? A IA pode gerar código para qualquer resposta que você escolher, mas não pode escolher por você. Corretude vem de tomar essas decisões e forçar o código a obedecê-las.
A IA pode produzir muito código rápido, mas a última milha ainda é trabalho humano: provar que faz o que você quis e falhar com segurança quando não faz.
Antes de checar as caixas, escolha a menor definição de “pronto” que importa. Para uma pequena feature, isso pode ser um caminho feliz, dois caminhos de falha e uma revisão de legibilidade rápida. Para pagamentos ou auth, suba a régua.
Suponha que a IA adicionou “convidar usuários em massa” numa tela admin. O caminho feliz funciona, mas o risco real são casos-limite: emails duplicados, falhas parciais e limites de taxa. Uma decisão sólida para envio poderia ser um teste automatizado para duplicatas, uma checagem manual para mensagens em falha parcial e um plano de rollback.
Quando o código é barato, o risco muda para qualidade da decisão: o que você pediu, o que aceitou e o que enviou. A maneira mais rápida de fazer essas verdades valerem no trabalho assistido por IA é adicionar guardrails que impeçam mudanças “quase certas” de passar.
Comece com uma especificação de uma página para a próxima feature. Mantenha simples: para quem é, o que faz, o que não faz e um punhado de testes de aceitação em linguagem cotidiana. Esses testes viram sua âncora quando a IA sugerir um atalho tentador.
Um conjunto de guardrails que escala sem muito overhead de processo:
Prompts fazem parte do seu processo agora. Concorde com um estilo da casa: quais bibliotecas são permitidas, como erros são tratados, o que significa “pronto” e quais testes devem passar. Se um prompt não puder ser reutilizado por outro colega, provavelmente está muito vago.
Se preferir um modo chat-first para construir web, backend e apps mobile, Koder.ai é um exemplo de plataforma onde modo de planejamento, snapshots e exportação de código podem apoiar esses guardrails. A ferramenta acelera rascunhos, mas a disciplina é o que mantém humanos no comando da corretude.
Trate a saída da IA como um rascunho rápido, não como uma funcionalidade pronta. Comece escrevendo de 3 a 5 critérios de aceitação pass/fail, gere uma fatia pequena (um endpoint, uma tela, uma migração) e verifique com testes e tentativas de casos de falha antes de seguir adiante.
Porque os testes são onde você descobre o que o código realmente faz. A IA pode produzir lógica verossímil que deixa passar uma regra-chave (permissões, tentativas, estados limites). Testes transformam suas expectativas em algo executável, repetível e confiável.
Comece pelo que causaria mais dano se quebrar:
Cubra mais depois que os comportamentos de alto dano estiverem travados.
Peça a abordagem mais simples com restrições explícitas e delete camadas extras a menos que elas tragam benefício claro. Uma boa regra: não introduza uma nova abstração a menos que ela remova duplicação em 3+ lugares ou facilite provar a correção.
Escreva uma pequena especificação: entradas, saídas, erros, restrições e não-objetivos. Inclua exemplos concretos (requisições/respostas exemplo, casos-limite). Depois defina “pronto”: testes necessários, expectativas de compatibilidade e uma nota rápida de como verificar.
Divida. Mantenha cada conjunto de mudanças revisável em poucos minutos:
Isso transforma a revisão em algo real e evita carimbos automáticos de aprovação.
Não confie na confiança — confie na prova. Rode testes, tente entradas malformadas e verifique limites de permissão. Procure também por armadilhas comuns da IA: checks de auth faltando, construção insegura de queries, manejo fraco de tokens e silenciamento de erros.
Prefira endpoints de transição explícitos ao invés de um update genérico. Por exemplo: use submit, approve, reject, pay em vez de uma rota que atualiza qualquer campo. Depois escreva testes que reforcem quem pode executar cada transição e quais transições são proibidas.
Dê aos candidatos um diff gerado por IA com problemas realistas: nomes confusos, um teste faltando, um caso-limite e um pequeno problema de segurança. Peça para explicarem a intenção, encontrarem as partes de maior risco, proporem correções e esboçarem os testes que adicionariam.
Use recursos da ferramenta para apoiar um loop disciplinado: planeje primeiro, gere em fatias pequenas, faça snapshot antes de mudanças arriscadas e reverta se a validação falhar. Em plataformas chat-first como Koder.ai, isso combina bem com modos de planejamento, snapshots e rollback — especialmente quando mudanças tocam auth, pagamentos ou migrações.