52 pontos por GN⁺ 2025-02-06 | 6 comentários | Compartilhar no WhatsApp
  • Desenvolvedores experientes compartilharam com desenvolvedores iniciantes sua filosofia de desenvolvimento de software
  • Traz conselhos sobre vários temas, como evitar reescritas completas (ground-up rewrite), gestão de cronograma, qualidade de código, automação e tratamento de casos de borda

Evite, a qualquer custo, situações em que uma reescrita completa (ground-up rewrite) pareça atraente

  • A tentação de uma reescrita completa surge justamente quando a dívida técnica acumulada já tornou difícil manter o código existente
  • É preciso identificar com antecedência os sinais de alerta quando a complexidade do código começa a se acumular — como dificuldade até para fazer mudanças simples, dificuldade para comentar/documentar e redução do número de pessoas que entendem o código central — e buscar soluções ativamente
  • Depois que a fase de expansão terminar, é indispensável passar por uma etapa de integração para reduzir a complexidade e reorganizar a qualidade
  • Se uma reescrita completa se tornar inevitável, isso significa que o projeto já entrou em uma fase de risco
  • Para reduzir esse risco, é preciso gerenciar continuamente a dívida técnica e monitorar com atenção a qualidade do código

Conclua 90% do trabalho na metade do tempo disponível

  • Escrever o código pela primeira vez e fazê-lo funcionar representa apenas cerca de metade do trabalho total
  • As etapas posteriores — tratamento de edge cases, testes, deploy, documentação, performance e manutenibilidade — exigem muito mais tempo do que costuma parecer
  • É necessário deixar buffer suficiente para lidar com problemas inesperados e com o trabalho de acabamento (polishing)
  • No fim, é preciso reconhecer a diferença entre “fazer o código funcionar de qualquer jeito no começo” e “transformá-lo em uma funcionalidade pronta” e refletir isso no cronograma

Automatize as boas práticas

  • Se você apenas repetir verbalmente ou em documentos que os desenvolvedores “devem fazer assim”, os erros tendem a acontecer com facilidade
  • Sempre que possível, é mais eficaz impor isso com testes automatizados ou scripts, em formatos como “falhar o build quando a regra for violada”
  • Mesmo regras introduzidas recentemente — como APIs proibidas ou comentários obrigatórios — podem ser automatizadas gradualmente para reduzir erros e omissões
  • Mas nem tudo pode ser automatizado, e regras excessivamente rígidas podem atrapalhar o fluxo de desenvolvimento, então é preciso equilíbrio

Considere também dados extremos (patológicos/Pathological)

  • Julgar o código apenas com entradas normais (golden path) é perigoso
  • É preciso supor situações problemáticas, como requisições atrasadas ou interrompidas, volumes enormes de dados (de milhões a centenas de milhões de linhas) e strings estranhas (muito longas ou contendo barras ou espaços)
  • Preparar-se de forma rigorosa para edge cases é o que determina a qualidade final do código

Normalmente existe uma forma mais simples de escrever

  • Depois de fazer o código funcionar inicialmente, é melhor reservar um tempo e revisitá-lo para torná-lo mais simples e claro
  • Mesmo que você já tenha encontrado uma “solução que parece boa”, é importante manter a atitude de verificar novamente se existe uma solução melhor
  • O processo de refatorar código longo e complexo para deixá-lo conciso eleva a qualidade final

Escreva código que possa ser testado

  • Se você minimizar interfaces e side effects, escrever testes automatizados se torna muito mais fácil
  • Se a estrutura é difícil de testar, há grande chance de a encapsulação não ter sido feita adequadamente
  • Se a estrutura do código for projetada de forma que os testes fluam bem na prática, será possível encontrar bugs mais cedo

O fato de o código parecer “provadamente” correto não significa que acabou

  • Mesmo um código que pareça estruturalmente seguro pode apresentar problemas se o ambiente ao redor mudar ou se parte da forma de chamá-lo for alterada
  • No caso de código relacionado à segurança, mesmo que os pontos de chamada atuais sejam seguros, é preciso projetá-lo considerando a possibilidade de mudanças futuras
  • O código deve ser escrito de modo que seja “obviamente seguro e continue sem problemas mesmo quando mudar”

Comentários

  • A frase “A razão de eu não ter escrito esta carta mais curta é que não tive tempo para escrevê-la mais curta” tem Pascal como fonte
  • Sempre tome cuidado com erros off-by-one
  • Adicionar novas diretrizes à wiki
    • Se a estrutura de documentação/compartilhamento de conhecimento da empresa não estiver bem estabelecida, as informações acabam dispersas e surge confusão
    • Pode acontecer de a documentação oficial ficar desatualizada por passar por processos de aprovação, ou de existirem várias wikis ao mesmo tempo, sem que se saiba qual é o material oficial
    • Funciona bem definir uma única wiki como ponto central para todo o material e, quando houver lacunas, os próprios desenvolvedores escreverem ou preencherem isso por meio de engenharia reversa (reverse-engineering)
    • Quando a documentação está bem integrada com outros lugares (controle de versão, comentários no código etc.), fica mais fácil manter as informações atualizadas
    • Se o ambiente de testes automatizados e build for complexo ou não estiver exposto aos desenvolvedores, pode surgir a situação em que ninguém jamais executou de fato a suíte completa de testes
    • É vantajoso manter os scripts de build do Jenkins simples, no formato cd project; ./build-it, e incluir o próprio script no controle de versão
    • Compartilhar com toda a equipe o mesmo ambiente para build e testes (por exemplo, imagem de máquina virtual, configurações de build) ajuda a reduzir problemas antes que ocorram
    • É útil considerar edge cases já na fase de desenvolvimento, tratando situações como destroy_object(foo) funcionar com segurança mesmo se foo for NULL, ou fazer create_object() chamar destroy_object() internamente em caso de falha
    • Em última instância, é importante fazer com que todos os desenvolvedores possam acessar facilmente e participar da documentação e do ambiente de build/testes
  • Documentação e testes automatizados: é mencionada a importância de documentos e wikis para compartilhamento de conhecimento, além da sugestão prática de que a configuração de ambientes CI/CD como Jenkins precisa poder ser compartilhada
  • Não existe ferramenta de mudança de comportamento mais eficaz do que “causar incômodo” até as pessoas decorarem o comportamento desejado
  • Assim como há quem discorde do provérbio do xadrez “quando você vir um lance que parece bom, execute-o”, o mesmo vale para programação. Há dois lados nisso.

6 comentários

 
bbulbum 2025-02-07

> Normalmente existe uma forma de escrever isso de maneira mais simples.

Acredito que pensar em uma solução mais concisa reduz a complexidade do código e das políticas.

 
kipsong133 2025-02-06

> "Concluir 90% de todo o trabalho em metade do tempo possível"

Acho que isso faz muito sentido mesmo. Em vez de implementar tudo perfeitamente de uma vez, revisar rapidamente pela segunda vez acaba reduzindo erros e também ajuda a gerenciar melhor o tempo.

 
jhj0517 2025-02-06
  1. Automatize boas práticas (código de teste, pipeline de CI/CD)
  2. É melhor primeiro fazer o código funcionar e depois, com alguma folga de tempo, revisitá-lo para torná-lo mais simples e claro
  3. Evite a qualquer custo situações em que uma reescrita completa (ground-up rewrite) pareça atraente
 
winterjung 2025-02-06

> Sempre tome cuidado com erros de off-by-one

Eu não sabia exatamente o que era um erro de off-by-one, mas é um tipo de erro relacionado a condições de limite, como for (int i = 1; i < n; ++i) { ... } e for (int i = 0; i <= n; ++i) { ... }.

 
ethanhur 2025-02-06

> Evite situações em que uma reescrita completa (ground-up rewrite) pareça atraente, custe o que custar.

Parece ser uma armadilha que impede muitas empresas de TI de fazer o negócio evoluir, e quando há muito débito técnico acumulado, organizações de engenharia que não conseguem resolvê-lo (ou não querem resolvê-lo) parecem considerar uma reescrita completa algo atraente.

Concordo profundamente com a ideia dos autores de que, a menos que haja um motivo realmente convincente, reescritas devem ser evitadas ao máximo.

 
GN⁺ 2025-02-06
Opiniões no Hacker News
  • O desenvolvimento de software é um processo iterativo de tentativa e aprendizado. Desenvolvedores experientes aprofundam o entendimento escrevendo código e testando, aprendendo bastante, mas isso muitas vezes não aparece bem em reuniões ou no planejamento. Desenvolvedores juniores têm dificuldade para entregar código pronto para produção e relutam em fazer trabalho que pode ser descartado. Quando o gestor tem pouca experiência em desenvolvimento, isso pode agravar esses problemas

  • A citação "Peço desculpas por ter escrito uma carta longa, mas não tive tempo de escrever uma curta" é atribuída ao matemático e filósofo francês Blaise Pascal e transmite a ideia de que escrever de forma breve é mais difícil

  • A regra 90/50 enfatiza a importância de testes e tratamento de exceções para melhorar a qualidade do código. Configurar testes automatizados ajuda a estabelecer expectativas claras para a codebase

  • O ensino de ciência da computação muitas vezes incentiva a escrever código complexo, mas é importante escrever código fácil de ler. Isso exige bons nomes para variáveis e funções, formatação consistente e design modular

  • Um mecanismo chamado Ratchet oferece uma forma eficaz de chegar a algo melhor no futuro

  • Tentar generalizar experiência e percepção de domínio pode levar a generalizações equivocadas. Desenvolver é a arte de gerenciar complexidade, e aprender com falhas é importante

  • A citação "Os primeiros 90% do trabalho consomem 90% do tempo, e os 10% restantes consomem outros 90% do tempo" representa bem a realidade do desenvolvimento. Ao considerar tratamento de exceções, erros, usabilidade e segurança, muito trabalho inesperado se torna necessário

  • O texto de Joel Spolsky alerta para os riscos de reescrever tudo e destaca a sabedoria do DevOps

  • É preciso otimizar a legibilidade do código e reconhecer que, quanto maior o tempo até um bug ser descoberto, maior será o custo. É vantajoso preferir princípios de programação funcional e usar um sistema de tipos forte