Clean code, o guia definitivo
Dirty code, o problema
Quem nunca pegou um código escrito por você mesmo, e não conseguia mais entender o que o seu eu de um dia atrás quis dizer com aquelas lógicas bizarras ? Ou quem nunca se deparou com uma função gigantesca que só de abrir o vscode trava ? Pois bem, isso é um dos exemplos de códigos que podem ter com certeza code smells (problemas no código).
Clean code, a solução
Estes exemplos de problemas no tópico acima evidenciam que a manutenção de um software é tão crucial quanto o seu desenvolvimento inicial. Fazer software é fácil; difícil é torná-lo maleável o suficiente para acomodar incrementos e correções.
Conforme destacado por Robert C. Martin em seu icônico livro “Clean Code”, amplamente reconhecido em nossa área, existem práticas e perspectivas fundamentais que desempenham um papel vital na sustentação da longevidade de nosso software.
Vale ressaltar que este post não substitui a leitura do livro, que aprofunda ainda mais esses conceitos.
Empresas investem milhões em projetos de software todos os anos. No entanto, com as constantes mudanças em equipes e tecnologias, como podemos garantir que esse investimento seja duradouro? Como assegurar uma manutenção eficaz e a longevidade de um software? Segundo Uncle Bob (Robert C. Martin), as práticas a seguir apontam o caminho, mantendo a essência, mas de uma forma mais acessível.
A Complexidade das Abstrações
Abstrações são como mágica no mundo do desenvolvimento, mas às vezes essa mágica se parece mais com um truque de ilusionismo complicado. Mantenha suas abstrações simples e fáceis de entender.
Siga o princípio KISS (Keep It Simple, Stupid) - mantenha suas funções pequenas e focadas. Grandes abstrações complexas podem ser como um labirinto sem saída.
A Regra do Escoteiro
Sempre mantenha o acampamento mais limpo do que você o encontrou! O mesmo princípio se aplica ao nosso código. Ao mexer em alguma parte do código, assegure-se de que esse código esteja em melhor estado do que quando você o recebeu. Se cada membro da equipe seguir essa mentalidade e entregar um código aprimorado a cada vez, em pouco tempo o projeto por si só vai estar múltiplas vezes melhor.
O Poder dos Nomes
Vamos começar com algo simples, mas poderoso: nomes de variáveis ou métodos. Escolher nomes significativos é como dar um mapa para quem está tentando entender seu código, não se preocupe com o tamanho, o nome deve ser autoexplicativo ao ponto de não necessitar de comentários adicionais.
// Impossível de saber o que é
const a1 = 1000;
// Total do que ?
const total = 1000;
// Muito melhor
const saldoTotalConta = 1000;
Faça distinções significantes
Sempre escolha nomes que tornem o significado distinto, permitindo que qualquer pessoa que leia o código possa diferenciá-los de outros possíveis nomes.
// Duração em que ?
const duração = 10;
// Agora sim :)
const duraçãoEmMinutos = 10;
Utilize nomes pronunciáveis e buscáveis
// Evite
const stringTexto = "Meu texto aqui";
// Evite
public void GenerateBancoCode(): string {}
// Evite
public void Registry(): void {}
Evite uso de condicional com strings
Quem nunca perdeu uma tarde procurando um bug, que era apenas um typo (erro de digitação), ou teve que alterar a lógica de negócio em vários lugares que faziam a mesma comparação com string? Evite esses problemas colocando a string em uma constante, para ser utilizado em todo escopo, assim só será necessário modificá-lo em um único lugar.
// Com certeza vai ter mais de um lugar na aplicação verificando essa condição
if (environment === 'PROD')
// Podemos colocar a string em uma constante
export const ENV = 'PROD'
// E melhor ainda, podemos criar um método disponível em toda aplicação,
// para manter a condição padronizada
export function isProductionEnvironment(environment: string): boolean {
return environment === ENV
}
// Agora em todo lugar que precisamos verificar essa condição,
// podemos utilizar a função criada para fazê-lo
if (isProductionEnvironment(environment))
Cada um com sua função
As mesmas regras de nomes mencionadas antes podem ser aplicadas para funções.
Menores e com menos responsabilidades
Do mesmo jeito que nós, ao acumularmos muitas responsabilidades e funções, acabamos não fazendo nenhuma de forma eficiente, as funções em código também obedecem a essa premissa. Funções com uma carga de responsabilidades acabam ficando extensas, difíceis de ler devido às várias ramificações de ifs que surgirão e difíceis de serem testadas unitariamente, pois aumentam a complexidade do teste e, pior ainda, ficam muito difíceis de serem reaproveitadas devido à complexidade delas. Faça funções menores e atribua a elas uma única e exclusiva responsabilidade.
// Como seria se tudo ficasse em uma função
public function finalizarPedido(): Promise<void> {
// Cadastra o cliente
// Valida o pedido
// Salva os itens do pedido
// Aplica o desconto
// Atualiza o estoque
// Salva o pedido
}
// Distribua as responsabilidades em várias funções menores
public function registerUser() {}
public function validateOrder() {}
public function saveOrderItems() {}
public function applyDiscountOnOrder() {}
public function updateInventory() {}
public function saveOrder() {}
Não Engorde Sua Função Com Vários Parâmetros
Evite o uso excessivo de parâmetros em seus métodos. Tente agrupá-los em um objeto, por exemplo.
// Evite
public function saveAddress(
street: string,
neighborhood: string,
number: number,
zipcode: number,
state: string
): Promise<boolean> {}
// Utilizando um objeto Address para salvar no banco um endereço
public function saveAddress(address: Address): Promise<boolean> {}
Se Você Utiliza Flags, Dá Para Separar
Se na sua função está sendo utilizado o famoso flag para controlar o fluxo do seu método, provavelmente você consegue refatorar seu método para separar a responsabilidade em outros métodos menores.
// Aqui temos uma flag 'create', controlando se vai para o fluxo de criação ou atualização
class OrderRepository {
public function createOrUpdate(order: Order, create: boolean): Promise<void> {
if (create) {
...
} else {
...
}
}
}
// Ao invés disso, podemos separar o método em duas funções,
// uma para criar, outra para atualizar
public function createOrder(order: Order): Promise<void> {}
public function updateOrder(order: Order): Promise<void> {}
Comentários Devem Ser a Exceção
O código por si só já deve ser expressivo o suficiente para ser possível ler e entender, assim como fazemos com as linguagens humanas. Caso esteja necessitando de muitos comentários para explicar o seu código, provavelmente é bom voltar para a seção de nomes para verificar o que pode ser melhorado.
Evite Ser Redundante
Evite comentários sem necessidade.
// Evite
// Cria usuario
public function createUser() {}
Não Deixe Código Comentado
Se lembra daquele código que você comentou porque achou que iria precisar algum dia? Então, com certeza, se você voltar lá depois de anos, ele ainda estará lá esperando por você. Hoje temos versionadores de versões e somos capazes de “voltar no tempo” com tranquilidade.
// Vai por mim, você provavelmente não vai mais precisar
public function createUser(user: User) {
// const isDev = user.isIdev TODO: um dia posso precisar
}
**Bom Uso de Comentário Existe? **
SIM, omentários são muito bem-vindos, principalmente para explicitar decisões de negócio, esclarecimentos do porquê foi implementado daquela forma ou consequências que podem ter caso o código seja modificado. Essas coisas apenas com nomes e organização não vão deixar evidenciadas por si só.
// Mostrando qual foi a intenção do código
// Retorna lista de produtos que estão inativos
// para fazer o relatório de fechamento mensal
public function obtemProdutosInativos(): Product[] {}
// Esclarecendo sobre o porquê da condicional
// Se o pedido já foi enviado
// Este pedido não pode mais ser estornado
if (sendDate > now) {}
// Alertando sobre consequências de alterar um endpoint compartilhado por diversas plataformas
// ATENÇÃO: Este método é compartilhado com os aplicativos
public function getUser() {}
Testando, Testando, 1, 2, 3
Com as funções menores e seu código mais expressivo, os testes com certeza serão mamão com açúcar. Faça um teste seguindo o princípio da importância da nomenclatura das coisas, deixando o teste expressivo, teste focado em cada método do seu código e mantenha tudo independente e rápido.
Conclusão === Introdução
Bem tudo que descrevi, é apenas um grão de areia no deserto do classíco livro “Clean Code”, recomendo dar uma leitura pelo menos uma vez na sua carreira como desenvolvedor, pois irá te abrir os olhos para muitas oportunidades de melhoria de código que você não notava antes.
Até o proximo deploy 🚀