- 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
> 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.
> "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.
ground-up rewrite) pareça atraente> 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) { ... }efor (int i = 0; i <= n; ++i) { ... }.> 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.
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