Skip to content

Instantly share code, notes, and snippets.

@Leo-Lima-Mar
Last active January 9, 2024 23:03
Show Gist options
  • Save Leo-Lima-Mar/f4fce3ccb9a59c96a819c32411d592dd to your computer and use it in GitHub Desktop.
Save Leo-Lima-Mar/f4fce3ccb9a59c96a819c32411d592dd to your computer and use it in GitHub Desktop.
Resumo de parte do livro Clean Code

Clean Code

Aqui estão listadas algumas das principais recomendações do livro Clean Code - Robert C. Martin, 2008

2) Meaningful Names

Use Intention-Revealing names

Avoid Disinformation

  • Não adicionar o tipo da variável no seu nome.
    Ex: accountList.
  • Não utilizar nomes parecidos.
  • Jamais utilizar a letra 'l' ou a letra 'O' como nomes de variáveis.

Make Meaningful Distinctions

  • Não utilizar nomeação serial com números.
    Ex: var1, var2, ..., varN
  • Não utilizar nomes diferentes para o mesmo significado.
    Ex: Product, ProductInfo, ProductData são similares.

Use Pronunceable Names

  • Com nomes pronunciáveis a comunicação entre programadores se torna mais fácil.

Use Searchable Names

  • Não utilizar nomes de apenas uma letra.
  • Não utilizar números diretamente (usar constantes).
  • O tamanho de um nome deve ser correspondente ao escopo deste nome. Quanto maior o escopo, maior deve ser o nome, assim evitando confusões.
  • É aceitável utilizar uma letra para controle de loop.

Avoid Mental Mapping

  • É bom evitar o mapeamento mental, onde a memória deve ser usada para lembrar o que um trecho de código faz, ao invés de seus nomes serem claros.

Class Names

  • Classes e objetos devem ter substantivos em seus nomes. Evitar palavras como manager, processor, data, info.

Method Names

  • Métodos devem ter verbos em seus nomes.
  • Quando há mais de um construtor para a classe, usar static factory methods com nomes que descrevem os argumentos.
    Ex:
    Complex fulcrumPoint = new Complex(23.0);
    // Substituir por:
    Complex fulcrumPoint = Complex.FromRealNumber(23.0);
    // E fazer os construtores equivalentes privados.

Pick One Word per Concept

  • Utilizar a mesma palavra para o mesmo conceito.
    Ex: Escolher uma entre: get, fetch, retrieve. Escolher uma entre controller, manager, driver.

Use Solution Domain Names

  • Usar nomes técnicos conhecidos, como termos de ciência da computação, nomes de algoritmos, padrões, termos matemáticos, etc.

Use Problem Domain Names

  • Quando não houver possibilidade de nomear de acordo com a seção anterior, nomear de acordo com o domínio do problema.

Add Meaningful Context

  • Se um nome puder dizer outra coisa em outro contexto, o ideal é encapsulá-lo em uma classe.

Don't Add Gratuitous Context

Ex: Empresa com as iniciais GSD. Não utilizar esta sigla como prefixo de todas as classes.

3) Functions

Small!

  • Funções devem ser do menor tamanho possível. O ideal é até 5 linhas, podendo ser maiores se for absolutamente necessário.
  • Blocos dentro de if, else, while normalmente devem ter apenas uma linha, que pode ser a chamada de uma função.
  • Uma função não deve ter um nível de identação maior do que 1 ou 2.

Do One Thing

  • Uma função deve realizar o trabalho de apenas um nível de abstração abaixo do nome da função.

Switch Statements

  • Só deve ser utilizado em caso de extrema necessidade.
  • Se existir, deve ser em uma classe de baixo nível e jamais se repetir.

Use Descriptive Names

  • Um nome longo descritivo é melhor do que um nome curto enigmático.
  • Não ter medo de perder tempo escolhendo um bom nome.

Function Arguments

  • Tentar utilizar o mínimo de argumentos possível.
  • Não utilizar argumentos de saída.
  • Evitar ao máximo passar booleanos para funções. Provavelmente deveriam ser duas funções.
  • Evitar funções com dois parâmetros, pois torna-se necessário lembrar a ordem dos argumentos.
  • Funções com três parâmetros são ainda piores, devendo ser muito evitadas. Talvez seja melhor passar um objeto, por exemplo.

Have no Side Effects

Command Query Separation

  • Funções devem fazer algo ou responder algo, não os dois. Ou seja, ou devem alterar algo ou retornar algo.

Prefer Exceptions to Returning Error Codes

  • É melhor extrair os corpos dos blocos try e catch em suas próprias funções.
  • Tratar erros já é uma coisa, logo uma função com um bloco try-catch não deve precisar fazer mais nada.

Don't Repeat Yourself

  • Duplicação de código é um dos maiores problemas na programação.

4) Comments

Comments Do Not Make Up for Bad Code

  • O uso apropriado de comentários serve para compensar nossa falha em nos expressar no código.
  • Comentários errados são piores do que não haver comentários.

Explain Yourself in Code

  • Na maioria das vezes, um comentário pode ser substituído por uma função com o nome apropriado.

Good Comments

  • Legal Comments: copyright e authorship podem ser necessários e razoáveis de se colocar no início de cada arquivo.
  • Informative Comments: por exemplo, explicando o formato aceito por uma expressão regular.
  • Explanation of Intent: explicar a intenção por trás de uma decisão.
  • Clarification: quando o código não puder ser modificado e algo obscuro necessitar ser explicado.
  • Warning of Consequences: avisar os efeitos colaterais de se executar um método.
    Ex: Test case demorado.
  • TODO Comments: não é uma desculpa para deixar código ruim no sistema. Serve como um lembrete.
  • Amplification: amplifica a importância de uma ação que caso contrário poderia parecer sem importância.
  • Javadocs in Public APIs.

Bad Comments

  • Mumbling: comentários inexpressivos. Qualquer comentário que o force a olhar outro módulo para entender o significado dele é ruim.
  • Redundant Comments: comentários que explicam algo o qual o próprio código explica bem.
  • Misleading Comments: comentários que enganam quem lê, dizendo que algo que não acontece está ocorrendo (ou vice-versa).
  • Mandated Comments: forçar que todas as funções, classes ou variáveis tenham Javadoc.
  • Journal Comments: comentários feitos a cada edição de código, algo da época em que não havia sistemas de controle de versão.
  • Noise Comments: falam apenas o óbvio, sem dar nenhuma nova informação.
  • Scary Noise: comentários que sequer foram lidos por quem os escreveu, possuindo erros grosseiros.
  • Don't Use a Comment When You Can Use a Function or a Variable
  • Position Markers: separadores de áreas do código.
  • Closing Brace Comments: comentários no final de chaves (blocos).
  • Commented-Out Code: trecho de código comentado sem explicação para tal.
  • HTML Comments: comentários com HTML.
  • Nonlocal Information: comentários devem se referir a algo próximo a ele.
  • Too Much Information: comentários não devem ter detalhes irrelevantes.
  • Inobvious Connection: comentários que não parecem relacionados ao código ao qual estão associados.
  • Function Headers: funções pequenas devem ser entendidas apenas pelo seu nome, retirando a necessidade de comentários explicativos no cabeçalho da função.
  • Javadoc in Nonpublic Code: código privado não deve ter javadoc.

5) Formatting

Vertical Formatting

The Newspaper Article Metaphor
  • O nome deve ser simples mas explanatório, sendo o suficiente por si só para sabermos se estamos no módulo certo ou não.
  • No topo deve haver os conceitos e algoritmos de nível mais alto, enquanto os detalhes aumentam conforme se vai descendo.
Vertical Opennes Between Concepts
  • Cada linha representa uma expressão ou uma cláusula, e cada grupo de linhas representa um pensamento completo. Esses pensamentos devem estar separados uns dos outros com linhas em branco.
Vertical Density
  • Linhas que estão estreitamente relacionadas devem aparecer verticalmente densas.
Vertical Distance
  • Conceitos que são proximamente relacionados devem ficar verticalmente próximos uns dos outros. Devem ficar no mesmo arquivo a não ser que haja uma ótima razão para o contrário.
  • Variable Declarations: variáveis devem ser declaradas o mais próximo possível de onde são usadas. Com funções de poucas linhas, portanto, devem aparecer no topo. Variáveis de instância devem aparecer no topo da classe.
  • Dependent Functions: se uma função chama outra, elas devem estar verticalmente próximas e a chamadora deve estar acima da chamada, se possível.
  • Conceptual Affinity: quanto maior a afinidade, menor deve ser a distância vertical entre duas coisas.
Vertical Ordering
  • A ideia é que chamadas de função apontem para baixo, de alto para baixo nível.

Horizontal Formatting

  • Linhas devem ter no máximo 100-120 caracteres.
Horizontal Opennes and Density
  • Espaço horizontal é utilizado para associar coisas que estão fortemente relacionadas e desassociar coisas que estão mais fracamente relacionadas.
  • Outro uso é para acentuar a precedência de operadores.
    Ex: x = b*b - 4*a*c
Horizontal Alignment
  • Declarações e atribuições devem estar desalinhadas.
    Ex:
    x = y;
    agua = mar;

    // Ao invés de:
    x =    y;
    agua = mar;
Identation
  • A identação demonstra a hierarquia de escopos do código.
  • Breaking Indentation: funções ou loops curtos não devem ser escritos em uma única linha.
Dummy Scopes
  • Loops sem bloco de código devem ser evitados. Se não for possível evitar, deixar {} vazio.
    Ex:
    while (readLine() != null) {
    }

Team Rules

  • Uma equipe de desenvolvedores deve concordar com um único estilo de formatação.

6) Objects and Data Structures

Data Abstraction

  • Uma classe expõe interfaces abstratas que permitem usuários manipularem a essência dos dados sem ter que conhecer sua implementação.
  • Getters e Setters devem ser bem pensados antes de serem implementados, para evitar a criação de dependências.

Data/Object Anti-Symmetry

  • Objetos escondem seus dados atrás de abstrações e expõem funções que operam dados.
  • Estruturas de dados expõem seus dados e não possuem funções significativas.
  • Código procedural faz ser difícil adicionar novas estruturas de dados porque todas as funções devem mudar. Código OO faz ser difícil adicionar novas funções porque todas as classes devem mudar.
  • A ideia de que tudo é um objeto é um mito.

The Law of Demeter

  • É uma heurística que diz que um módulo não deve saber sobre o interior dos objetos que ele manipula.
  • O método não deve invocar métodos de objetos que são retornados por alguma das funções permitidas. Em outras palavras: converse com amigos, não com estranhos.
  • Train Wrecks: código com chamadas de funções encadeadas deve ser evitado.
    Ex: x = obj.getProp().getX().getY();
  • Hybrids: trazem o pior dos dois lados, pois se torna difícil adicionar novas funções assim como novas estruturas de dados.

Data Transfer Objects

  • Uma estrutura de dados é essencialmente uma classe com variáveis públicas e sem funções. Às vezes são chamadas de DTOs.
  • Active Record: forma especial de um DTO que tem métodos navegacionais como save e find. Tipicamente traduzem dados do banco de dados ou outras fontes.

Conclusion

  • Objetos expõem comportamento e escondem dados.
  • Estruturas de dados expõem dados e não possuem comportamento significativo.

7) Error Handling

  • Se o tratamento de erros obscurecer a lógica, ele está errado.

Use Exceptions Rather Than Return Codes

  • É fácil se esquecer do significado de códigos de erro, por isso exceções devem ser preferíveis.

Write Your Try-Catch-Finally Statement First

  • Blocos try são como transações: devem deixar o programa em um estado consistente, independente do que ocorre em seu corpo.

Use Unchecked Exceptions

  • Checked Exceptions violam o Open/Closed Principle, pois toda a pilha de chamadas deve repassar a exceção desde onde ela foi lançada até onde for tratada.
  • Seu uso pode ser útil em bibliotecas críticas, onde o tratamento deve ser realizado.

Provide Context With Exceptions

  • Cada exceção lançada deve prover contexto suficiente para determinar a fonte e o local do erro.
  • Crie mensagens de erro informativas.

Define Exception Classes In Terms of a Caller's Needs

  • A preocupação deve ser em como a exceção deve ser tratada pelo chamador.

Define the Normal Flow

  • Special Case Pattern: uma classe é criada ou um objeto é configurado de modo que ele lide com um caso especial. O comportamento fica encapsulado no objeto do caso especial.

Don't Return Null

  • Ao invés de retornar null, considere lançar uma exceção ou retornar um special case object.

Don't Pass Null

  • Evite passar null sempre que possível, pois nem sempre a função chamada possui o tratamento adequado.

8) Boundaries

Using Third-Party Code

  • Use código de terceiros de forma encapsulada, escondendo detalhes de implementação e facilitando a manutenção.

Exploring and Learning Boundaries

  • É uma boa prática escrever testes para código de terceiros.
  • Esses testes podem ser chamados de testes de aprendizado, pois os escrevemos conforme aprendemos a utilizar o código de terceiros, e o conhecimento desse código fica disponível nos testes de limites.

Learning Tests Are Better Than Free

  • Além de centralizar o conhecimento e facilitar o aprendizado, são úteis para verificar se uma nova versão da dependência não fez com que algo deixasse de funcionar.

Using Code That Does Not Yet Exist

  • Algumas vezes pode ser necessário escrever um código que use outro código que ainda não existe, como por exemplo uma interface que será escrita por outros. Isso pode fazer com que o design da própria interface seja melhor.

Clean Boundaries

  • Trechos de código nos limites precisam ter uma separação clara e testes que definem expectativas.

9) Unit Tests

The Three Laws of TDD

  • 1ª) Você não deve escrever código de produção até que você tenha escrito um teste unitário que falhe.
  • 2ª) Você não deve escrever mais de um teste unitário que seja suficiente para falhar, e não compilar é falhar.
  • 3ª) Você não deve escrever mais código de produção do que o suficiente para passar o teste atual que está falhando.

Keeping Tests Clean

  • Ter testes ruins é equivalente a não ter testes, se não for pior.
  • Testes unitários fazem com que nosso código seja flexível, manutenível e reusável.
  • Quanto maior a cobertura de testes, menor o medo de alterar o código.

Clean Tests

  • Readability é o principal.
  • Build-Operate-Check Pattern: constrói os dados do teste, opera nos dados de teste e verifica se a operação retornou os resultados esperados.
  • Código de testes não precisa ser tão eficiente quanto o de produção.

One Assert Per Test

  • O número de asserts em um teste deve ser minimizado e cada função de teste deve testar apenas um conceito.

F.I.R.S.T

  • Fast: testes devem rodar rapidamente.
  • Independent: testes não devem depender uns dos outros.
  • Repeatable: testes devem poder ser repetidos em qualquer ambiente.
  • Self-Validating: testes devem ter uma saída booleana: passou ou falhou.
  • Timely: testes unitários devem ser escritos exatamente antes do código de produção.

10) Classes

Class Organization

  • Seguindo a convenção padrão do Java, uma classe deve começar com public statics constants, private static variables, private instance variables, nesta ordem, deixando as public instance variables por último, se existirem.
  • Funções públicas devem seguir a lista de variáveis. Funções privadas devem estar logo após as funções públicas que as chamam.

Classes Should Be Small!

  • Classes devem ser pequenas, mas aqui a medida são as responsabilidades da mesma.
  • O nome da classe deve descrever quais responsabilidades ela tem. Deve ser conciso, pois se for ambíguo, pode indicar que há responsabilidades demais.
  • Devemos ser capazes de descrever uma classe com cerca de 25 palavras, sem usar as palavras "se", "e", "ou" ou "mas".
  • As palavras "Processor", "Manager" ou "Super" geralmente indicam uma agregação de responsabilidades.
The Single Responsibility Principle
  • Diz que uma classe ou módulo deve ter apenas uma razão para mudar (tendo, portanto, apenas uma responsabilidade).
  • Queremos que nossos sistemas sejam compostos de muitas classes pequenas ao invés de poucas grandes.
Cohesion
  • Classes devem ter um pequeno número de variáveis de instância. Cada método da classe deve manipular uma ou mais destas variáveis.
  • No geral, quanto mais variáveis um método manipula, mais coesivo este método é para sua classe. Uma classe onde cada variável é usada por cada método é maximamente coesiva.
Maintaining Cohesion Results in Many Small Classes
  • O ato de quebrar funções grandes em menores causa uma proliferação de classes.
  • Quando classes perderem coesão, separe-as.

12) Emergence

Getting Clean Via Emergent Design

Um design é simples se seguir as seguintes regras, em ordem de importância:

  • Executa todos os testes.
  • Não contém duplicação.
  • Expressa a intenção do programador.
  • Minimiza o número de classes e métodos.

Simple Design Rule 1: Runs All the Tests

  • Um sistema deve testar todas as suas funcionalidades e seus testes devem passar todas as vezes.
  • Um sistema que não pode ser verificado nunca deveria ser implantado.

Simples Design Rules 2-4: Refactoring

  • O fato de ter uma cobertura completa e testes elimina o medo de limpar o código através de refatorações.
  • Com a refatoração, podemos aumentar a coesão, diminuir o acoplamento, separar preocupações, modularizar preocupações de sistema, diminuir classes e funções, escolher nomes melhores, etc.

No duplication

  • Há as duplicações óbvias, onde as linhas de código são exatamente iguais, outras que são diferentes mas fazem o mesmo trabalho e ainda há aquelas que fazem algo muito parecido com outra parte do código.
  • A filosofia de reusar pequenas partes diminui drasticamente a complexidade do sistema.

Expressive

  • É fácil escrever código que nós entendemos, pois no momento em que o escrevemos estamos com um entendimento profundo sobre o problema em que estamos tentando resolver. Outros mantenedores do código, ou até nós mesmos em outro momento, não teremos tal entendimento profundo do problema.
  • A maior parte do custo de um software vem da manutenção de longo prazo. Para diminuir o potencial de introduzir defeitos com mudanças, é crítico que seja possível entender o que um sistema faz.
  • O código deve expressar claramente a intenção do autor. Quanto mais claro o código for, menos tempo outros vão ter que perder para entendê-lo. Isso reduz defeitos e diminui o custo da manutenção.
  • Modos de se expressar bem:
    • Escolhendo bons nomes, que reflitam da melhor maneira possível a resposabilidade da classe, método ou propriedade.
    • Mantendo classes e funções pequenas, pois normalmente serão mais fáceis de nomear, escrever e entender.
    • Usando padrões de nomenclatura.
    • Criando testes unitários bem escritos.
    • O modo mais importante de ser expressivo é TENTAR. Com muita frequência nós deixamos nosso código funcionando e então vamos para o próximo problema sem ter pensado o suficiente em como deixar o código ser fácil de ler para a próxima pessoa. Lembre-se: normalmente a próxima pessoa a ler este código será você.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment