Todos nós conhecemos a emoção de desrespeitar as regras, ou mesmo quebrá-las. Talvez seja 56 em uma zona de 55 MPH ou deixar o parquímetro expirar. Talvez seja dividir dois números sem testar para ver se o denominador é zero.
Os programadores têm uma relação estranha com as regras. Por um lado, o código é apenas uma enorme pilha de regras – regras que são aplicadas indefinidamente por obedientes portas de silício, sem medo ou favor, quase sempre sem erros induzidos por partículas alfa. Queremos que os transistores sigam essas regras perfeitamente.
Mas há outra camada de regras que não são tão sacrossantas. Ao contrário das instruções que damos às máquinas, as regras que criamos para nós mesmos são altamente flexíveis. Alguns são simplesmente estilísticos, outros são projetados para trazer consistência às nossas pilhas indisciplinadas de código. Este conjunto de regras aplica-se ao que fazemos e não à forma como as máquinas respondem.
O verdadeiro debate é se é uma boa ideia os humanos quebrarem as suas próprias regras. Não temos o direito de reinterpretá-los na hora? Talvez seja porque algumas das regras vêm de uma época diferente. Talvez algumas fossem noções incompletas desde o início. Talvez alguns parecessem uma ideia inteligente na época. Talvez alguns possam ser melhor chamados de “hábitos”.
Há alguns anos, compilei uma lista de maus hábitos de programação que amamos secretamente. No interesse de avançar na arte da programação, aqui estão mais 10 hábitos de programação tão ruins que podem ser bons.
10 maus hábitos de programação que os desenvolvedores adoram
- Codificação sem comentários
- Código lento
- Código Ramble
- Seu código antigo
- Crie seu próprio código
- Otimizando muito cedo
- Descuido
- Inconsistência
- Perseguindo sinos e assobios
- Quebrando as regras
Codificação sem comentários
É um fato bem conhecido que código não documentado é um pesadelo para entender e depurar. Nossas aulas de programação nos ensinam que escrever bons comentários é essencial. A programação alfabetizada, o estilo de programação que combina linguagem natural e código, foi inventada por Don Knuth – talvez o maior programador que já existiu. Quem somos nós para argumentar?
Mas a triste verdade é que há momentos em que os comentários pioram as coisas. Às vezes, a documentação parece ter pouco a ver com o código. Talvez a equipe de documentação more longe da equipe de codificação, em outro estado – ou na verdade, em outro estado de espírito. Talvez os programadores tenham implementado um patch crítico sem informar a equipe de documentação sobre isso, ou a equipe de documentação saiba, mas ainda não atualizou os comentários. Às vezes, os programadores nem atualizam o comentário no início de um método que alteraram. Só nos resta descobrir por conta própria.
Existem outros problemas. Talvez o comentário tenha sido escrito em uma linguagem natural que você não conhece. Talvez o conceito não pudesse ser facilmente resumido em menos de sete parágrafos e o programador estivesse em um sprint ágil. Talvez a pessoa que fez o comentário estivesse errada.
Por todas essas razões e mais algumas, alguns desenvolvedores acreditam que a melhor solução para comentários inúteis é incluir menos deles – ou nenhum. Em vez disso, eles preferem escrever funções simples e mais curtas que usam nomes de variáveis camelcase mais longos e descritivos como orientação. Na ausência de um erro no compilador, o código deve ser o reflexo mais preciso do que o computador está fazendo.
Código lento
Se você deseja que seu código seja rápido, simplifique-o. Se você quer que seja realmente rápido, torne-o complexo. Encontrar o ponto ideal para esta tarefa específica não é tão fácil.
É uma troca. Geralmente, queremos que nossos programas sejam rápidos. Mas a complexidade pode ser uma chatice se ninguém a compreender mais tarde. Portanto, se a velocidade não for essencial, pode fazer sentido escrever um código um pouco mais lento, mas também mais fácil de entender. Às vezes, mais simples e mais lento é uma escolha melhor do que superinteligente e super rápido.
Código Ramble
Um de meus colegas de trabalho adora usar todos os novos operadores inteligentes em JavaScript, como reticências. O código resultante é mais conciso, o que para eles significa mais simples e melhor. Todas as revisões de código retornam com sugestões de onde podemos reescrever o código para usar a nova sintaxe.
Alguns de meus outros colegas de trabalho não têm tanta certeza de que algo mais simples seja mais fácil de entender. A leitura do código requer a descompactação dos novos operadores, alguns dos quais podem ser usados de diversas maneiras diferentes. Compreender como o operador foi usado requer pausas e reflexão profunda, em vez da rápida leitura superficial a que estão acostumados. Ler o código se torna uma tarefa árdua.
Existem argumentos históricos que explicam por que as pessoas não gostam de códigos super rígidos. Linguagens como APL, que foram projetadas para serem incrivelmente rígidas e eficientes graças aos seus símbolos personalizados, praticamente desapareceram. Outras linguagens como Python, que evitam chaves, continuam a crescer em popularidade.
Os amantes das melhores e mais recentes abstrações continuarão a promover novos recursos concisos e a elogiar a brevidade. Eles afirmam ser modernos e modernos. Alguns outros, porém, continuarão a inserir códigos mais longos e mais legíveis na pilha; eles sabem que, no final, é mais fácil de ler.
Seu código antigo
Pessoas que projetam linguagens de programação adoram inventar abstrações inteligentes e estruturas sintáticas que facilitam a solução de certos tipos de problemas. Suas línguas estão cheias dessas abstrações, e é por isso que às vezes seus manuais têm mais de mil páginas.
Algumas pessoas acreditam que usar esses recursos é o melhor. Afinal, dizem eles, a primeira regra do poder é “use-o ou perca-o”. Não deveríamos usar cada gota de açúcar sintático descrito naquele manual de mil páginas?
No entanto, essa nem sempre é uma boa regra. Muitos recursos podem gerar confusão. Existem agora tantos truques sintáticos inteligentes que nenhum programador poderia estar familiarizado com todos eles. E por que deveríamos? De quantas maneiras precisamos testar a nulidade, digamos, ou fazer a herança funcionar em múltiplas dimensões? Um deles está certo ou é melhor que os outros? Certamente, alguns programadores da equipe encontrarão uma maneira de criar drama discutindo sobre eles e arruinando o almoço ou a reunião stand-up.
Pelo menos um conjunto de designers de linguagem decidiu limitar o conjunto de recursos. Os criadores da linguagem Go disseram que queriam construir algo que pudesse ser aprendido muito rapidamente, talvez até em um dia. Isso significava que todos os programadores da equipe poderiam ler todo o código. Menos recursos levam a menos confusão.
Crie seu próprio código
Os especialistas em eficiência gostam de dizer: “Não reinvente a roda”. Use as bibliotecas de estoque bem testadas e prontas para execução. Use o código legado que já foi comprovado.
Mas às vezes uma nova abordagem faz sentido. As bibliotecas geralmente são escritas para generalistas e casos de uso diário. Eles são carregados com testes de cintos e suspensórios para garantir que os dados sejam consistentes e que o usuário não atrapalhe o trabalho enviando parâmetros errados. Mas se você tiver um caso especial, algumas linhas de código especializado poderão ser dramaticamente mais rápidas. Ele não fará tudo o que a biblioteca pode fazer, mas fará o que você precisa na metade do tempo.
Claro, há casos em que isso pode ser perigoso. Alguns códigos são tão esotéricos e complexos, como em sistemas criptográficos, que não é uma boa ideia montá-los, mesmo que você saiba toda a matemática. Mas nas situações certas, quando a biblioteca é o grande gargalo da sua carga de trabalho, algumas funções de substituição inteligentes podem ser milagrosas.
Otimizando muito cedo
É comum que os programadores juntem algum código e justifiquem seu trabalho rápido com a velha máxima de que a otimização prematura é uma perda de tempo. A ideia é que ninguém sabe qual parte do código será o verdadeiro gargalo até que acionemos todo o sistema. Desperdiçar horas criando uma ótima função é tolice se ela for chamada apenas uma vez por ano.
Geralmente, essa é uma boa regra prática. Alguns projetos não conseguem sair da linha de partida devido ao excesso de planejamento e otimização excessiva. Mas há muitos casos em que apenas um pouco de premeditação pode fazer uma grande diferença. Às vezes, escolher estruturas de dados e esquemas errados produz uma arquitetura que não é fácil de otimizar posteriormente. Às vezes, sua estrutura foi incorporada a tantas partes do código que um pouco de refatoração inteligente simplesmente não será suficiente. Nestes casos, um pouco de otimização prematura acaba sendo a resposta certa.
Descuido
Todo mundo sabe que bons programadores olham para os dois lados antes de atravessar uma rua de mão única. Eles inserem muitas linhas extras de código que estão sempre verificando os dados duas ou três vezes antes de fazer qualquer coisa com eles. Afinal, um ponteiro nulo poderia ter entrado ali!
Infelizmente, todo esse cuidado extra pode retardar nosso código. Às vezes, por questões de desempenho, precisamos ignorar nossos instintos e apenas escrever código que não se importa tanto. Se quisermos um código que seja executado rapidamente, devemos fazer apenas o mínimo e nada mais.
Inconsistência
As pessoas geralmente gostam de ordem, e os programadores muitas vezes insistem que uma pilha de código use a mesma técnica, algoritmo ou sintaxe em todas as partes. Essa diligência facilita a vida de quem chega mais tarde e precisa entender o código.
Por outro lado, a consistência tem um custo em tempo e, por vezes, em complexidade. Corrigir as diferenças significa voltar e reescrever todo o código que seguiu o caminho errado. Só isso pode sobrecarregar o orçamento.
Um problema mais profundo surge com o relacionamento entre as diferentes seções. Alguns projetos dependem de código legado. Outros dependem de bibliotecas. Muitas não conseguem funcionar sem APIs escritas por pessoas totalmente diferentes em empresas distintas.
Suavizar as diferenças entre esses grupos muitas vezes é impossível, e muitas vezes você pode reescrever a pilha inteira para se adequar à visão mais recente. Um canto estranho do nosso cérebro anseia por ordem perfeita, mas talvez seja melhor fazer as pazes com a inconsistência.
Perseguindo sinos e assobios
Outra questão com muita consistência é que impede a inovação. Também encoraja uma espécie de adesão rígida à velha maneira de fazer as coisas.
Às vezes, adicionar novos recursos, incorporar novas bibliotecas ou integrar a pilha com novas APIs significa quebrar os padrões antigos. Sim, isso tornará a vida um pouco mais difícil para quem precisa mudar de marcha enquanto lê o código, mas esse é o preço do progresso. Também faz parte do que torna a codificação divertida.
Quebrando as regras
Para rir, perguntei ao Gemini do Google se os programadores quebraram alguma regra no processo de criação. Gemini respondeu: “Em vez de os programadores quebrarem regras específicas, é mais correto dizer que eles podem ter ultrapassado os limites de algumas práticas recomendadas ao criar grandes modelos de linguagem como eu”.
“Grandes modelos de linguagem como o meu treinam com grandes quantidades de dados, e há um elemento de “incógnita” na forma como o modelo aprende com esses dados.” disse Gêmeos. “Algumas técnicas usadas para criar grandes modelos de linguagem podem ser muito eficientes, mas pode ser difícil entender exatamente como o modelo chega às suas respostas.”
Ai está. Os LLMs sabem melhor do que nós que as antigas regras estão a mudar. Quando você pode inserir conjuntos de treinamento massivos na caixa, talvez não precise gastar tanto tempo entendendo o algoritmo. Então vá em frente e seja humano! Deixe os LLMs seguirem as regras.