Por que meu feito para andar e não anda está funcionando pelo avesso
Já tive um projeto que eu considerei praticamente resolvido. A estrutura estava montada, os testes passavam em 94% dos casos, e eu ia entregar na sexta-feira. Na segunda, o sistema começou a entrar em modo inconsistente. Não quebrou. Apenas parou de fazer o que foi projetado para fazer, e fez outra coisa no lugar. Isso é o que eu chamo de feito para andar e não anda. Não é um erro clássico. Erro clássico é o sistema cair, dar stack trace, voltar zero. Feito para andar e não anda é mais sutil. A interface responde. Os logs mostram sucesso. Mas o comportamento observado não corresponde ao comportamento esperado. E você gasta três dias inteira tentando entender onde está o bug, quando na verdade o bug nunca existiu. O problema estava na especificação.
O caso do serviço de agendamento
Em 2022, construí um microserviço de agendamento de consultas médicas. O requisito era simples: o paciente escolhe um horário disponível, o sistema reserva, e retorna o ID da consulta. Parece trivial. Mas na prática, o sistema reservava o horário corretamente nos logs, o banco registrava a linha, e o usuário via "consulta agendada com sucesso" na tela. O problema era que, uma vez a cada duzentas requisições, o horário era duplicado entre dois pacientes diferentes. Nenhum erro. Nenhum aviso. Apenas dois registros distintos apontando para o mesmo slot temporal. A solução não foi complexa. Adicionei uma constraint de unicidade composta no banco: (paciente_id, horario_inicio, horario_fim). Antes disso, eu havia gasto sessenta e oito horas inteiras rastreando problemas de concorrência, retry exponential, e finalmente cheguei à conclusão de que o problema estava em outro lugar. O bug não estava no código. Estava na ausência de uma regra de negócio que ninguém havia escrito.
Esse tipo de falha é comum quando a equipe entrega rápido demais. O produto funciona na maior parte do tempo. Mas existe um edge case não documentado que só aparece sob carga real ou sob combinações específicas de entrada. O sistema não quebra. Ele apenas não faz o que deveria fazer. E você acaba gastando muito mais tempo investigando do que construindo.
Como evitar que seu projeto vire feito para andar e não anda
O primeiro passo é escrever testes de propriedade, não testes de caso. Testes de caso verificam entradas específicas. Testes de propriedade verificam invariantes. Por exemplo: "após N agendamentos, o número de slots ocupados deve ser menor ou igual a N". Se esse invariant falhar, você tem um problema. Se não falhar, pode significar que o teste está mal formulado. Segundo passo: use contratos de interface explicitamente. Não confie em documentação natural da linguagem ou em comentários. Defina schemas claros, valide entradas e saídas, e rejeite qualquer coisa que não se encaixe no contrato. Isso corta muitos casos de erro silencioso.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Terceiro passo: monitore comportamento, não apenas saúde do sistema. Health check passando não significa que o sistema está fazendo o que deve fazer. Coloque métricas de Negócio: taxa de sucesso real, tempo médio até a conclusão efetiva, número de retentativas por chamada. Se esses números se desviarem da linha de base, investigue antes que o problema vire normal.
Quando desistir e mudar de abordagem
Nem todo feito para andar e não anda tem solução rápida. Às vezes, a arquitetura inicial simplesmente não suporta o domínio. Em um projeto de logística, identifiquei que o modelo de dados estava projetado para rastreamento em tempo real, mas o requisito real era histórico com análise posterior. O sistema "funcionava". Apenas não respondia à pergunta certa. Migrei para um modelo event sourcing, levei duas semanas, e o problema sumiu. Uma semana antes eu estava tentando otimizar queries que nunca seriam usadas. Alternativa viável: às vezes, o problema não é o código. É a ferramenta. Testei três frameworks diferentes para um projeto de processamento de vídeo, e o que parecia mais adequado tecnicamente tinha um gargalo de memória que impossibilitava escala. Troquei por uma abordagem diferente, usando streaming direto, e o throughput triplicou sem mudar uma linha de lógica de negócio. Ferramenta errada para o domínio mata projeto mais rápido do que bug.
Seu feito para andar e não anda pode estar em qualquer camada. Camada de apresentação, lógica de negócio, persistência, integração. O sintoma é sempre o mesmo: algo que deveria fazer X está fazendo Y, ou não fazendo nada. A diferença é que Y parece correto olhando rápido, e X só aparece quando você testa sob condições reais.
Checklist rápido antes de entregar
- Verifique invariantes de negócio. O sistema mantém as propriedades esperadas sob carga?
- Teste com dados reais. Não use dados sintéticos. Gere dados a partir de produção ou simule padrões observados.
- Monitore métricas de Negócio. Não apenas Uptime. Taxa de sucesso, latência percebida pelo usuário, número de chamadas retornando status 200 mas com conteúdo errado.
- Revise contratos. A interface exposta corresponde ao comportamento real? Muitas vezes, o contrato diz uma coisa e o código faz outra.
- Prepare um plano B. Se o sistema não funcionar como esperado, você consegue identificar rapidamente onde está o desvio?
A maioria dos projetos que viram feito para andar e não anda nasce de pressa. A equipe quer entregar, o product manager cobra prazo, e a qualidade fica para depois. O resultado é um sistema que funciona na maior parte do tempo, mas falha de forma silenciosa quando mais importa. E você gasta semanas inteiras tentando consertar algo que nunca foi corrigido na raiz. Meu conselho prático: antes de considerar o projeto entregue, teste com cenários de borda reais, não com fluxos felizes. O fluxo feliz funciona. O fluxo de borda é que define se o sistema é confiável ou apenas aparenta ser confiável.