Clean Code: Como escrever códigos de qualidade, fáceis e gostosos de ler – Blog Bugginho Academy
Uma coisa é fato, se você já trabalhou em equipes formadas por programadores inexperientes ou dando suporte a códigos desenvolvidos por outros programadores, a experiência certamente foi traumatizante. Eu sou um dos que se tiver que trabalhar com código de outras pessoas ou até mesmo meus códigos de 4 ou 5 anos atrás, já sente aquele leve frio na barriga.
Independentemente de qual linguagem, Framework ou para qual plataforma você desenvolva, escrever códigos fáceis de ler e manter é uma tarefa árdua. Então, se liga nessas dicas para escrever um código do qual você se orgulhe de mostrar aos outros 🙂
Importante: Boa parte das dicas postadas aqui foram retiradas do livro "Código Limpo – Habilidades Práticas do Agile Software", então, para se aprofundar no assunto, sugiro a leitura do mesmo.
Assim como os autores de livros de romance, nós também somos autores e todos autores têm leitores, então é sua obrigação utilizar uma linguagem clara e direta para que sua obra seja de fácil entendimento. Se seu leitor (Que pode ser você mesmo, daqui uns meses ou anos) precisar ficar rolando a página, acessando outros arquivos, etc. Para poder entender o que você estava tentando fazer naquele bloco de código, isso é um bom sinal de que você não escreveu da melhor maneira possível.
A primeira dica de como escrever um bom código é definir um idioma padrão. Não existe nada que lhe obrigue a escrever códigos em inglês, mas se você dominar o idioma, prefira escrever em inglês. Português requer acentuações e isso pode gerar alguns problemas de entendimento, já que você não pode (ou pelo menos não deve, mesmo que a linguagem permita) escrever com acentuação. Além disso, a sintaxe das linguagens já são em inglês, então é meio estranho ler código em português e inglês ao mesmo tempo. Exemplo:
void definirNome(String Nome) { // ... }
void definirNome(String Nome) {
A situação piora ainda mais quando você mesmo resolve misturar os dois idiomas.
Contrato contract = user.getContrato();
Super ruim de ler isso, não é verdade?
Importante: Optei por usar Português nos exemplos desse Post, para ficar mais acessível a quem não tem domínio de inglês, mas que com certeza estão dando atenção a isso, não é amiguinhos?
Imagine que você esteja iniciando uma nova graduação e em sua sala de aula há 45 alunos, todos com nomes muito legais como: "a", "a2", "x", "clt", "b2c3", etc. Esses nomes foram escolhidos pelos pais deles, porque estavam com preguiça de ter que escrever nomes grandes toda hora. Imagine a confusão?
int a = 10; int b = 1; if(a < b) { // ... }
Você viu o código acima? O que ele significa? O que o programador pensou ao fazer ele?
Você pode até pensar: "Pô, mas ninguém faz isso". Acredite, faz sim e é muito mais comum do que você pensa, então, evite fazer isso, é muito mais fácil de entender quando você dá nomes significativos as suas variáveis, funções, classes, etc.
int limiteDeTentativas = 10; int tentativasRealizadas = 1; if(tentativasRealizadas < limiteDeTentativas) { // ... }
int limiteDeTentativas = 10;
int tentativasRealizadas = 1;
if(tentativasRealizadas < limiteDeTentativas) {
Olha como o código ficou bem melhor de entender agora.
Aqui cabe uma observação importante. Se você usa variáveis i em blocos for, como por exemplo: for(int i=0; i<10; i++), isso não chega a ser um problema. Como essa prática é comumente utilizada em diversas linguagens, meio que virou uma conversão, então praticamente todo mundo sabe o que isso significa.
Uma outra dica é: Evite usar prefixo nos nomes de suas variáveis, funções, etc…
String nomeFuncionario = funcionario.meuFW_obterNome(); int idadeFuncionario = funcionario.meuFW_obterIdade(); Double salarioFuncionario = funcionario.meuFW_obterSalario();
String nomeFuncionario = funcionario.meuFW_obterNome();
int idadeFuncionario = funcionario.meuFW_obterIdade();
Double salarioFuncionario = funcionario.meuFW_obterSalario();
Além de ficar horrível, isso prejudica um dos recursos mais interessantes das IDEs, o autocomplete. Imagine você ter diversos métodos e digitar "me". Uma lista enorme de métodos e propriedades são exibidas sem necessidade. Fora isso, chega um momento que seu cérebro se acostuma com esses prefixos e ele nem mais os lê, porque ele já sabe que aquela informação inútil está ali. E se ele não mais as lê, pra que elas existem?
Objetos com parâmetros
Quando uma função parece precisar de mais de dois ou três parâmetros, é provável que alguns deles podem ser colocados em uma classe própria.
void enviarEmailDeNotificacao( int idCliente, String nomeCliente, String emailCliente, String avatarCliente, String mensagem ) { // ... }
void enviarEmailDeNotificacao(
int idCliente,
String nomeCliente,
String emailCliente,
String avatarCliente,
String mensagem
Percebe como há muita informação que poderia ser enviada em um único Objeto? Não seria melhor fazer algo assim?
void enviarEmailDeNotificacao(Cliente cliente, String mensagem) { // ... }
void enviarEmailDeNotificacao(Cliente cliente, String mensagem) {
Prefira exceções a retorno de código de erro
Uma função deve fazer ou retornar algo, não ambos e quando fazermos funções retornarem código de erros esse conceito é violado, pois os comandos são usados como expressões de comparação em estruturas if. Um exemplo:
if(deletarPagina(pagina) == E_OK) { if(registro.deletarReferencia(pagina.nome) == E_OK) { log.gravar("Página Excluída"); } else { log.gravar("Ocorreu um erro ao tentar excluir o registro da página: " + pagina.nome); } } else { log.gravar("Ocorreu um erro ao tentar excluir a página: " + pagina.nome); }
if(deletarPagina(pagina) == E_OK) {
if(registro.deletarReferencia(pagina.nome) == E_OK) {
log.gravar("Página Excluída");
log.gravar("Ocorreu um erro ao tentar excluir o registro da página: " + pagina.nome);
log.gravar("Ocorreu um erro ao tentar excluir a página: " + pagina.nome);
Em um exemplo como esse, o seu sistema precisaria de vários ifs desnecessários para ficar comparando a todo tempo se uma função fez algo.
Não seria muito mais simples fazer algo assim:
try { deletarPagina(pagina); registro.DeletarReferencia(pagina.nome); } catch(Exception e) { log.gravar(e.getMessage()); }
deletarPagina(pagina);
registro.DeletarReferencia(pagina.nome);
log.gravar(e.getMessage());
Inclusive, se você extrair os blocos try/catch, o resultado é ainda melhor. Exemplo:
public void deletar(Pagina pagina) { try { deletarPaginaETodasReferencias(pagina); } catch(Exception e) { gravarLog(e); } } private void deletarPaginaETodasReferencias(Pagina pagina) throws Exception { deletarPagina(pagina); registro.deletarReferencia(pagina.nome); } private void gravarLog(Exception e) { log.gravar(e.getMessage()); }
public void deletar(Pagina pagina) {
deletarPaginaETodasReferencias(pagina);
gravarLog(e);
private void deletarPaginaETodasReferencias(Pagina pagina) throws Exception {
private void gravarLog(Exception e) {
Mas Paulo, a segunda opção é bem maior, eu vou precisar digitar muito mais e terei 3 funções
Sim, eu sei, porém cada uma tem sua responsabilidade. Isso a principio pode parecer mais trabalhoso, mas ao decorrer do desenvolvimento do seu sistema você verá o quanto isso vai lhe ajudar. 🙂
Endentação/Indentação)
Endentar um código adequadamente é tão importante que algumas linguagem (como o Python por exemplo) obrigam você a usar. Mas a questão aqui vai além disso. Blocos dentro de instruções if, else, while, etc. Devem ter apenas uma linha, que geralmente será uma chamada para uma função pequena. Isso melhora consideravelmente a qualidade de seu código e você ainda pensará melhor no conceito de responsabilidade única.
Lembre-se: Uma função deve fazer apenas uma coisa e fazer-la bem.
void cadastrarFuncionario(Funcionario funcionario) { if(funcionario.obterEmail() != null && !funcionario.obterEmail().trim().isEmpty()) { if(funcionarioDao.inserir(funcionario) { if(disparadorDeEmail.enviarBoasVindas(funcionario.obterEmail())) { // ... } else { log.gravar("Ocorreu um erro ao tentar enviar as boas vindas para: " + funcionario.getId()); } } else { log.gravar("Ocorreu um erro ao tentar cadastrar o funcionario: " + funcionario.getId()); } } }
void cadastrarFuncionario(Funcionario funcionario) {
if(funcionario.obterEmail() != null && !funcionario.obterEmail().trim().isEmpty()) {
if(funcionarioDao.inserir(funcionario) {
if(disparadorDeEmail.enviarBoasVindas(funcionario.obterEmail())) {
log.gravar("Ocorreu um erro ao tentar enviar as boas vindas para: " + funcionario.getId());
log.gravar("Ocorreu um erro ao tentar cadastrar o funcionario: " + funcionario.getId());
A função acima tem um nível de profundidade muito grande nos ifs, podemos refatorar esse código para melhorar sua manutenibilidade e sua legibilidade. Uma sugestão seria:
void cadastrarFuncionario(Funcionario funcionario) { try { funcionarioDao.inserir(funcionario); disparadorDeEmail.enviarBoasVindas(funcionario.obterEmail()); } catch(Exception e) { log.gravar(e.getMessage()); } }
funcionarioDao.inserir(funcionario);
disparadorDeEmail.enviarBoasVindas(funcionario.obterEmail());
Qual código é mais fácil de ler?
Evite ao máximo o uso do ELSE
Se você abrir alguns de seus códigos e analisar mais detalhadamente suas funções, certamente você verá uma série de elses que não tem a menor razão para existir. Exemplo:
boolean eMaiorDeIdade(Aluno aluno) { if(aluno.obterIdade >= 18) { return true; } else { return false; } }
boolean eMaiorDeIdade(Aluno aluno) {
if(aluno.obterIdade >= 18) {
Você poderia simplificar esse código para:
boolean eMaiorDeIdade(Aluno aluno) { if(aluno.obterIdade >= 18) { return true; } return false; }
viu como o else não é necessário? Mas ainda dá para melhorar ainda mais:
boolean eMaiorDeIdade(Aluno aluno) { return aluno.obterIdade() >= 18; }
return aluno.obterIdade() >= 18;
Aqui pode estar a parte mais polêmica desse Post. Sabe quando seu professor dizia que é sempre bom comentar todo o seu código? Pois é, ele estava errado. Comentários, na grande maioria das vezes serve para uma única coisa. Compensar o nosso fracasso em nos expressar em nossos código.
É muito comum lermos códigos onde 50% ou mais do código é comentário. Você muitas vezes tem 5 ou 6 linhas de comentários para explicar 2 linhas de código. Mas, se o código for autoexplicativo, pra que comentário?
O que você preferiria ver em um código? Isso:
/** * Caso o funcionário tenha mais de 60 anos, tenha um salário inferior a R$ 3000,00 * e caso ele tenha filhos, o mesmo está elegível a receber um bonus */ if(funcionario.idade > 60 and funcionario.salario < 3000 and funcionario.temFilhos()) { // }
* Caso o funcionário tenha mais de 60 anos, tenha um salário inferior a R$ 3000,00
* e caso ele tenha filhos, o mesmo está elegível a receber um bonus
if(funcionario.idade > 60 and funcionario.salario < 3000 and funcionario.temFilhos()) {
ou simplesmente um desses métodos?
if(funcionario.estaElegivelParaOBonus()) { } if(funcionario.eBonificavel()) { } if(funcionario.podeReceberBonus()) { }
if(funcionario.estaElegivelParaOBonus()) { }
if(funcionario.eBonificavel()) { }
if(funcionario.podeReceberBonus()) { }
É claro que não estou dizendo para você nunca comentar seus código. Em diversas situações você precisará deixar um comentário explicativo. Um exemplo disso é em blocos de expressões regulares, que geralmente tem uma leitura bem complicada ou em blocos que podem afetar arquivos importantes caso sejam mal utilizados. Mas a ideia aqui é você tornar o seu código o mais legível possível, assim você irá cada vez menos precisar de comentários.
Uma coisa é bastante irritante é abrir um código como esse:
// Armazena o nome do usuário String nome; // Armazena a idade do usuário int idade; // Armazena o e-mail do usuário String email;
// Armazena o nome do usuário
// Armazena a idade do usuário
// Armazena o e-mail do usuário
Qual a necessidade disso, jovem? será que um atributo nome, dentro de uma classe Usuario, não é explicativo o suficiente?
Bem, esse foi o básico sobre Clean Code. É óbvio que tem muito mais coisa a ser explorada e provavelmente eu faço uma parte 2, e 3 e 4… desse post.
Espero ter sido claro em minhas explicações e espero que esse Post lhe ajude de alguma forma a melhorar seu código, mesmo que você trabalhe sozinho. Até a próxima!!!
Rangel Rodrigo disse:
Muito interessante, legal o artigo!
De tudo que foi dito aí reparei que tenho como hábito exagerar no comentário, incluindo as vezes até o demandante de uma solicitação. Penso na rapidez de manutenção e que o próximo a trabalhar no código pode ser novato.
Valeu, Rangel 😉
Mais legal do que o artigo é o teu site, Bugginho. Inspirador demais esse layout clean. <3 Abraço!
Valeu, amiguinho. Mas o Layout não foi desenvolvido por mim, eu apenas tive o bom gosto de escolher ele :), caso queira utiliza-lo, eu comprei ele por cerca de U$ 70,00 no Theme Forest. O nome dele é: Gridlove
11 de fevereiro de 2018 às 15:17
Faz tempo que não utilizo o else. Puuts realmente ele é desnecessário, só fazer a lógica ao contrário em alguns casos.
Também faço nome de variáveis bem auto-explicativas, assim qualquer programador vá conseguir entender, Nome dos métodos faço a mesma coisa, sempre.
Quanto ao uso de Regex, quando é algo meio complicado, eu faço bloco de comentários e a string onde a regex irá resolver o problema. Também faço comentários em consts de ValueObjects, quando o objetivo é encasular vários status para uma regra de negócio. Mesmo os consts sendo auto-explicativas, acho necessário escrever algo resumido só para ter algo documentado.
Ótimo artigo, clean code é vida!
11 de fevereiro de 2018 às 15:38
É isso ai, amiguinho. Um código bem escrito é melhor que 1.000 linhas de comentários inúteis 😉
Excelente post este livro faz parte de uma lista de livros ao qual tenho que ler.
19 de fevereiro de 2018 às 04:06
Concordo com quase tudo q vc disse, exceto pelo uso do else. Nunca vi o else como inimigo e varias vezes eu acabei encontrando falhas de design quando me pergunto "e se essa condição for falsa? Tenho q fazer alguma coisa?". Agora, retornar variáveis inicializadas com valor default me parece mais o uso de funções com ponto de retorno único. Sempre gostei dessa técnica, ela faz sentido principalmente em linguagens como C que não tem limpeza de memória automática.
23 de julho de 2018 às 06:24
Discordo fortemente da ideia de não usar comentários, e de códigos """""""""auto explicativos""""""""" (sim, com essa quantidade de aspas).
O que é fácil e auto explicativo para você, programador, não o será para o próximo programador, nem para você mesmo, daqui a vários anos.
E ainda que definir e explicitar o que faz uma variável pelo nome às vezes baste, não podemos nem de longe dizer o mesmo de funções, como elas operam e fazem o que têm de fazer.
Na minha opinião, um código bem feito, tem sim mais comentários que código escrito.
As variáveis são curtas e bem explicadas, e o "grosso" dos comentários se limita às declarações iniciais, sendo reduzido conforme o código vai sendo escrito.