O Custo da “Perfeição”

English Version Cada atividade desenvolvida em uma empresa de tecnologia possui suas peculiaridades e desafios. Sem dúvidas, o overengineering é um dos medos comuns a praticamente todas as áreas do desenvolvimento de software. Na constante corrida do mercado da tecnologia, as empresas sofrem uma “pressão natural” para adotarem novas soluções. Pensar demais antes de implementar uma arquitetura mais robusta ou uma nova ferramenta inovadora — que ofereça performance superior e custos significativamente menores — pode ser decisivo para alcançar destaque ou para gerar um atraso em relação aos concorrentes. Contudo, muitas vezes esquecemos que o excesso de recursos pode ser tão prejudicial quanto a sua ausência. Gostaria de trazer uma reflexão sobre o quão custoso ou mortífero para um produto pode ser a busca pela perfeição. Acho que a maioria dos desenvolvedores vai se identificar com o ciclo abaixo: Ideia genial surge: "Nossa, isso resolveria um problema real! Ninguém fez isso ainda??" Pesquisa de mercado/concorrência: "Hm, já tem algo parecido... mas acho que dá pra fazer melhor." Escolha da stack e arquitetura: "Ok, vou usar a stack mais moderna que conheço, com clean architecture, DDD, CQRS, event sourcing, monorepo com turborepo, microserviços com gRPC, e claro, full TypeScript." Estruturação exagerada: "Antes de escrever qualquer feature, vou montar os folders certinhos, configs, pipelines, testes, linter, husky, commitizen, docker-compose, etc." Cansaço/ansiedade/overload: "Putz... só de pensar em seguir isso tudo já dá preguiça. E ainda tenho aquele outro trabalho/freela/prazo..." Abandono silencioso: “Um dia eu volto...” O repositório fica lá, com um README inacabado e três commits. E se pensarmos no impacto desse ciclo para uma empresa? No exemplo acima vemos o cenário onde o desenvolvedor decide ser dono do seu próprio negócio e por em prática uma ideia autoral, mas a única diferença entre essa realidade para uma em que esse mesmo desenvolvedor trabalha para uma empresa é o último passo, onde na maioria das vezes o projeto não é abandonado, muito pelo contrário, ele cresce. Por que o excesso pode ser pior que a falta? “A diferença entre o remédio e o veneno é a dose.” Essa frase, atribuída ao médico Paracelso, transcende a medicina e se aplica perfeitamente ao desenvolvimento de software. O sistema que você desenvolveu pode apresentar deficiências ou patologias; porém, iniciar a aplicação de todos os “remédios” disponíveis, sem compreender a real necessidade ou o porquê de cada solução, pode agravar ainda mais os problemas. Para ilustrar, veja os pontos negativos de cada extremo: Falta: Dificuldade de manutenção: A falta de organização e padronização na base de código pode transformar a busca pela causa de um bug em um verdadeiro martírio, mesmo para o próprio desenvolvedor que criou a funcionalidade. Falta de escalabilidade; Baixo nível de confiabilidade; Desempenho ineficiente; Queda no ritmo das entregas. Excesso: Dificuldade de manutenção: Complexidades desnecessárias podem tornar o código confuso e difícil de atualizar. Necessidade de um nível técnico elevado por parte do time; A soma dos problemas citados na falta, potencializados pela complexidade adicional. Causas e “Miragens Técnicas” As causas para a falta e o excesso de engenharia podem seguir caminhos opostos. Enquanto a carência de engenharia costuma estar relacionada ao débito técnico ou ao raciocínio imediatista, o excesso muitas vezes surge da busca incessante pela perfeição, combinada com um conhecimento superficial de diversas ferramentas e um raciocínio hiperprudente. Um programador com uma “maleta recheada de ferramentas” não necessariamente sabe o momento ideal para usar cada uma delas. No mundo atual, onde a informação circula rapidamente, é comum conhecer diversas soluções por meio de vídeos curtos ou cursos rápidos. Contudo, compreender superficialmente um conceito — como explicado em um vídeo de poucos minutos — não equivale a dominar sua aplicação em projetos reais. Devemos ter cuidado para não confundir “conhecer” com “dominar”. Especialmente quando estamos aprendendo um novo framework, é importante evitar as “miragens técnicas” que podem nos levar a acreditar que entendemos o suficiente para aplicar soluções complexas sem uma análise profunda. Especulações e Suas Ramificações Durante pesquisas sobre o tema, notei que muitos artigos e vídeos (irei disponibilizar alguns links no final) destacam a “especulação” como um fator comum. Essa especulação gera diversas ramificações, como: Escopo mal definido: Quanto mais vaga a definição dos requisitos, mais amplo tende a ser o código desenvolvido para “proteger” contra incertezas. Cobertura excessiva: Desenvolvedores, na tentativa de cobrir todos os cenários, podem acabar expandindo tarefas simples para problemas maiores, especialmente quando não há desafios empolgantes ou prazos cu

Apr 10, 2025 - 01:58
 0
O Custo da “Perfeição”

English Version

Cada atividade desenvolvida em uma empresa de tecnologia possui suas peculiaridades e desafios. Sem dúvidas, o overengineering é um dos medos comuns a praticamente todas as áreas do desenvolvimento de software.

Na constante corrida do mercado da tecnologia, as empresas sofrem uma “pressão natural” para adotarem novas soluções. Pensar demais antes de implementar uma arquitetura mais robusta ou uma nova ferramenta inovadora — que ofereça performance superior e custos significativamente menores — pode ser decisivo para alcançar destaque ou para gerar um atraso em relação aos concorrentes. Contudo, muitas vezes esquecemos que o excesso de recursos pode ser tão prejudicial quanto a sua ausência.

Gostaria de trazer uma reflexão sobre o quão custoso ou mortífero para um produto pode ser a busca pela perfeição. Acho que a maioria dos desenvolvedores vai se identificar com o ciclo abaixo:

  1. Ideia genial surge:

    "Nossa, isso resolveria um problema real! Ninguém fez isso ainda??"

  2. Pesquisa de mercado/concorrência:

    "Hm, já tem algo parecido... mas acho que dá pra fazer melhor."

  3. Escolha da stack e arquitetura:

    "Ok, vou usar a stack mais moderna que conheço, com clean architecture, DDD, CQRS, event sourcing, monorepo com turborepo, microserviços com gRPC, e claro, full TypeScript."

  4. Estruturação exagerada:

    "Antes de escrever qualquer feature, vou montar os folders certinhos, configs, pipelines, testes, linter, husky, commitizen, docker-compose, etc."

  5. Cansaço/ansiedade/overload:

    "Putz... só de pensar em seguir isso tudo já dá preguiça. E ainda tenho aquele outro trabalho/freela/prazo..."

  6. Abandono silencioso:

    “Um dia eu volto...”

    O repositório fica lá, com um README inacabado e três commits.

E se pensarmos no impacto desse ciclo para uma empresa? No exemplo acima vemos o cenário onde o desenvolvedor decide ser dono do seu próprio negócio e por em prática uma ideia autoral, mas a única diferença entre essa realidade para uma em que esse mesmo desenvolvedor trabalha para uma empresa é o último passo, onde na maioria das vezes o projeto não é abandonado, muito pelo contrário, ele cresce.

Por que o excesso pode ser pior que a falta?

“A diferença entre o remédio e o veneno é a dose.”

Essa frase, atribuída ao médico Paracelso, transcende a medicina e se aplica perfeitamente ao desenvolvimento de software. O sistema que você desenvolveu pode apresentar deficiências ou patologias; porém, iniciar a aplicação de todos os “remédios” disponíveis, sem compreender a real necessidade ou o porquê de cada solução, pode agravar ainda mais os problemas.

Para ilustrar, veja os pontos negativos de cada extremo:

Falta:

  • Dificuldade de manutenção: A falta de organização e padronização na base de código pode transformar a busca pela causa de um bug em um verdadeiro martírio, mesmo para o próprio desenvolvedor que criou a funcionalidade.
  • Falta de escalabilidade;
  • Baixo nível de confiabilidade;
  • Desempenho ineficiente;
  • Queda no ritmo das entregas.

Excesso:

  • Dificuldade de manutenção: Complexidades desnecessárias podem tornar o código confuso e difícil de atualizar.
  • Necessidade de um nível técnico elevado por parte do time;
  • A soma dos problemas citados na falta, potencializados pela complexidade adicional.

Causas e “Miragens Técnicas”

As causas para a falta e o excesso de engenharia podem seguir caminhos opostos. Enquanto a carência de engenharia costuma estar relacionada ao débito técnico ou ao raciocínio imediatista, o excesso muitas vezes surge da busca incessante pela perfeição, combinada com um conhecimento superficial de diversas ferramentas e um raciocínio hiperprudente.

Um programador com uma “maleta recheada de ferramentas” não necessariamente sabe o momento ideal para usar cada uma delas. No mundo atual, onde a informação circula rapidamente, é comum conhecer diversas soluções por meio de vídeos curtos ou cursos rápidos. Contudo, compreender superficialmente um conceito — como explicado em um vídeo de poucos minutos — não equivale a dominar sua aplicação em projetos reais.

Devemos ter cuidado para não confundir “conhecer” com “dominar”. Especialmente quando estamos aprendendo um novo framework, é importante evitar as “miragens técnicas” que podem nos levar a acreditar que entendemos o suficiente para aplicar soluções complexas sem uma análise profunda.

Especulações e Suas Ramificações

Durante pesquisas sobre o tema, notei que muitos artigos e vídeos (irei disponibilizar alguns links no final) destacam a “especulação” como um fator comum. Essa especulação gera diversas ramificações, como:

  • Escopo mal definido: Quanto mais vaga a definição dos requisitos, mais amplo tende a ser o código desenvolvido para “proteger” contra incertezas.
  • Cobertura excessiva: Desenvolvedores, na tentativa de cobrir todos os cenários, podem acabar expandindo tarefas simples para problemas maiores, especialmente quando não há desafios empolgantes ou prazos curtos.

Além disso, o tédio pode levar à superengenharia. Existem dois perfis profissionais comuns no desenvolvimento de software:

  1. O profissional que executa suas tarefas de forma pontual, recorrendo a hábitos que, em momentos de ociosidade, se refletem em comportamentos pouco produtivos;
  2. Aquele que está sempre em busca de aprender e aplicar novas tecnologias.

    Embora o segundo perfil seja geralmente mais produtivo, se não for desafiado com tarefas estimulantes, pode transformar uma solução simples em um problema complexo na tentativa de experimentar novidades.

Gráfico de complexidade do código com base na experiência do desenvolvedor
(Overengineering can kill your product)

Outro exemplo clássico é a otimização prematura. Preparar um sistema para absorver um alto tráfego com uma infraestrutura excessivamente complicada, antes mesmo de ter usuários, é uma armadilha. Arquiteturas mirabolantes e complexas podem não ser necessárias se seus pontos fortes não se encaixarem no contexto atual. Uma solução boa e escalável é aquela que traça uma trilha clara para o crescimento e facilita as próximas decisões, sem tentar abarcar todos os cenários possíveis.

Explorando as Consequências

Toda decisão envolve trade-offs. A complexidade crescente aumenta a curva de adaptação dos novos membros da equipe, elevando os custos. Em alguns casos, esse custo pode ser compensado no longo prazo, mas, frequentemente, a complexidade excessiva gera dependência dos desenvolvedores mais experientes e dificulta o trabalho dos menos experientes, acumulando débito técnico. Essa situação se reflete na dificuldade para debugar, na manutenção onerosa e no aumento dos custos com infraestrutura.

Fugindo um pouco da esfera do desenvolvimento de código e entrando na parte de gerenciamento de processos, no Scrum, por exemplo, apenas o ato de granularizar melhor as atividades já tem um impacto direto na regra INVEST (Independente, Negociável, Valioso, Estimável, Pequeno, Testável). Isso facilita significativamente o refinamento e permite estimativas mais precisas e realistas. Vamos a um exemplo simples:

-- Card 1 --

Nome: Desenvolver uma tela de login

Descrição:
[ ] O usuário deverá conseguir realizar o login e recuperar a senha.
-- Card 2 --

Nome: Desenvolver uma tela de login

Descrição:
[ ] Deve possuir um campo de E-mail, com validação de formato e feedback visual;
[ ] Deve possuir um campo de Senha, com validação de tamanho (mínimo de 6 caracteres) e feedback visual;
[ ] Deve haver um link “Recuperar senha”, que redireciona o usuário para uma outra página (essa página será desenvolvida posteriormente).

Ao se deparar com o Card 1, surgem imediatamente uma série de dúvidas e possibilidades na mente do desenvolvedor: o login será feito com e-mail ou username? As validações serão feitas apenas no backend ou também no frontend? Quais as regras exatas de validação? Quanto tempo levaria para desenvolver a página de recuperação de senha? Enviaremos o link por e-mail ou SMS? Talvez seja melhor criar algo mais flexível desde já para facilitar futuras alterações...

Esse cenário tende a resultar em estimativas infladas, maior esforço para entrega, risco elevado de fuga do escopo, maior probabilidade de adicionar funcionalidades desnecessárias e, claro, overengineering. A sensação é semelhante àquelas cenas clássicas de desenhos animados, onde a sombra na parede parece ser de um monstro assustador — mas, ao acender a luz, percebemos que era apenas um bichinho de pelúcia.

Esse efeito psicológico impacta diretamente a pró-atividade da equipe e aumenta os níveis de estresse, comprometendo — e muito — a adesão aos valores do Scrum: foco, coragem, comprometimento, respeito e abertura.

Como Evitar o Overengineering?

O principal caminho para evitar o overengineering é questionar o porquê de cada decisão, mantendo o foco na resolução de problemas reais. Algumas diretrizes que ajudam nesse processo são:

  • Keep It Simple, Stupid (KISS): Busque sempre a solução mais simples que atenda aos requisitos do problema. Isso garante um código mais legível, com menor necessidade de um alto nível técnico para manutenção e entendimento.
  • You Ain’t Gonna Need It (YAGNI): Se não há uma demanda concreta para determinada funcionalidade, não a implemente. Soluções para problemas imaginários geram código desnecessário e desperdício de tempo. Estudos, como o realizado pela Pennsylvania State University, mostram que aproximadamente 91% dos problemas especulados não se concretizam.

Existem algumas formas mais específicas de se evitar, dependendo do seu papel no ciclo de desenvolvimento. No caso de uma liderança, converse com sua equipe e esteja aberto a novas perspectivas. Se, ao apresentar uma solução, você se vê explicando demasiadamente seus fundamentos, talvez seja um sinal de que a solução está sendo mais complexa do que o necessário. Foque na otimização das partes com maior impacto no produto — aquele 1% do código que pode potencialmente otimizar 90% dos resultados, em vez de investir esforços em áreas de menor relevância.

Nas funções de gerência, buscar ser o mais específico possível ao repassar as demandas tende a criar “guard rails” para o time de desenvolvimento, evitando que o desenvolvedor tenha que pensar demasiadamente sobre os possíveis problemas/cenários que a essa nova solução envolve.

Conclusão

Este texto não foi criado para justificar divergências de opinião sobre arquiteturas ou ferramentas em projetos colaborativos, mas para enfatizar a importância de compreender os impactos de cada decisão. O equilíbrio é crucial para um desenvolvimento saudável. Busque conhecimento, experiências, teste e treine; porém, sempre lembrando de que um bom marinheiro não usa sua experiência para enfrentar mais tempestades, mas sim para evitá-las.

Compartilhe suas experiências, revise seus projetos com essa perspectiva e mantenha o foco na criação de valor real através de soluções simples e eficazes.

Links e Referências