Clean Code: Por onde começar?

Luis Junior
afya
10 min readApr 2, 2021

--

Hoje muito se fala nesse tão famoso "Clean Code" quando se trata de desenvolvimento. Mas de fato o que isso significa? Como podemos começar a aplicá-lo em nosso dia a dia?

Estas e muitas outras perguntas serão respondidas neste artigo, muito embasadas neste livro do Martin que trata sobre o assunto.

E o que é de fato Clean Code?

A melhor definição que encontrei durante o estudo deste tema pertence ao Fowler:

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
Fowler, Martin

Verdade nua e crua, de fato qualquer um pode escrever um pedaço de código, mas somente bons programadores conseguem escrever algo legível para humanos.

E como se não bastasse a objetividade do Fowler, olhem a abertura do livro do Martin:

“Writing clean code is what you must do in order to call yourself a professional.
There is no reasonable excuse for doing anything less than your best.”
Martin, Robert

Martin afirma no livro, depois de uma análise de sua própria IDE, que passa dez vezes mais tempo lendo um código do que escrevendo um. Talvez essa proporção varie um pouco dependendo da experiência de cada programador, mas fato é que passamos muito mais tempo lendo e tentando entender o que aquele trecho faz, do que de fato implementando novas funcionalidades.

E se de fato um código limpo é tão importante, por quê nos deparamos com muitos códigos ruins, incluindo nossos próprios? Desculpas é o que não faltam:

  • Cronograma apertado;
  • Mudança de escopo;
  • Fazer funcionar o básico e melhorar depois;

Quem nunca utilizou essa última, que atire a primeira pedra. O problema é que o "depois" custa a chegar, isso quando chega.

Uma das grandes (e muitas) lições que aprendi com esse livro foi a regra dos escoteiros:

“Always leave the campground cleaner than you found it.”

O mesmo vale para o código. Assim como os escoteiros deixam a área mais limpa do que encontraram para o próximo grupo de escoteiros, é sua função como desenvolvedor fazer o mesmo para os colegas de profissão. Com certeza, meu "eu" do futuro irá sempre agradecer meu "eu" do passado por ter melhorado mesmo que pequenas partes de um código existente.

Seguindo a proporção de leitura/escrita citada pelo Martin, é muito comum, e já vi isso acontecer inúmeras vezes, nos depararmos com códigos que nós mesmos escrevemos, inclusive trabalhando em grandes equipes. Por isso, dê o seu melhor no que estiver fazendo.

Escrever um código limpo ajuda e muito em um dos principais gargalos de muitos times: o Code Review.

Imagine que acabou de abrir um PR, seja para a correção de um bug ou implantação de uma feature nova, como é o sentimento depois que o PR é aberto? Falo por mim, e o mínimo que sinto é ansiedade de ver aquele card se mover para done. Desde o refinamento até o code review é sua direta responsabilidade fazer o melhor possível, e está somente em suas mãos, o quão rápido e quanto de qualidade colocará em prática.

Essa ansiedade muitas vezes é a causa do gargalo de revisão, onde uma vez que o PR está aberto, muitos desenvolvedores puxam novos cards atacar. Claro que em algum momento alguém deverá revisar o seu código, mas neste status, não é responsabilidade exclusiva do revisor dar andamento no seu card, mas sim do autor em fazer o melhor possível para que a revisão seja bem feita e focada no que importa, otimizando assim não só o código mas também o tempo, recurso mais que escasso na nossa profissão.

E como depois que enviamos um card para revisão, podemos acelerá-lo sendo que não depende de nós? Simples. Revise outros pull requests. Ao revisar os PRs de seus pares, o card deles anda, e libera tempo, que se investido na revisão do seu, cria-se uma situação de ganha-ganha e a roda se retro-alimenta para o próximo ciclo.

Otimizando o review

Aqui deixarei algumas dicas de como acelerar o code review e como um código limpo ajuda neste processo. Os exemplos e libs citadas abaixo serão em python, por preferência pessoal, mas podem ser aplicados a qualquer linguagens com ou sem ajuda de bibliotecas compatíveis.

pre-commit

Essa foi uma lib que causou uma virada de chave nos reviews assim que foi implementada em nosso time. O pre-commit checa inúmeras coisas antes mesmo do commit partir chegar ao repositório, além de permitir que sejam criados hooks customizados.

Com ele é possível impedir que códigos fora do acordo de time sejam commitados com o auxílio de diversos hooks, como por exemplo:

  • Black: formatador de código python;
  • isort: Organizador de imports, que ordena de forma alfabética, separados por seções e tipos;
  • flake8: um projeto que combina 3 ótimas ferramentas em um só pacote: Pep8 que verifica se o estilo do código respeita o padrão adotado pela comunidade; PyFlakes que analisa estaticamente seu código detectando inúmeros anti-patterns e erros lógicos como módulos importados que não são utilizados, uso de variáveis não declaradas, entre muitas outras coisas e o Codepaths que realiza a análise da complexidade ciclomática do código com base nas métricas de McCabe.

Dicas úteis por onde começar a escrever um código limpo:

Utilize nomes que revelem o seu propósito

Qual das linhas abaixo teria mais facilidade de entender?

d = 30elapsed_time_in_days = 30

Evite informações erradas

Não se refira a um grupo de contas como account_list, a não ser que seja de fato uma lista. List significa algo específico para os programadores, e se de fato não for uma lista opte pelo simples: accounts.

Organize o seu código pensando na busca por alguém que nunca viu aquele trecho.

Se você precisa saber onde foi criado um método para obter todos os usuários de uma conta, qual o primeiro local que vai procurar? Claro, na classe Account, e de preferência por um método chamado get_users. Qualquer situação muito diferente disso, com certeza não será intuitiva e provavelmente não está no melhor lugar no código.

Aproveite da legibilidade

É totalmente aceitável que uma variável tenha apenas uma letra, desde que o tamanho de seu nome seja proporcional com seu escopo, como por exemplo em estruturas de repetição:

def total_price(self):
s = 0
for u in self.get_users():
s += u.price
return s

A variável s neste pequeno escopo representa a soma de todos os preços de cada usuário e está claro neste pequeno laço. Um outro excelente nome seria total.

Agora se uma variável for utilizada em diversas partes do código, uma única letra pode complicar a muito se precisar buscá-la.

Nomes de classes

Os nomes das classes devem ser substantivos, como Account, User, evite nomes como Manager, Processor, Data ou Info.

Nomes de métodos

Devem ser verbos pois eles fazem alguma coisa, como save, post e delete.

Selecione uma palavra por conceito

É bastante comum encontrar classes com diferentes métodos equivalentes como get, retrieve, fetch. Isso gera uma confusão mental e um esforço tamanho em lembrar ou consultar qual método é de cada classe.

Selecione apenas uma palavra por contexto:
- add ou insert;
- delete ou remove;
- get ou retrieve;

Evite comentários

Apenas faça uso de comentários quando estritamente necessários. Se escreveu um bloco de código que requer um comentário, há grandes chances de não estar bem escrito, considere refatorá-lo antes de incluir um novo comentários.

Faça distinções significativas

Evite utilizar variáveis como a1, a2, a3. Além de não oferecerem informação alguma, geram confusão.

Use nomes pronunciáveis

Abreviações e acrônimos, além de muitas vezes ficarem somente na cabeça do autor, são impronunciáveis quando houver a necessidade de discutir a respeito.

No livro Martin cita um exemplo, onde uma variável foi definida como gen_ymd_his, e toda conversa que requeria citá-la, era feito da seguinte forma: gen ipslon eme dê agá i ésse.

Percebendo o quão improdutivo e ridículo isso era, ela foi renomeada para generation_timestamp.

Conheça seus Design Patterns e nomeie por domínio de solução

Para quem não conhece o termo, recomendo a leitura deste um ótimo livro a respeito. Também há uma abordagem bem prática neste que mostra exemplos em python.

E por quê é importante conhecer os Design Patterns? Da mesma forma que quando nomeia algo como List, algo já ascende na mente do desenvolvedor indicando que aquele objeto é iterável, utilizar de nomes conhecidos e consolidados que resolvem problemas há muito tempo, tem o mesmo efeito.

Quando utilizamos Singleton, Factory, Visitor ou Strategy por exemplo, já no nome de uma classe, aquele código não será total desconhecido para um próximo desenvolvedor que o pegar, facilitando e o otimizando assim muito o tempo de leitura e entendimento.

Funções e métodos

Funções/Métodos devem fazer apenas uma coisa e devem fazê-la bem.

Evite a todo custo efeitos colaterais. Uma função/método que checa uma senha, deve apenas fazer isso e jamais iniciar uma nova sessão para o usuário. No contexto de login ambas estão diretamente ligadas, mas qualquer outra checagem de senha não pode iniciar um nova sessão, pois trará um efeito indesejado e difícil de ser rastreado.

Outro ponto importante é que devem ser pequenas. Um bloco if dentro do método pode ser facilmente extraído para outro método, permitindo que dentro do condicional seja executada somente a linha da invocação do método novo criado.

Quantidade de parâmetros

Segundo o Martin, o número de parâmetros ideal para um método é zero, mas um é o mais comum de encontrarmos.
Até dois é aceitável, porém mais que isso começa a complicar muito, inclusive nos testes, além é claro de explicitar que aquele método faz mais que apenas uma coisa.

Evite passar booleanos para funções/métodos

Por quê? Simples. Informar um booleano para um método, na maioria das vezes é esperado que ele tenha um if em seu corpo e obviamente fará duas coisas: uma quando for True e outra quando for False;

Extraia os blocos try/except/finally para métodos separados
Isso facilita a leitura do bloco de código que o invoca e deixa muito mais fácil os testes.

Classes

Classes devem ser pequenas possuirem uma única responsabilidade e seguir os 5 princípios do S.O.L.I.D. :

  • (S)ingle Responsiblity Principle (Princípio da responsabilidade única)
  • (O)pen-Closed Principle (Princípio Aberto-Fechado)
  • (L)iskov Substitution Principle (Princípio da substituição de Liskov
  • (I)nterface Segregation Principle (Princípio da Segregação da Interface
  • (D)ependency Inversion Principle (Princípio da inversão da dependência)

Comentários

Comentários em sua maioria, são tentativas frustradas de melhorar um código ruim. Ao invés de comentar, refatore e limpe o código.

Essa prática não é a melhor forma de explicar ou até mesmo documentar o que aquele código faz, pois deterioram muito rápido. Muitas vezes uma alteração no código não é refletida em seu comentário, o que o torna mentiroso e atrapalhará mais do que ajudará.

Outro detalhe curioso é como editores apresentam os comentários, muitos deles com uma cor bem suave e com uma opacidade considerável em relação ao resto do código, o que pode passar batido durante uma leitura desatenta.

Outra prática bastante comum são os famosos comentários TODO. É perfeitamente aceitável que sejam utilizados, mas apenas durante o desenvolvimento. Se forem enviados para o repositório, na maioria das vezes serão esquecidos pelo autor, e demais desenvolvedores, sem o contexto daquele escopo, não se sentirão confortáveis de resolvê-lo, o que o transforma em um comentário WONTDO.

Testes

Os cuidados com os testes devem ser o mesmos apresentados até aqui. Os testes são igualmente importantes dentro de um sistema tanto quanto o próprio código de produção. Ter uma suíte de testes limpas garantirá que ela entregue uma cobertura melhor, tenha baixa manutenibilidade e proporcione maior confiança aos desenvolvedores durante o trabalho.

Ainda sobre cobertura, vale acrescentar um ponto curioso. Qual o percentual aceitável de cobertura de um código? Em uma talk de 2019, Martin questionou isso ao público presente, e ao receber a resposta de 80%, devolveu com a seguinte pergunta:

Se você acha que 80% é o aceitável , então 20% do seu código não precisa funcionar, certo?

100% é um número alto a ser buscado mas os benefícios que uma boa suite de testes trás são imensuráveis. Imagine que tenha um botão que disparará sua suite. Durante o desenvolvimento de uma nova feature, você pode apertar o botão quando quiser. Quando o que estiver fazendo quebrar algum teste em outra parte do sistema, terá poucas linhas para analizar e corrigir o problema.

Outro ponto importante é que 100% de cobertura não é ter testes que correm em todas as linhas da sua aplicação. Isso é fácil de fazer será uma bomba difícil de desarmar quando encontrar um problema. Ter uma suite com cobertura total também requer que os testes estejam bem escritos, cobrindo todos os cenários e principalmente: limpos.

Uma boa forma de atingir isso é utilizando o TDD e seguindo o princípio do
F.I.R.S.T.:

  • (F)ast: devem ser rápidos;
  • (I)ndependent: não devem depender uns dos outros;
  • (R)epeatable: Deve-se poder repetir em qualquer ambiente;
  • (S)elf-validating: Devem ter uma saída booleana;
  • (T)imely: Unitários devem ser criados antes do código de produção;

Recomendações

Assistam ao Evento “Coding Better World Together (2019)” com Robert C. Martin.
- Lesson 1 (1h48) — https://www.youtube.com/watch?v=7EmboKQH8lM
- Lesson 2 (1h06) — https://www.youtube.com/watch?v=2a_ytyt9sf8
- Lesson 3 (59m) — https://www.youtube.com/watch?v=Qjywrq2gM8o
- Lesson 4 (1h30) — https://www.youtube.com/watch?v=58jGpV2Cg50

Bônus: Agile + Clean Architecture:
- Lesson 5 (2h10) — https://www.youtube.com/watch?v=sn0aFEMVTpA
- Lesson 6 (1h38) — https://www.youtube.com/watch?v=l-gF0vDhJVI

As dicas e sugestões aqui, não são a verdade absoluta sobre Clean Code, apenas uma perceção minha em relação as recomendações do Martin. É importante que as entenda, analise e implemente as que julgarem interessantes e necessárias.

Lembrem-se da fala do Martin:

Ninguém escreve código limpo na primeira tentativa. O cérebro humano não pensa dessa forma. Primeiro resolvemos o problema de maneira desordenada. Quando o código funciona, completamos apenas 50% do trabalho a ser feito.

E ai curtiu a leitura? Que tal um feedback? Deixe uma palma se as dicas acima foram úteis ou ainda se aprendeu algo novo ou um comentário se tem alguma outra sugestão, crítica, elogio, etc.

--

--

Luis Junior
afya
Writer for

Squad Leader at iClinic (aquired by NASDAQ:AFYA)