A Injustiça Silenciosa da Engenharia de Software

Published on July 28, 2025

Você já corrigiu um bug que, no fundo, nem era um erro de código mas sim de entendimento?

A verdade é que boa parte dos problemas que enfrentamos em sistemas não nasce apenas de falhas técnicas, mas da ausência de contexto claro: regras mal definidas, decisões técnicas sem justificativa e expectativas que nunca foram comunicadas.

E sem esse contexto, até o melhor desenvolvedor escreve o comportamento errado — porque está tentando adivinhar o que o sistema deveria fazer.

Neste artigo, compartilho pontos em que a falta de contexto sabota times, gera bugs e aumenta o custo de engenharia. E, no final, como os testes podem ser nossos aliados para preservar essa clareza no tempo.

Assine gratuitamente para receber novos posts e apoiar meu trabalho.

Por que podemos chamar isso de injustiça?

Porque você assume responsabilidades sem herdar o conhecimento.

Você recebe um código sem história, um sistema sem mapa, e uma tarefa com prazo. Só que o que está em jogo ali não é apenas lógica, mas decisões de negócio — muitas vezes não documentadas, mal compreendidas ou já esquecidas.

É injusto porque o sistema cobra de você respostas técnicas para erros que são organizacionais. É injusto porque você responde por bugs que nasceram da ausência de contexto e não da falta de competência. E é ainda mais injusto porque, no final, o código quebra… mas quem quebrou mesmo foi a comunicação.

A maior parte dos bugs que surgem em produção não são apenas falhas técnicas.
São comportamentos inesperados, regras mal interpretadas, intenções perdidas no tempo. Funcionalidades que “tecnicamente funcionam”, mas não do jeito que o negócio esperava.

Esses bugs não nascem do código. Eles nascem do contexto perdido:

  • Requisitos mal compreendidos ou mal repassados

  • Falta de explicação da intenção original

  • Mudanças de regras que nunca chegaram ao time

  • Decisões técnicas tomadas sem registro do porquê

  • Comunicação truncada entre áreas

  • Falta de testes que expressem o que realmente importa

E quando você vai debugar a causa raiz, percebe:
o erro não estava na lógica — estava na lacuna entre times, intenções e entendimento. Comunicação ruim = decisões ruins!

Essa realidade é confirmada por quem estuda engenharia de software com profundidade.

📘 Em Team Topologies, Matthew Skelton e Manuel Pais mostram como times sobrecarregados cognitivamente, mal alinhados ou com fronteiras mal definidas criam sistemas que tecnicamente funcionam, mas não resolvem o problema certo. A organização dos times e sua comunicação são partes centrais da qualidade do software.

“When teams are overloaded with cognitive demands, the quality of their software suffers — not due to technical incompetence, but due to systemic overload.”
(Quando os times estão sobrecarregados cognitivamente, a qualidade do software sofre — não por incompetência técnica, mas por sobrecarga sistêmica.)

“Poorly defined team boundaries and communication paths lead to software that ‘works’, but not in a way that aligns with business needs.”
(Limites mal definidos entre os times e caminhos de comunicação confusos levam a software que “funciona”, mas não de forma alinhada com o negócio.)

📗 Já em Accelerate, Nicole Forsgren, Jez Humble e Gene Kim analisam centenas de organizações e mostram que times de alta performance passam menos tempo corrigindo bugs porque têm mais alinhamento, mais feedback e mais clareza de propósito — não porque escrevem código “mais bonito”, mas porque entendem melhor o que precisa ser construído.

“High performers spend less time fixing bugs and reworking code — not because they make fewer mistakes, but because they get faster feedback and work in better systems.”
(Times de alta performance passam menos tempo corrigindo bugs — não porque cometem menos erros, mas porque recebem feedback mais rápido e trabalham em sistemas melhores.)

“The key to software quality is not heroic debugging, but tight feedback loops and shared understanding.”
(A chave para qualidade de software não é o debugging heroico, mas ciclos de feedback rápidos e entendimento compartilhado.)

Quem já enfrentou bugs em produção, sabe:
não basta entender o código — é preciso entender o que ele deveria estar dizendo. E esse entendimento raramente vem sozinho. Ele precisa ser construído, compartilhado e protegido. Com contexto.

O código funcion, mas ninguém sabe o porquê🤷🏻‍♂️

Você abre o arquivo na IDE.
Olha aquela função que roda há anos em produção.
Não tem erro. Não tem warning. Não tem nem comentário.

Aí você vê um trecho assim:

if (cliente.vip && total > 500) {
    aplicarDesconto(0.1);
}

E pensa: “Funciona… mas por que isso existe?”

Ninguém lembra.
Não tem card no Jira explicando.
Não tem nome de commit que ajude.
E muito menos teste cobrindo esse cenário de forma clara.

Esse tipo de código é um clássico do "comportamento sem contexto".

O sistema está entregando algo, sim.
Mas ninguém consegue afirmar com segurança se isso ainda faz sentido.
Foi uma regra do marketing?
Foi um pedido pontual de um cliente importante?
É uma condição que só existia num contrato de 2020?

Você não sabe.
E o pior: você tem medo de mexer.

Então a gente entra naquele ciclo comum:

  • Deixa o if quieto — vai que é importante

  • Duplica a lógica parecida em outro lugar

  • Adiciona mais um && !cancelado no desespero

  • Escreve testes que não cobrem regras de negócio.

Em pouco tempo, temos um sistema que parece funcionar…
Mas ninguém entende direito o que ele está tentando entregar.
E quando algo quebra, o debugging vira arqueologia.

Vamos ser sinceros:

  • Quantas vezes você ou seu time já deixaram passar um comportamento porque parecia estar certo?

  • Quantas vezes você escreveu uma regra porque “o PO falou isso numa call”, sem nem confirmar?

  • Quantas vezes você implementou uma regra “temporária” que ainda está lá, 3 anos depois?

Essa é a raiz de muitos bugs difíceis: o sistema faz alguma coisa, mas ninguém sabe por que, nem se ainda deveria. E com o tempo, esses comportamentos viram dívida técnica silenciosa — que acumula, cresce, e um dia explode em produção.

Se o código não reflete uma intenção clara, ele pode até compilar.
Mas ele está quebrado em essência.

📌 E aqui vai uma provocação:

“Se você tivesse que reescrever essa lógica do zero hoje, você conseguiria explicar o motivo dela estar ali?”

Se a resposta for "não"... então você não tem código. Você tem um enigma.

Decisões técnicas sem o “porquê” são armadilhas para o futuro

Você chega num novo projeto.
Dá aquela explorada no repositório, vê a stack, os serviços.
E logo bate aquela pergunta:

“Por que a gente usa Kafka aqui?”

Silêncio.

Aí alguém arrisca:

“Acho que era uma demanda antiga. Algo sobre escalabilidade…”
“Foi decisão da squad de 2021, quando o sistema começou.”

Pronto.
Você acabou de entrar num terreno perigoso:
decisões técnicas que sobreviveram mais que o contexto que as justificava.

Essas decisões podem ser qualquer coisa:

  • Um banco NoSQL ou SQL

  • Um serviço separado que nunca escalou

  • Um event-driven que virou spaghetti assíncrono

  • Um framework customizado que só um dev sabia mexer

Elas entram com ótimas intenções.
Na época, até faziam sentido.

Mas o problema não é a tecnologia. É que o motivo original desapareceu e ninguém teve coragem (ou tempo) de revisar.

E o que acontece quando ninguém mais lembra o “porquê”?

O time se prende a escolhas antigas como se fossem imutáveis.
As decisões viram dogmas. E você se pega dizendo coisas como:

  • “A gente usa isso aqui porque... sempre foi assim.”

  • “Melhor não mexer, pode quebrar outras coisas.”

  • “Nem sei se ainda precisa disso, mas tá funcionando.”

Ou seja: o time perdeu a autonomia técnica sem perceber.

O ciclo é sempre o mesmo:

  1. Alguém tomou uma decisão técnica sem deixar claro o motivo…

  2. O tempo passou. A documentação sumiu. O time mudou…

  3. O código ficou…

  4. E agora, qualquer tentativa de mudar parece arriscada demais…

E assim nascem as arquiteturas-zumbi:
Estruturas que andam, mas não têm alma.
Que respondem, mas ninguém sabe o que alimenta.

E aqui vai o ponto mais cruel:

Essa é a injustiça silenciosa da engenharia: Você lida com escolhas que não fez, mas vai pagar o preço delas ou ter que apagar o incêndio delas.

Como escapar disso?

  • Questione decisões antigas com curiosidade, não com julgamento,

  • Documente as próximas escolhas que você fizer — o código muda, o porquê não pode sumir,

  • E principalmente: mantenha espaço no time para revisar o que virou padrão por inércia.

Porque tecnologia desatualizada é fácil de resolver. Tecnologia sem contexto é uma armadilha.

A cadeia de comunicação entre produto e engenharia está quebrada

Nem todo bug é um erro de código.

Muitos bugs nascem antes da primeira linha ser escrita — quando o requisito chega atravessado, mal explicado ou pior: não chega.

Quem nunca viveu isso?

“Fizeram uma call rápida, mas você não estava.”
“Te mandaram um print no chat com ‘o que precisa ser feito’.”
“Alguém resumiu no Jira em uma linha: ‘implementar lógica de cobrança no checkout’.”

E de repente, você — que deveria ser implementador de uma solução clara — vira intérprete de intenções vagas. Um arqueólogo tentando reconstruir o que talvez alguém tenha dito numa daily, duas semanas atrás.

É nesse momento que o primeiro bug já está no ar.
Mesmo sem deploy.
Mesmo sem código.

Porque o time já começou a trabalhar com uma lacuna de entendimento.

Vamos pensar num cenário realista:

“Temos um fluxo de compra com pagamento no boleto.

Se o cliente não pagar em até 3 dias úteis, o sistema precisa invalidar o boleto…”

Parece simples?

Não quando você começa a fazer perguntas que ninguém respondeu:

  • O que exatamente significa "invalidar"? Cancelar o pedido? Enviar um e-mail? Bloquear o acesso ao produto?

  • Considera-se o dia da compra como o primeiro dia útil ou o seguinte?

  • O que acontece se o boleto for pago no quarto dia útil — o sistema ignora? Reembolsa? Processa mesmo assim?

  • Como o sistema sabe que se passaram 3 dias úteis? E se houver um feriado no meio?

  • A contagem deve levar em conta o calendário nacional, estadual ou da sede da empresa?

  • O cliente recebe algum aviso antes de o boleto ser invalidado?

  • Se o boleto for reemitido, o prazo reinicia?

  • O estoque é reservado durante esses 3 dias? E se o produto acabar nesse meio tempo?

Essas perguntas são o contexto.
Sem elas, o dev pode até entregar algo…
Mas dificilmente vai entregar o que o negócio esperava.

Comunicação ruim quebra a cadeia de valor.

  • O PO acha que explicou

  • O dev acha que entendeu

  • O QA acha que validou

  • E o cliente final descobre o bug

Quando o ciclo se repete, o time vira executor de tickets malformados.
E cada entrega vira um campo minado.

E o que a gente pode fazer como programadores?

1. Fazer perguntas incômodas

Não é ser chato. É ser responsável.
Se algo estiver mal definido, insista. Duvide. Pergunte “e se?”.
Se alguém ficar incomodado, melhor agora do que depois da entrega errada.

2. Traduzir intenções em comportamentos

Exemplo: “avisar o cliente” → “enviar email + notificação push até X horas após falha de pagamento”

O diabo está nos detalhes. Descreva como o sistema deve reagir — não só o que ele deve “fazer”.

3. Documentar as decisões durante as conversas

Um print de um board não é briefing. Um emoji no chat não é aprovação.
Documente as regras discutidas — nem que seja em um comentário no PR ou issue. Isso cria lastro técnico.

4. Chamar o time para desenhar fluxos juntos

Fluxo de compra, pagamento, cobrança... não são coisas apenas do negócio.
São comportamentos que precisam ser bem entendidos por quem irá escrever o código.

No fim, bugs não nascem só no código.
Eles nascem no silêncio.
Na suposição.
Na pressa de dizer "beleza, entendi" sem ter entendido nada.

Sem comunicação clara, não existe engenharia. Existe tentativa e erro com deploy em produção.

Compartilhar

Documentar é repassar contexto para o ‘você do futuro’

“Ah, mas documentação ninguém lê…”
“Isso vai ficar desatualizado, nem adianta.”
“O código é autoexplicativo.”

Essa cultura de “documentação é perda de tempo” parece ágil.
Mas na prática, custa caro. Muito caro.

Porque sem documentação clara, o time inteiro vira dependente da memória das pessoas. E a memória falha. As pessoas mudam de squad. Saem da empresa. Estão de férias.

Quando o conhecimento só vive na cabeça de alguém, ele não é conhecimento. É risco.

Mas afinal, o que é uma boa documentação?

Muita gente ainda acha que documentação é um manual técnico ou um monte de comentários no código. Não é isso.

Documentar é registrar decisões e intenções.
É preservar o porquê — não só o como.

Uma boa documentação responde perguntas como:

  • O que estamos resolvendo?

  • Quais regras de negócio regem esse comportamento?

  • Por que escolhemos essa arquitetura?

  • O que acontece se... [condição específica]?

  • Quais exceções foram previstas (ou não)?

  • O que esse sistema não faz de propósito?

Exemplos práticos de documentação que fazem diferença:

  • 📄 Registros de decisão de arquitetura (ADR) → Por que escolhemos Kafka ao invés de filas simples?

  • 🧭 Mapas de jornada do usuário → Quais eventos disparam ações no backend?

  • 💼 Regras de negócio em linguagem do domínio → Exemplo: “Todo cliente com assinatura atrasada há mais de 15 dias será migrado para o plano gratuito automaticamente.”

  • 📚 Casos de uso bem descritos → Fluxos completos e variações (felizes e tristes), sem jargão técnico

E o que não é documentação?

  • ❌ Comentários no código do tipo // aplica desconto se for cliente vip

  • ❌ Issues perdidas no Jira com print de chat

  • ❌ Conversas no Slack que nunca viram texto formal

  • ❌ Diagramas soltos sem explicação de regra

Esses elementos podem ajudar, mas não são fontes oficiais de verdade.

Por que isso importa tanto?

Porque sem um lugar centralizado, confiável e acessível com as decisões de negócio, você cria um sistema que ninguém entende completamente. Nem o time técnico. Nem o time de produto. Nem o cliente.

E aí surgem problemas sérios:

  • Cada dev interpreta as regras do seu jeito

  • O suporte responde com base no que “acha”

  • O QA não sabe o que é bug ou mudança

  • O produto toma decisões sem saber o impacto

E aqui vai o ponto central:

Comentários no código não salvam ninguém.
O que salva é o contexto registrado. De forma clara, fora do código. Num repositório vivo, revisado e versionado.

Quer evoluir a cultura de documentação no seu time? Comece com isso:

  1. Crie um espaço centralizado só para decisões de negócio
    (Wiki, Notion, Confluence, Git, não importa — o que importa é ter um único lugar e não vários fragmentados)

  2. Diferencie regras técnicas de regras de negócio
    Exemplo: "verifica se o token JWT expirou" não é negócio. "Cancelamento automático em 30 dias" é.

  3. Estabeleça uma política de revisão mensal da documentação
    (Pode ser simples: "todo mês, revisamos 3 fluxos juntos").

  4. Registre exceções e limites do sistema
    Saber o que o sistema não faz é tão importante quanto saber o que ele faz.

Porque o código pode estar limpo, modular, testado…
Mas sem documentação real, o seu sistema é uma caixa preta.

E o você do futuro vai sofrer para entender o que o você do presente estava tentando resolver.

“Escrever para a máquina não é tão complexo. Escrever para humanos, especialmente os que virão depois, é engenharia de verdade.”

Contexto compartilhado é produtividade multiplicada

Uma equipe de cobrança está focada em aumentar a resiliência das operações sobre faturas — criando novas estratégias de retry, fallback e controle de status.
Enquanto isso, o time de compras avança com uma nova lógica de split de pagamento, otimizando como os valores são distribuídos entre fornecedores.
Já o time de suporte automatiza os envios de e-mails baseados em falhas de operação, para agilizar o atendimento ao cliente.

Cada time trabalha bem, dentro do seu domínio, com clareza sobre seus próprios objetivos.

Mas ninguém parou para enxergar o todo.
Que as decisões de um lado impactam o outro.
E que, sem contexto compartilhado, todo mundo está otimizando localmente — e quebrando globalmente.

Já aconteceu com você?

  • A lógica de pagamento foi alterada, e o time de suporte só descobriu quando o cliente começou a reclamar.

  • O time de cobrança criou um endpoint, mas ninguém avisou o time de integração externa.

  • Um serviço mudou o comportamento de um campo — e quebrou o parsing de um relatório em outro sistema.

Essas situações não são falha técnica.
São falhas de contexto compartilhado.

Produtividade real não é velocidade isolada. É coordenação com propósito.

Quando um time acelera sem enxergar o todo, ele pode estar:

  • Criando dependências invisíveis

  • Quebrando garantias implícitas

  • Otimizando para métricas locais enquanto prejudica a experiência global do usuário

Por que times de domínios diferentes precisam se conhecer?

Porque sistemas modernos são vividos de forma transversal pelo usuário.

→ O cliente não vê “squads”.
Ele vê uma jornada: compra, paga, recebe, troca, cancela, reclama, renova.

E se cada pedaço dessa jornada é construído por uma equipe diferente sem conversar com as outras, o que nasce é um sistema fragmentado.

O que fazer para melhorar isso?

✅ 1. Promover rotinas de alinhamento intertimes

O que é:
Criar espaços regulares onde times de diferentes domínios compartilham o que estão construindo, suas prioridades, decisões técnicas e impactos esperados.

Exemplos práticos:

  • Uma review mensal entre áreas: o time de pagamentos apresenta o novo fluxo de conciliação, e o time de contabilidade valida se a informação financeira será compatível com seus relatórios.

  • Uma demo cruzada: o time de CRM mostra uma nova tela que coleta feedback do cliente, e o time de produto sugere uma integração com alertas de churn.

  • Um fórum técnico aberto: cada time compartilha suas decisões arquiteturais recentes e escuta sugestões ou alertas de outras equipes (ex: “vocês sabiam que isso pode impactar o serviço de autenticação?”).

Por quê isso evita bugs:
Alinhamento intertimes evita que decisões locais causem efeitos colaterais inesperados no sistema global.

✅ 2. Mapear dependências de jornada

O que é:
Ao invés de olhar só para os limites dos seus serviços, visualize o fluxo do ponto de vista do usuário ou processo completo. Entenda onde seu sistema começa e onde ele impacta os outros.

Exemplo prático:
O time responsável por emissão de faturas descobre que, ao alterar o status para "cancelado", isso impacta:

  • o sistema de comissões do time comercial (que não pode pagar comissão com fatura cancelada),

  • o financeiro, que precisa gerar estornos,

  • e o e-mail de notificação, que usa um template diferente.

Se cada time olhar só pro seu pedaço, o sistema quebra nas bordas.

Dica prática:
Crie fluxogramas de ponta a ponta com post-its, Miro, ou diagramas compartilhados.
Pergunte:

  • Quem depende da minha saída?

  • De quem eu dependo pra começar?

Entender isso é essencial!

✅ 3. Criar documentação de integração entre domínios

O que é:
Documentar regras e comportamentos que afetam mais de um time em um repositório comum — e não espalhadas em wikis individuais ou no Jira de cada squad.

Exemplo prático:

Regra de expiração de tokens de pagamento:
“Após 30 minutos sem ação do usuário, o token do cartão expira e a compra é cancelada.”

Se essa regra não for visível pro time de UX (que controla timers), pro time de pagamentos (que gera o token), e pro suporte (que recebe reclamações), a confusão é inevitável.

Como fazer:
Crie uma área comum de regras interdomínio:

  • business-rules/shared-rules.md

  • Ou um espaço no Notion/Confluence com o título: “Decisões que impactam mais de um time”

E defina responsáveis por manter isso atualizado.

✅ 4. Fomentar linguagem ubíqua entre áreas

O que é:
Garantir que todos — de produto a engenharia, de suporte a QA — estejam usando os mesmos termos para se referir aos mesmos conceitos.

Exemplo prático:

  • Um time chama de "assinatura vencida", outro chama de "cancelamento por inatividade", e outro ainda chama de "cliente inativo".

  • Resultado? Três interpretações diferentes para o mesmo evento → bugs, confusão nos dashboards, e cliente impactado.

Como resolver:

  • Crie um glossário colaborativo com termos usados nos fluxos.

  • Exemplo:

Cliente Inativo → Cliente com fatura vencida há mais de 30 dias sem tentativa de pagamento.
Assinatura Pendente → Fatura gerada, mas ainda não paga.

Dica prática:
Sempre que surgir um novo termo em reunião, PR ou card: pare e pergunte

“Esse nome é claro pra todos? Isso já está no nosso glossário?”

Resumo prático:

Times produtivos não são os mais rápidos em isolar tarefas.
São os que entendem o impacto do que fazem nos outros.

Contexto compartilhado não é luxo — é pré-requisito para sistemas saudáveis e times maduros.

Deixe um comentário

Testes de unidade como documentação de comportamento observável

Eu já falei disso 300 vezes.
Mas agora, colocando em prática com disciplina, os testes deixaram de ser um fardo e se tornaram um recurso estratégico.

Quando os testes são bem escritos, eles documentam o comportamento do sistema de forma mais clara do que qualquer comentário ou diagrama.

E como diz o Vladimir Khorikov, bons testes fazem três coisas:

  1. Protegem contra regressões

  2. Dão confiança para refatorar

  3. Explicam regras de negócio para humanos

Aqui vão três pontos cruciais que estou vivenciando com resultados cada vez mais consistentes:

✅ 1. Testes com nome e descrição de negócio clara

Teste ruim:
testDiscountAppliedWhenVipAndTotalAbove500

Teste bom:
Deve_aplicar_10_por_cento_de_desconto_para_cliente_VIP_com_compra_acima_de_500_reais

Esse tipo de teste não é sobre o código.
É sobre a regra de negócio que o código deve obedecer.

Quando alguém novo entra no projeto e lê esse teste, ele entende:

  • Quem tem direito ao desconto

  • Qual a condição

  • Qual o valor

  • E principalmente: que isso é esperado pelo negócio, não apenas um detalhe técnico

💡 Dica de Khorikov: o nome do teste deve descrever o cenário de negócio, não a estrutura do código.

✅ 2. Cada teste representa um cenário de uso, não um trecho de código

Ao invés de testar o método calcularDesconto() diretamente em sua lógica interna, teste o comportamento externo visível do sistema.

Exemplo:

  • Cenário: “Cliente comum com total de 600 reais”
    → Esperado: “Não recebe desconto”

  • Cenário: “Cliente VIP com 400 reais”
    → Esperado: “Não recebe desconto”

  • Cenário: “Cliente VIP com 600 reais”
    → Esperado: “Recebe 10% de desconto”

Isso traz três ganhos imediatos:

  • 🔍 Clareza sobre as regras

  • 🔐 Segurança ao refatorar (você muda o código, mas mantém os testes)

  • 🧠 Facilidade para outras pessoas entenderem o sistema só pelos testes

💡 Dica de Khorikov: “Um teste deve validar um cenário completo de negócio, não uma combinação arbitrária de inputs.”

✅ 3. Testes que falham por comportamento, não por detalhe técnico

O bom teste não quebra por mudar uma vírgula no código, ele só quebra quando o comportamento esperado não está mais sendo respeitado.

Isso é o que o Khorikov chama de resistência à refatoração.

Quando seus testes:

  • Não estão acoplados ao framework

  • Não dependem de mocks frágeis

  • Não validam chamadas internas irrelevantes
    ... então eles se tornam confiáveis.

💥 Resultado: você pode refatorar à vontade, sem medo.
O que importa é: o sistema ainda entrega o mesmo comportamento para o mundo externo?

Testes bem escritos não são só para detectar bugs.
Eles são a melhor forma de preservar conhecimento e garantir evolução sustentável.

Eles documentam, orientam, educam, e dão confiança.
Quando bem usados, são a prova viva de que engenharia não é só código — é intenção, alinhamento e continuidade.

✅ 4. Mocks com dados realistas para testar regras de verdade

Um erro comum em testes de unidade é usar dados genéricos, sem nenhum vínculo com a realidade. Algo como:

const cliente = { tipo: 'X', total: 123 };

Parece simples. Mas também não testa nada de útil para o negócio.

Se você quer validar uma regra de desconto real, os mocks precisam refletir cenários reais com nomes, valores e condições que fazem sentido dentro do domínio.

Exemplo:

const cliente = {
  nome: 'Maria Fernandes',
  categoria: 'VIP',
  totalCompras: 650.00,
};

Assim, quando alguém lê o teste, ele não só entende a regra, mas confia que aquilo foi pensado com base em situações reais. Isso também ajuda a detectar casos limites, exceções e falhas de interpretação.

Dica: evite dados aleatórios ou mágicos como "XYZ" ou 99999. Use valores que parecem ter vindo do mundo real, porque é esse o mundo que seu código vai enfrentar em produção.

Resultado: seus testes se tornam verdadeiros simuladores de decisões de negócio.
E quanto mais realista o cenário, mais útil será o teste — como documentação, proteção e aprendizado.

Conclusão

Essa é a verdadeira injustiça da engenharia:
os bugs não nascem só de erros de digitação — nascem do vazio entre intenção e execução.

Eles surgem quando o time constrói sem entender o que está construindo.
Quando as regras ficam na cabeça de alguém.
Quando decisões se perdem no tempo.
Quando os testes não contam a história.
Quando a comunicação quebra e o conhecimento se dissolve.

E o resultado?

O sistema vira um depósito de intenções esquecidas.
A produtividade some.
A confiança evapora.
A autonomia técnica vira um pôster bonito na parede da squad.
E todo mundo começa a andar em círculos, pisando em ovos.

Porque quando falta contexto, tudo trava. Todo mundo trava.

Então fica o convite para refletir com seu time:

  • As regras estão claras para todos?

  • As decisões ficam registradas ou morrem em reuniões?

  • Os testes explicam o que realmente importa?

  • As áreas se escutam de verdade ou só jogam demandas?

Se a maioria das respostas for "não", talvez o problema não esteja no seu código.
Esteja no silêncio organizacional que cerca o código.
E nesse silêncio, bugs, falhas e retrabalho são apenas os sintomas visíveis de um problema muito mais profundo.

Obrigado por ler Chronicles of a Pragmatic Programmer! Assine gratuitamente para receber novos posts e apoiar meu trabalho.