1 pontos por GN⁺ 2025-08-30 | 1 comentários | Compartilhar no WhatsApp
  • Em projeto de software, escolher sempre “a coisa mais simples possível” é um conselho eficaz
  • Um excelente projeto de sistemas não parece grandioso e, na prática, resolve problemas usando apenas o mínimo de componentes
  • Ao buscar soluções simples, o princípio YAGNI (You Aren't Gonna Need It) pode servir como filosofia central de projeto, expandindo gradualmente apenas quando surgirem novos requisitos
  • Embora haja debate sobre a definição de “simplicidade”, um sistema com poucos componentes, conexões internas frouxas e estabilidade é o que mais se aproxima do simples
  • A obsessão com escalabilidade excessiva torna a base de código menos flexível, por isso um projeto simples e fiel aos requisitos reais tende a ser melhor no longo prazo

Em busca da coisa mais simples possível

Ao projetar sistemas de software, é importante fazer “a coisa mais simples possível”. Essa abordagem pode ser aplicada em quase qualquer situação, como correção de bugs, manutenção de sistemas existentes e projeto de novas arquiteturas. Muitos engenheiros sonham com um sistema “ideal”, bem organizado, quase infinitamente escalável e elegantemente distribuído, mas, na prática, é mais eficaz entender profundamente o sistema atual e então escolher a solução mais fácil.

A subvalorização da simplicidade

  • O projeto de sistemas exige capacidade de usar várias ferramentas, como servidores de aplicação, proxies, bancos de dados, caches e filas
  • Engenheiros mais juniores costumam querer criar estruturas complexas usando várias tecnologias, mas a verdadeira habilidade está justamente em eliminar o desnecessário
  • Um excelente projeto de software parece sem graça e até passa a impressão de que o problema poderia ser resolvido mais facilmente do que se imaginava
  • Por exemplo, Unicorn e a API REST do Rails são excelentes em termos de projeto porque usam estrutura mínima aproveitando recursos básicos do Unix para garantir propriedades essenciais, como isolamento, escalabilidade e recuperação

A forma de pensar para implementar com simplicidade

  • Por exemplo, ao adicionar rate limiting a uma aplicação em Golang, pode-se usar armazenamento externo como Redis, mas, se não for estritamente necessário, vale tentar primeiro uma abordagem mais simples, como contagem em memória
  • Se o método simples for suficiente, pode-se adiar a adição de infraestrutura pesada
  • Isso permite uma evolução incremental do desenvolvimento, expandindo o sistema apenas quando novos requisitos realmente aparecerem
  • Trata-se de uma abordagem que coloca o princípio YAGNI como prioridade máxima no projeto

As armadilhas da simplicidade e os limites práticos

1. O fenômeno Big Ball of Mud

  • Resolver todas as demandas imediatamente pode fazer a base de código degenerar em um “amontoado de lama (big ball of mud)” complexo e difícil de manter
  • No entanto, remendos improvisados (hack) não são simplicidade; pelo contrário, criam ainda mais complexidade para entender e manter
  • Para encontrar uma solução realmente simples, é preciso comparar muitos caminhos e de fato fazer engenharia sistemática

2. A definição de simplicidade

  • Não é fácil chegar a um consenso sobre o que é “simplicidade”
  • Em geral, um sistema simples tem poucos elementos móveis (partes em funcionamento), baixo acoplamento entre componentes e interfaces bem definidas
  • Exemplo prático: processos Unix e Unicorn têm alta simplicidade porque não compartilham memória; comparados com Puma ou Redis, possuem menor conectividade interna
  • Quando a escolha é ambígua, considera-se mais simples o lado que exige menos manutenção

3. Crítica à obsessão por escalabilidade

  • O “método simples” pode não ser adequado para tráfego em grande escala
  • Mas projetar algo previamente complexo para uma futura expansão brusca de escala geralmente é esforço inútil
  • A maior parte do código, na prática, só precisa suportar algo como 2 a 5 vezes mais tráfego; acima disso, faz sentido reagir quando o problema realmente surgir
  • Projetos excessivamente centrados em escalabilidade prejudicam a flexibilidade do código; divisões estruturais além do necessário podem até dificultar a implementação de certas funcionalidades e exigir gerenciamento transacional complexo

Conclusão

  • Com o tempo, a pessoa tende a ficar mais pessimista sobre sua capacidade de prever as necessidades futuras de um sistema
  • Na prática, já é difícil o bastante entender corretamente o estado atual do sistema, e esse é o principal problema que atrapalha um bom projeto
  • Há, em linhas gerais, duas formas de desenvolver software
    • Projetar tentando prever requisitos futuros
    • Repetir a coisa mais simples possível, sendo fiel aos requisitos atuais
  • A segunda funciona melhor na prática, e uma mentalidade centrada em YAGNI e simplicidade leva a projetos melhores no longo prazo

Apêndice: discussão no Hacker News e origem do termo

  • Em resposta à opinião de que a simplicidade arquitetural perde relevância à medida que a escala cresce, o autor argumenta que, pelo contrário, quanto maior a escala, mais importante é uma estrutura simples (em razão da complexidade das interações entre funcionalidades)
  • A expressão “Do the simplest thing that could possibly work” foi criada por Ward Cunningham e Kent Beck

1 comentários

 
GN⁺ 2025-08-30
Comentários do Hacker News
  • Acho que essa abordagem pode funcionar bem em domínios simples. Mas, mesmo depois de trabalhar muito tempo em grandes empresas de tecnologia, continuo me surpreendendo com a complexidade exigida. Até os problemas de negócio mais simples levam mais de um ano para serem resolvidos, ou quebram com frequência por causa de inúmeros casos de borda e problemas de escala. Quem vive gritando por simplicidade provavelmente nunca teve experiência em larga escala. Mesmo olhando para uma base de código de 10 anos, há coisas demais para considerar, e reescritas também falham com frequência. Como na analogia da Chesterton's Fence, é preciso ter a sabedoria de não remover algo levianamente quando você não sabe por que aquilo existe

    • Acho que isso é um mal-entendido clássico que surge porque engenheiros de software não se comunicam bem entre si. O conselho, como diz o título do artigo, é fazer "a coisa mais simples possível". Em problemas complexos, alguma complexidade é inevitável, mas o ponto é tomar cuidado para não cometer o erro de deixá-los desnecessariamente mais complexos. Não significa evitar toda complexidade, e sim ficar atento ao hábito de complicar demais

    • Como foi apontado, a complexidade pode não vir do domínio em si. Pode ser um problema gerado por um design de software ruim. Se está cheio de dependências e efeitos colaterais, é porque a separação de responsabilidades e o acoplamento não estão bem gerenciados. Refatorar fica quase impossível e, se a cultura organizacional não melhorar, durante a refatoração só vão sendo grudados novos problemas por cima. Ainda assim, só com separação de responsabilidades e composição simples já dá para resolver problemas complexos. É difícil, mas as chances de sucesso aumentam bastante quando desenvolvedores sêniores adotam firmemente essa visão

    • Mencionando que o autor é staff engineer no GitHub, acho que ele também tem experiência suficiente com sistemas em larga escala

    • Sistemas legados vivem nas bordas. Mesmo em sistemas reais, assim como em espaços multidimensionais os pontos se concentram nas superfícies de fronteira à medida que o número de dimensões aumenta, os usuários reais também acabam trabalhando perto dos limites do sistema na maior parte do tempo. No fim, o sistema antigo que já existe é justamente aquele que acomoda de forma ideal todas essas bordas

    • No meu emprego anterior, boa parte da complexidade vinha de tentativas de refatoração ou melhoria que fracassaram, foram abandonadas no meio ou ficaram inacabadas. Muitas vezes me perguntei se teríamos herdado um sistema mais simples se isso tivesse sido evitado logo no começo. Não estou dizendo que não se deve refatorar nem melhorar nada, mas acho que é preciso planejar de forma a cobrir com segurança 100% dos casos de uso, garantir orçamento e marcos, e depois assegurar que a melhoria aconteça de forma incremental

  • Eu gostaria muito que a origem da expressão "a coisa mais simples possível" tivesse sido explicitada. Esse lema era usado com frequência por Ward Cunningham, inventor do wiki, e Kent Beck quando trabalhavam juntos no fim dos anos 80. Enquanto programavam, ficavam relembrando um ao outro desse princípio, e depois isso virou um tema importante em palestras e textos. Como no exemplo da porta fechada, o artigo também menciona que algo pode parecer simples, mas essa "simplicidade" muda conforme o contexto. Ou seja, encontrar a solução simples nem sempre é simples. Havia também a consciência de que essa abordagem podia deixar dívida técnica, mas a prioridade era fazer o código funcionar primeiro. Pessoalmente, acho que esse texto também poderia ter explorado mais o lado da dívida técnica

    • Mais tarde, Kent Beck formalizou o Extreme Programming. Essa metodologia é um conjunto de práticas voltadas a permitir que um sistema simples evolua naturalmente conforme os requisitos mudam

    • Já tinha ouvido essa expressão de um colega, que a usa como lema de vida, mas não sabia que Ward Cunningham era a origem. Como a frase ficou tão difundida, talvez a maior honra seja justamente ninguém mais saber quem foi o autor original

    • Foi interessante mencionarem o wiki. Em um emprego antigo, eu gerenciava projetos com Lotus Notes, e era útil porque o sistema destacava quando um documento tinha sido alterado. No projeto seguinte, usamos só wiki, mas como não dava para ver de relance quais documentos tinham mudado, acabou ficando praticamente inútil. Era simples, mas simples demais, a ponto de perder utilidade prática

  • Ao longo da minha carreira, a maior fonte de discussão sempre foi a definição de "funciona". A frase "funcionar não significa que não está quebrado" faz sentido de forma intuitiva para quem já consertou alguma coisa na prática. Mecânicos acabam percebendo que estão remendando demais com ferramenta quebrada e decidem trocar, enquanto desenvolvedores parecem ter mais dificuldade de sentir essa necessidade de "consertar de verdade"

    • O período mais difícil da minha carreira foi numa empresa que tinha um time apaixonado por protótipos. Esse time fazia rapidamente uma prova de conceito, mostrava uma demo para executivos e já publicava aquilo. Aí os executivos passavam a achar que, se foi feito tão rápido, então já estava pronto para produção. Depois, quando o time responsável abria o código, via que faltavam segurança end-to-end, validação, tratamento de erro e várias outras coisas indispensáveis. No fim, precisávamos reconstruir tudo do zero, e os executivos entendiam isso como se o time de entrega estivesse complicando algo sem necessidade

    • Vi em algum lugar uma citação marcante: "Não basta um programa funcionar. Ele precisa funcionar pelas razões corretas". No fundo, é a mesma mensagem

    • Esse tipo de conversa é importante do ponto de vista profissional. Dá para dizer que um sistema "funciona" no sentido de produzir a saída desejada, mas ainda assim ele pode estar falhando em termos de confiabilidade ou custo

    • A definição de "funciona" no meu trabalho depende de onde meu empregador decide investir recursos, ou seja, meu tempo. Se há espaço para gastar tempo melhorando a qualidade, eu faço o melhor possível. Se, ao contrário, querem apenas bater meta ou cumprir checklist, eu trabalho de acordo com isso. Acho que o resultado sempre segue aquilo que se mede, então eu posso aconselhar, mas a decisão não é minha

  • A ironia desse tipo de conselho é que quem realmente consegue aplicá-lo bem já costuma ser um especialista experiente. Por exemplo, como saber o que é "a coisa mais simples"? Como julgar se aquilo realmente vai funcionar? Recentemente, tive um erro de tratamento de namespace XML em um importador XLSX que eu mesmo fiz, porque parti da premissa de que o Excel sempre usaria apenas o namespace padrão. Aí mais tarde chegou um arquivo com namespace diferente e tudo quebrou. Seria mais simples só remover o prefixo e seguir em frente, mas, pensando no meu eu do futuro, passei 4 horas reescrevendo o parser inteiro para ser compatível com namespaces. Fazer "a coisa mais simples" na prática não é tão simples assim, e quanto mais experiência você tem, melhor consegue julgar isso. Mas, ao mesmo tempo, quando já se tem esse nível de experiência, talvez nem se precise mais desse conselho

    • Acho que o próprio texto menciona por que esse conselho é difícil de aplicar. O ponto central é: para encontrar a solução mais simples, é preciso considerar várias abordagens e, no fim, pensar como engenheiro

    • Na empresa, temos o princípio de "não adicionar código na direção errada". Mesmo que seja uma implementação parcial, ela precisa ser feita numa direção boa para evoluir depois. É KISS, mas sem jamais permitir gambiarra

    • Eu julgo a simplicidade pelo critério de "o que é mais fácil de transferir". Um serviço único que não depende de abstrações complexas nem de infraestrutura pesada acaba sendo mais fácil de passar adiante. Quem lê entende rápido, e handoff e debugging também ficam mais fáceis. Mas, quando abstração é necessária, ela deve entrar; e quando infraestrutura é realmente necessária, ela deve ser adicionada. O mais importante é sempre pensar se, ao passar aquele código para outra pessoa, a estrutura será a mais fácil de explicar. Eu antes tentava abstrair tudo ao máximo, separar por serviços, deixar tudo baseado em configuração e quase sem código, mas na prática a transição e a transferência de contexto falhavam completamente. Hoje, só aumento a complexidade estrutural quando é realmente preciso, e mantenho como padrão uma simplicidade intuitiva. No fim, isso ajuda em onboarding, correção de bugs e na transferência real do trabalho

    • O vibecoding com IA é parecido. Quanto mais experiência e repertório você tem, mais fácil fica escolher tarefas adequadas e gerenciar os agentes

    • Acho que muita gente deixa passar que "a coisa mais simples" não significa gambiarra nem remendo apressado. Na verdade, quanto mais simples o método, mais reflexão e entendimento do sistema ele costuma exigir, e o início do texto falava justamente disso. Muita gente parece ter lido só o título e despejado as próprias frustrações

  • Em geral, quando ouço princípios ou afirmações fortes desse tipo, fico logo em alerta. Quando alguém fala sobre desenvolvimento de software como se existisse uma resposta universal, normalmente é porque não entende tanto assim. A conclusão de quem realmente tem muita experiência é que software é difícil e exige cuidado. Não existe solução mágica. É preciso mente aberta e consideração pelos outros

    • Simplicidade, ou seja, o oposto da complexidade, é quase sempre o critério principal ao comparar alternativas de design de software. O motivo é simples: no fim das contas, são pessoas que precisam planejar, concordar, implementar e manter aquilo. Só que julgar simplicidade é extremamente difícil, e não dá para confiar muito nesse julgamento da média dos engenheiros da indústria. Além disso, a própria ideia de "simplicidade" se espalhou como um bordão meio vazio, sendo usada só para rebater argumentos válidos com um "isso é mais simples". O líder técnico deveria detectar bem esse tipo de problema, mas a capacidade dos líderes está ficando cada vez menos confiável, o que torna tudo mais difícil

    • Houve uma passagem marcante no artigo. O verdadeiro mestre sabe não "adicionar", mas "fazer menos"; o iniciante se move muito e faz coisas vistosas, mas o mestre se move pouco e dá apenas o golpe decisivo

    • No começo da leitura, esse texto me irritou um pouco, mas acho que ele acerta ao mostrar que "atalhos" só adicionam outro tipo de complexidade e capturam bem a essência do aprendizado por tentativa e erro. O problema é quando esse tipo de princípio vira ordem de gestão e passa a ser aplicado em excesso: tenta-se responder de forma simplista a problemas complexos, fica difícil transferir conhecimento e, depois, sobra uma correção ainda mais complicada. Por outro lado, quando algo fica complexo demais, muitas vezes é porque a equipe ficou tempo demais presa em remendos e improvisos e, quando finalmente tenta resolver tudo, exagera para o outro lado. No fim, os desenvolvedores precisam participar ativamente das discussões para ajudar a orientar decisões sensatas desde o começo

    • Não acho que isso chegue a ser um sinal vermelho. Como complexidade desnecessária cresce todos os dias, é realmente importante haver gente defendendo a simplicidade. Designers, gerentes de produto, clientes e até arquitetos tendem instintivamente a acrescentar complexidade

    • Também entendo seu ponto, mas vale notar que, no fim, isso sempre desemboca em algo como "tudo é trade-off" ou "não existe almoço grátis". Toda generalização nasce de um acúmulo de experiência prática, então não dá para eliminá-las por completo. O problema é quando um conceito útil se transforma em exagero, boato ou quase religião, e sobra apenas discussão sobre nome de pasta

  • Trabalhando em várias empresas de startup, de Seed até Series C, construindo sistemas 0-1, absorvi um princípio: "simplicidade é robustez". Tanto em design inicial quanto em melhoria de sistemas existentes, é muito fácil cair em overengineering. As necessidades dos clientes continuam evoluindo, e mesmo que tentemos prever demandas futuras, vamos errar de qualquer jeito. A simplicidade não só reduz erros; ela também cria uma base sobre a qual é fácil mudar a estrutura depois. Dá para se preparar para possíveis X, Y e Z, mas sigo buscando construir "a coisa mais simples" para deixar espaço e flexibilidade para expansão futura. A complexidade inevitavelmente traz mais restrições, e quanto mais ela se acumula com o tempo, mais frágil a stack fica

  • Acho que é preciso tentar, em algum grau, projetar o "sistema ideal" antes de conseguir chegar à "coisa mais simples". Fazer um sistema fácil de ler e fácil de mudar exige muito know-how. Melhorias verdadeiramente "simples" também nascem de experiência e entendimento profundo

  • Isso está alinhado com a Gall's law. Segundo essa ideia, um sistema complexo que funciona de verdade sempre começou como um sistema simples e foi ganhando complexidade aos poucos. Um sistema construído como complexo desde o início tende a não funcionar bem. Por isso, é preciso construir primeiro o sistema mais simples e ir adicionando complexidade gradualmente conforme os requisitos

    • Então quando é que se para? Existe um "chefão final" da complexidade, ou algum limite crítico?
  • Concordo com a intenção do texto, mas acho que o surgimento da infraestrutura em nuvem mudou a definição de "simplicidade". Antes, o contraste era entre "algo simples, mas sem escalabilidade" e "algo complexo, mas escalável"; hoje, isso não é mais necessariamente verdade. Uma solução de rate limiting em memória pode ser simples em um único servidor, mas com apenas dois servidores ela já vira imediatamente um problema de estado distribuído. Em contrapartida, um managed service como DynamoDB ou ElastiCache pode servir como fonte única da verdade tanto para um nó só quanto para 1000, reduzindo bastante a complexidade. Pela ótica de que "sistemas simples são robustos", usar managed services pode ser mais fundamentalmente simples, porque elimina problemas recorrentes como perda de dados e gerenciamento de estado distribuído. Hoje, a definição de "a coisa mais simples" parece ter mudado para o bom uso de managed services, sem precisar operar tudo manualmente. Em vez de evitar dependências externas, agora muitas vezes faz mais sentido alavancar sistemas isolados e validados para minimizar complexidade, tempo e custo

    • Não acho que isso seja algo exclusivo da nuvem. Mesmo em um único servidor, dá para fazer a mesma discussão entre flat file, sqlite, postgres e outras opções de armazenamento. A realidade hoje é que existe uma enorme quantidade de software que entrega capacidades muito grandes com relativamente pouca complexidade adicional. Claro que toda escolha envolve trade-offs, então capacidade de julgamento continua sendo essencial. Escolher um managed service da Amazon logo de cara nem sempre é a resposta certa. Já ouvi vários casos de arrependimento por adoção de DynamoDB sem necessidade real para a escala do problema
  • Concordo com a frase: "Faça tudo o mais simples possível, mas não mais simples do que isso". Eu sempre tento seguir esse princípio, mas fico me perguntando se, por não conhecer bem as tecnologias mais novas, estou deixando passar complexidades desnecessárias, ou se, ao contrário, todo mundo está se metendo em complexidade inútil. Também é difícil aprender se algo realmente presta sem usar em projeto real; por outro lado, já vi o estrago que dá quando alguém enfia ferramentas novas em produção só para aprendizado próprio

    • Estou vivendo um pesadelo parecido. Por causa de uma liderança sempre obcecada pelo software mais novo, preciso avaliar em produção ferramentas cujo valor nem é claro. Como exigem esse tipo de exploração toda semana, independentemente de qualquer resultado concreto, isso acaba virando um peso enorme

    • Na prática, acho que o certo é primeiro fazer funcionar da forma mais simples possível, até manualmente se precisar, e só adicionar o conjunto completo de funcionalidades quando realmente chegar a hora. Mesmo que no começo se perca tempo automatizando ou criando ferramentas antes da hora, a experiência de depois entender claramente por que aquilo era necessário acaba sendo ainda mais valiosa

    • Esse sentimento me é muito familiar. Eu também sempre concordo com textos assim e tento praticar a simplicidade, mas fico sempre na dúvida se isso é mesmo sabedoria e pragmatismo, ou se é só falta de experiência e habilidade

    • É por isso que tento tomar muito cuidado para não cair em "desenvolvimento guiado por currículo"