O que é um CÓDIGO BOM e um CÓDIGO RUIM e o que diferencia os ESTILOS DE CODIFICAÇÃO

Tempo de Leitura: 18 Minutos

Antes de mais nada, sei que é um assunto polêmico, porém, de grande importância na nossa vida como Developer ou Programador, e com certeza merece muito mais discussão e muito mais conteúdo que este simples artigo, mais temos que começar a abordar os PADRÕES DE CODIFICAÇÃO por algum ponto, e quem sabe um dia isso se torne parte do aprendizado obrigatório por exemplo em faculdades e outros cursos de programação. Indiferente da linguagem de programação escolhida, algumas práticas mesmo que não obrigatórias ajudam muito na hora de depurar um código ou mesmo de uma manutenção futura, vamos ver algumas dessas práticas e discorrer um pouco sobre sua importância.

Padrões de Projeto (Design Patterns)

São soluções típicas para problemas comuns em um projeto de software. Cada padrão é como uma planta de construção que você pode customizar para resolver um problema em particular no seu sistema, ele não é um pedaço de código específico, mas um conceito usado para resolver um problema parecido com o seu, que foi encontrado e sanado dessa mesma maneira por outros programadores ou equipes. São frequentemente confundidos com algoritmos, porém, se diferenciam destes pois são mais abstratos e mais genéricos se apresentando mais como uma regra urbana por exemplo, de que as janelas devem ser voltadas para a rua e estarem entre 1,2m e 1,4 do piso, enquanto um algoritmo é mais parecido com uma lei.

Os padrões de projeto são um kit de ferramentas para soluções tentadas e testadas para problemas comuns, e diferem em si pelo seu nível de complexidade. Os padrões mais básicos ou de baixo nível são conhecidos como idiomáticos, pois geralmente se aplicam apenas à uma linguagem de programação específica. Já padrões universais ou de alto nível são padrões arquitetônicos, e desenvolvedores podem implementar em praticamente qualquer linguagem, e podem ser usados para fazer o projeto da arquitetura de toda a aplicação. Além disso, os padrões podem ser categorizados de acordo com seu propósito.

Os padrões criacionais, fornecem mecanismos de criação de objetos e estruturas que aumentam a flexibilidade e a reutilização de código. Padrões estruturais explicam como montar objetos, classes, funções em estruturas maiores, enquanto ainda mantém as estruturas flexíveis e eficientes dentro do escopo do projeto. Já padrões comportamentais cuidam da comunicação eficiente e da assinalação de responsabilidades entre partes do código e de terceiros (como APIs externas e Bibliotecas Externas).

Os padrões de projeto representam uma técnica baseada em reuso de software e permitem ao desenvolvedor experimentar inúmeros benefícios dessa tecnologia. Para alcançar esses benefícios é importante que exista uma ampla e disponível documentação sobre cada um dos padrões adotados e a conscientização dos desenvolvedores e projetistas sobre as vantagens e benefícios de seu uso.

Existem no mercado centenas de padrões globalmente conhecidos, porém, nada impede que você e sua equipe utilize estes padrões e também implemente outros específicos a sua realidade e ao seu escopo de projetos, tornando o trabalho do grupo muito mais eficiente e independente. Note que estes padrões particulares devem respeitar padrões genéricos para poderem ser mais facilmente aceitos por novos membros e outras áreas externas a sua organização.

Padrões de Codificação (Coding Standards)

Quase a metade dos erros de código liberados para softwares em produção podem ser eliminados se uma ampla utilização de ferramentas de analise forem automaticamente implementadas, mesmo sem qualquer mudança de paradigma ou do uso de linguagens específicas, reduzindo o impacto de erros e prejuízos decorrentes destes.

A definição de uma convenção formal para a implementação de código fonte simplifica e agiliza a manutenção, pois facilita a compreensão e evita ambiguidades, de modo que a manutenção possa ser feita de modo seguro, e evita que erros sejam introduzidos quando da manutenção de um código confuso.

Alguns dos principais padrões envolvem uso de regras para a nomenclatura de constantes, variáveis, funções, classes, métodos, bibliotecas, APIs, e outras estruturas internas e externas ao seu código. Padrões para a documentação do código e criação dos manuais de manutenção e de uso do software, bem como documentação de bibliotecas e APIs em diversos níveis.

Uso correto de edentação e de espaços e quebras de linha para separação de estruturas, e correta utilização das estruturas de aninhamento (parenteses, colchetes, chaves, etc), além do uso de espaçamentos para padronizar as relações de múltiplas  linhas ou definições similares. Correto uso de quebras de linha de acordo com as práticas da linguagem ou de convenções específicas de codificação. Uso correto de cabeçalhos de arquivo e blocos de comentário para funções, métodos e outras estruturas.

Em estágios intermediários de qualidade, devemos analisar a complexidade de funções, métodos e classes, reduzido ao mínimo necessário, como criar diversas funções diferentes ao invés de sobrecarregar uso destas adicionando complexidade desnecessária. Reutilização de código e de estruturas, avaliação e definição de variáveis dentro do escopo de utilização, evitando herança de estruturas desnecessárias e que possam causar conflitos com outras definições. Sempre que possível fazer uso das declarações de variáveis e tipos, mesmo que a linguagem permita uso sem essa declaração, e declara-las o mais próximo possível de sua criação e utilização para facilitar o entendimento e a leitura do código.

Os princípios deste padrão são segurança, confiabilidade, portabilidade e facilidade de compreensão. Isto envolve a definição de uma linguagem base e em seguida restringir seu uso a um subconjunto seguro, que não permite que características da linguagem sejam interpretadas diferentemente de um compilador para outro, ou mesmo entre versões diferentes, características que permitam interpretação dúbia pelos programadores ou utilizadores. Código de qualidade é portável, facilmente compreendido, claro e não ambíguo.

Regras globais de nomenclatura sempre devem preceder a utilização de regras próprias quando da criação de métodos exteriores como APIs e Bibliotecas distribuídas, você e sua organização devem possuir regras claras e estruturas que permitam que novos membros entendam claramente as regras e código fora desses escopos e definições deve ser rejeitado.

Evite ser dogmático quanto a utilização de determinadas estruturas, já vi pessoas falando que por exemplo uso de GOTO é proibido, isso não deve ser uma verdade, se a linguagem permite seu uso, é porque existem usos onde seu uso é o mais indicado, a regra seria que uso dessas estruturas e aninhamentos muito extensos por exemplo de laços ou condicionais devem ser analisados para verificação se cabe ou não um refatoramento do código ou se o uso está correto.

A regra geral neste aspecto, é que toda a equipa concorde e conheça os padrões que estão sendo usados, e não encontrar o melhor ou pior padrão universal. Como os idiomas reais, cada linguagem de programação tem suas respectivas regras, individualidades, pontuações, pausas e pontos em comum com outras linguagens. Você precisa conhecer essas peculiaridades para falar fluentemente ou no mínimo ser entendido, precisamos seguir pelo menos algumas regras básicas para que o código não vire uma língua estrangeira no meio do projeto.

Padrões de Diretórios e Estruturas

Uma definição, que muitas vezes passa despercebida, e é tão importante quanto as demais, é uso correto de permissões e de diretórios, bem como a nomenclatura de arquivos e precedência ou uso correto dos versionadores, se a regra diz que um código importado deve estar dentro de um determinado diretório, ou uso correto de nomes de arquivos, imagina ter o trabalho de 1 semana destruído porque um arquivo foi sobreposto por não estar dentro das regras corretas de versionamento.

Controles de versão como GIT permitem voltar no tempo, porém, tem outras particularidades, por mais que um commit possa ser feito por dia de trabalho, regras para este tipo de controle devem ser claras e deve haver rejeição e controle de permissão de cada usuário. Um erro comum é definir a permissão de acordo com o nível do funcionário, isso pode ser uma realidade, em algumas situações mais não é um verdadeiro em todas as situações. As permissões devem ser definidas de acordo com a necessidade de uso e não com o grau de instrução, seres humanos naturalmente são tendenciosos ao erro, e um controle mais rígido é mais seguro.

O mesmo se aplica ao uso correto de diretórios, e aos nomes de arquivos, que devem garantir, uma individualidade de acordo com sua função e/ou origem. É uma boa prática, separar os tipos de arquivo pela sua função ou grupos de funções. Lembrando que cada linguagem ou framework em algumas situações podem ter definições próprias sobre essas estruturas.

Uma constante, principalmente em sistemas onde idiomas diferentes e acentuação seja usada, são as definições de páginas de código e codificação de caracteres, algumas linguagens exitem páginas de código específicas, e as mais modernas permitem uso de diversos padrões e definições, sempre procure um padrão que atenda a todos os idiomas e a correta utilização de acentuação de acordo com o idioma e os padrões de uso de códigos e definições numéricas, data, hora, fusos horários e outros. Lembre-se que em projetos internacionais podem haver grandes diferenças e de que erros podem ocorrer devido a essas mudanças.

Padrões de Bancos de Dados e Persistência de Dados

Outro fator, que deve ser atentado, é o correto uso dos bancos de dados, e de estruturas de persistência, como nomes de tabelas, nomes de campos, uso de alias e também, de quem é o responsável pela regra de negócio, não é incomum ver um código onde parte das regras de negócio estejam definidas na codificação e outra parte no SGDB.

Uma regra é manter um padrão, definir quem vai manter o controle é uma particularidade do projeto e do perfil do software que está sendo desenvolvido e do nível de envolvimento das tecnologias envolvidas. Por exemplo, se seu sistema permite uso de diversos modelos de bancos de dados de fabricantes diferentes, o ideal é centralizar essas definições no código, sempre abstraindo as chamadas através de camadas de acesso a dados, pois nem todos os SGDBs usam as mesmas regras e estruturas.

Atualmente, grande parte dos projetos usa sistemas de bancos de dados baseados em SQL (Structured Query Language) porém, note que sistemas diferentes usam dialetos diferentes, por exemplo uma definição usada no Oracle, pode ser totalmente diferente da mesma definição usando SQL-SERVER, e ainda diferente do Firebase ou do PostGree ou MySql e MariaDB. Portanto, para definir regras neste escopo, você deve ter a ciência que é uma decisão não só técnica mais também uma decisão que vai comprometer todas as ações futuras do ciclo de vida do seu sistema.

Uso de persistência de dados, é algo inerente a maioria dos softwares, e conforme a definição pode ser necessário regras muito rígidas de acesso a dados, e arquivos, bem como controle preciso sobre o que pode ou não ser feito e por quem. Regras neste sentido, envolvem não só a equipe de desenvolvedores, como também a equipe estratégica do negócio (gestores) e também os usuários e clientes.

Um outro aspecto importante, é a definição das estruturas, de acordo com o perfil do projeto, mais ou menos especificações para as estruturas de dados devem ser definidas, atualmente, uma onde de sistemas de bases de dados não relacionais vem surgindo (noSQL) e definir onde e quando esses formatos são melhor empregados, e também, se um determinado banco de dados deve ou não atender a todas as formas normais, definidas, se atender somente a algumas e onde cada especificação está correta ou errada, tudo isso, não basta seguir os padrões internacionais e pronto, são aspectos que influenciam não só a equipe, como a performance e eficiência de todo o projeto.

A padronização da nomenclatura de estrutura de dados nada mais é do que criar os objetos de banco de dados que possuem as mesmas funções de uma maneira uniforme, onde só de olhar seu nome seja possível identificar o que é, a quem pertence e o que ele faz. Apesar de parecer algo óbvio, a padronização das estruturas de bases de dados não é uma prática entre a maioria das empresas que tem desenvolvimento próprio ou possuem sistemas feitos por encomenda.

Hoje não é difícil encontrar bases de dados geradas a partir de modelos de classes, onde os atributos descritos em cada classe se transformam em colunas de uma tabela sem nenhum tratamento, e por este motivo, algumas colunas não possuem significado. Por exemplo, observe a coluna “Nome”, este termo não é auto explicativo, ou seja, ele deixa dúvidas. Não é possível afirmar se o “Nome” é o nome do cliente, do atendente ou qualquer outra coisa.

É claro que a padronização não é essencial para que uma base de dados funcione adequadamente e nem tem obrigação de tornar os dados confiáveis, mas mostra o nível de maturidade da empresa e dos profissionais responsáveis pela administração ou criação das estruturas de dados.

Frameworks, Dependências e Código de Terceiros

Uma parte que quase sempre é esquecida por muitas empresas, é o uso de frameworks, classes e softwares de terceiros, quando transferimos ou dependemos de um recurso de terceiro, é uma boa prática abstrair o acesso ou criar pontos únicos onde possa ser alterado a forma de acesso ao código externo. Imagina a situação, onde você usa uma API pública de um determinado serviço e alguns meses depois, a empresa fornecedora desse sistema muda a forma de acesso ou os requisitos, se você tem chamadas em centenas de lugares a essa API, você tem agora uma centena de problemas em pontos diversos da sua aplicação para resolver.

A mesma situação se aplica ao uso de frameworks e geradores de código/bibliotecas externas, imagina que toda a sua aplicação seja dependente do framework X, e você tem um contrato de manutenção de uma aplicação por digamos 10 anos, depois de 1 ano, o framework foi descontinuado pelos mantenedores ou tornaram o código proprietário, como o código escrito pelo framework geralmente é extremamente complexo para manutenção, ou você adquire o framework ou passa a manter uma atualização constante dele por conta própria ou re-escreve a aplicação para estender as funcionalidades, ou continua usando algo ultrapassado e que pode apresentar falhas de segurança e outras vulnerabilidades.

Regras específicas sobre esses formatos de trabalho e de reuso de código, devem ser muito claras, e muito bem analisadas antes de serem colocadas em produção. Um ponto específico que a maioria dos usuários de software de terceiros como os frameworks acaba se esquecendo é que menos de 30% do tempo gasto com o desenvolvimento de software é empregado na escrita de código, os outros mais de 70% são empregados em atividades de planejamento, definição de dados, uso de tecnologias, levantamento de recursos e provisionamento de estrutura, partes que o framework geralmente nunca vai poder automatizar. Fora que depois do software desenvolvido e colocado em produção ainda existe todo o ciclo de manutenção e evolução do sistema e na manutenção do código, funções que geralmente são mais dispendiosas quando estamos usando um framework, além de que estes não tem uma garantia de que em versões futuras exista a retrocompatibilidade com as versões anteriores.

Existem PADRÕES UNIVERSAIS?

Não. Simplesmente não existe um padrão que sirva para tudo, o que vale nesses casos é o bom senso, e a coerência entre todos os membros da equipe. O que existe são iniciativas de determinados grupos acerca de boas práticas para uma ou para outra linguagem, pois o que é uma boa prática em uma linguagem OOP pode não ser em uma linguagem totalmente estrutural.

Na atualidade, existem centenas de IDEs e softwares que podem auxiliar na validação e uso das boas práticas para nomenclatura de variáveis, definições de todas as estruturas e funções e até mesmo auxiliar no versionamento e manutenção de códigos, porém, por mais que o ambiente de desenvolvimento consiga localizar uma definição e até auxiliar na refatoração do código a expertise do programador e as boas práticas ainda são a melhor recomendação.

Muitas iniciativas de grupos como o PSR (PHP Standards Recommendations) ou do W3C, ou do Mozzilla Foundation ou práticas comuns como as adotadas pelos projetos GNU e demais estilos de codificação estão ganhando força e se tornando como padrões que vem sendo adotados como padrão e utilizados por uma grande quantidade de desenvolvedores em todo o mundo.

Por onde começar?

Quando você está começando a implementar padrões, seja em uma equipe já formada, ou em uma equipe em formação é comum encontrar uma certa resistência por parte dos desenvolvedores, principalmente dos mais experientes ou antigos pois envolve o gosto pessoal e crenças particulares.

Não adianta tentar implementar centenas de regras da noite para o dia, pois a resistência será grande e com certeza mais desgastante do que produtiva. Adotar pequenos padrões e regras, uma etapa de cada vez, é a melhor maneira de implementar e melhorar a produtividade e a qualidade do código e dos softwares produzidos.

Padrões de nomenclatura, o mais simples de implementar

Uma das práticas mais simples de começar a implementar padrões, é o uso de regras de nomenclatura de classes, métodos, funções, variáveis, constantes, arquivos e diretórios e também é uma das práticas que melhor gera resultados. Existem algumas dezenas de padronizações estão disponíveis e amplamente documentados, que deve ser definido pela equipe de desenvolvimento antes do início do projeto, e após definido e o projeto ser iniciado, ele jamais pode ser alterado ou discutido, todos devem seguir o padrão e ponto.

Padrões amplamente utilizados, em diversas linguagens de programação são por exemplo:

  • SNAKE_CASE – que usa o simbolo ( _ ) underscore ou sublinhado para ligar as palavras, usa geralmente todas as letras minusculas para nomes de variáveis e maiúsculas para constantes, algumas variações acrescentam uma mistura de letras maiúsculas e minusculas para classes e nomes iniciados por _nome para métodos privados e nome_método para públicos entre outros. Ulgumas linguagens usam amplamente este padrão nas definições como exemplo PHP (nomes de constantes, nomes de funções entre outros), C++, ERLANG, JAVA, PERL, PROLOG, PYTHON, RUBY, R, RUST
  • camelCase – Usa a primeira parte totalmente em minuscula, geralmente definindo o tipo ou origem, e a separação das palavras é feita com uso de uma letra maiúscula, ganhou o nome de Camel pois as letras maiúsculas em determinadas fontes mono-espaçadas fica parecendo as corcovas no animal camelCaseStyle como neste exemplo. Acabou ganhando uma força com o uso desta por diversas empresas de tecnologia como exemplos “iPhone”, “eBay”, entre outros. É sem dúvida uma das mais naturais para a escrita e também a mais fácil de ler, tanto que centenas de variações dela são usadas por dezenas de segmentos desde textos técnicos.
  • Notação Húngara Modificada – muito usada em chamadas a bibliotecas do windows, foi criada pela microsoft e quando bem empregada, é sem dúvida a melhor opção principalmente em linguagens de tipagem mais fraca e bancos de dados. Os primeiros caracteres identificam o tipo de dados, e depois o método ou nome propriamente dito, por exemplo intNomeVariavel, ou tbl_id para bancos de dados. Muitos criticam esta forma de nomenclatura, pois ela também é extensamente usada de maneira errada, e o que entra errado sai errado, por isso quando usada corretamente ajuda muito, usada errado, como a própria microsoft encorajou algum tempo depois do seu surgimento.

Outra característica que deve SEMPRE ser definida no padrão, qual idioma falado usar na nomenclatura, não é incomum ver coisas do tipo getNomeProduto e getNameProduct em definições dentro de um mesmo sistema, ou casos que chegam ao cúmulo como MyVar = MinhaVariavel pois parte dos programadores usou um nome e outra parte outro nome. Neste quesito, usar nomes no idioma nativo do software pode ser mais produtivos como padronizar uso do português, porém, casos onde exista possibilidade de internacionalização do código, nomenclatura usando o inglês universal é o mais tradicional. Um cuidado especial é idiomas onde acentuação seja empregada, por mais que os softwares permitam seu uso essa prática deve ser desencorajada devido a possibilidades de internacionalização e mesmo da falta de suporte a nomenclatura usando caracteres especiais.

A externalização do código e a abertura dos softwares pelo uso cada vez mais crescente de APIs e Bibliotecas Compartilhadas trás à tona a discussão sobre padronização, e não é incomum ver documentações de empresas de software principalmente as menos maduras onde as chamadas a métodos não segue um padrão bem definido. Basta ler algumas linhas de código em plataformas como GitHub, principalmente de APIs de uso geral, que você vai ver o quanto ainda é comum a falta de padronização, e o quanto é frágil as definições de padrões na maioria das software houses.

Documentação do Código, uma discussão complexa!

Documentação e comentários de código mereceriam uma dezena de artigos inteiros sobre elas, pois são sem dúvida um dos pontos mais controversos. Existem vertentes de desenvolvedores que alegam que é quase proibido documentar. Outros pregam que que a documentação deve ser escrita, quando se produz, outros deixam ela para momentos posteriores.

Metodologias como o manifesto Ágil pregam que documentação não devem ser extensas, mais também não pregam que não devem ser produzidas, portanto, cabe o bom senso. Documentar um cabeçalho ou uma função/método/classe é quase uma obrigação principalmente se o uso deste não é privado, e pode ser aberto até mesmo a terceiros como o uso de uma API.

Também é desnecessário, comentar cada linha do código, a não ser que você não saiba o que está fazendo ou está usando o código como um código exemplo ou em uma aula. Quem vai ler o seu código deve ser capaz de entender o que ele faz, sem precisar de uma documentação extensa. Porém, se você desenvolveu uma função por exemplo que implementa um algoritmo complexo ou tratamento de erros e que pode ser extensa é uma boa prática criar comentários simples e rápidos, que situem um outro profissional sobre o que esta sendo feito.

Por exemplo, um software que aplica cálculos complexos, pode ter um memorial de calculo descrito nos comentários. Um método de tratamento de erros, por mais que devolva somente um código de erro numérico, pode ter uma breve descrição do significado dos retornos implementado nos comentários.

Uma função ou API que recebe por exemplo dados para um CRUD, devem ter definidos os tipos de dados permitidos para cada parâmetro documentados e os retornos e exceções também documentados para facilitar seu uso pelos demais desenvolvedores.

Porém, se você tem uma definição clara, como uma declaração de variável como por exemplo String NomeDoPaciente; seria desnecessário acrescentar um comentário do tipo //recebe o nome do paciente ou seja, documentar o óbvio é desnecessário. Outra prática que deve ser extremamente desencorajada é manter versões e partes de código comentadas, se você não sabe o que vai fazer ou como fazer, não faça. A maioria dos versionadores atuais, permite que você volte no tempo para pegar uma versão diferente do código, e poder comparar para poder desfazer ou verificar, deixar esse tipo de comentário só torna o código mais difícil de ser lido e depurado.

Uma prática que só mostra a imaturidade do profissional é inserir comentários do tipo //não sei porque essa parte do código existe, mais mantive ela ai, para manter compatibilidade.  Ou //Melhorei essa função para uma versão mais rápida ou qualquer outro comentário sobre uma versão anterior, se precisar fazer um comentário deste, não use o código, você pode mandar um e-mail para o profissional ou se sente tanta necessidade de afirmar que você é bom, pode publicar nas suas redes sociais. Não existe nada pior do que ler dezenas de linhas de comentários sem sentido ou que expliquem o óbvio, ou que digam o que você fez de melhor. Se seu código é bom, não precisa divulgar, ele vai por sí mesmo se manter.

Conclusão

Boas práticas vão ser sempre boas práticas, já disse isso e continuo a repetir. Sempre que possível usar padrões, e soluções amplamente testadas, e documentadas por você e por outros, use. O que diferencia um código ruim de um código bom ou ótimo é a facilidade de entendimento deste, e sua performance.

Uma normalização das ações de uma equipe, é um processo que pode demorar, porém, sempre vai se mostrar extremamente compensador a médio e longo prazo. Indiferente da linguagem utilizada e do tamanho da equipe empregada, essas práticas vão evitar dores de cabeça.