Principles

Escopo: transversal. Aplica-se a qualquer linguagem ou stack do projeto.

SSOT (Single Source of Truth · fonte centralizada da verdade): a documentação de cada linguagem traduz estes princípios para o idioma dela. Em caso de conflito, este documento prevalece.

Princípios são critérios de avaliação. Eles respondem à pergunta "esse código está bem escrito?" antes de qualquer ferramenta automática entrar em cena.

A lista está organizada como checklist de revisão, do mais impactante ao mais granular:

  • Forma: a estrutura da função avaliada de fora para dentro
  • Legibilidade: fluxo, espaçamento e nomes lidos linha a linha
  • Controle de qualidade: as garantias de robustez: estado, erros, I/O (Input/Output · Entrada/Saída) e testes

Conceitos fundamentais

ConceitoO que é
SSOT (Single Source of Truth · fonte centralizada da verdade)Uma única fonte autoritativa de informação; documentações por linguagem aplicam, não redefinem
SLA (Single Level of Abstraction · Único Nível de Abstração)Cada função opera em apenas um nível de detalhe: ou coordena ou implementa
CQS (Command-Query Separation · Separação de Comando e Consulta)Funções que retornam valor não produzem efeitos colaterais; as que produzem efeitos retornam void
Caller (quem chama a função)Código que invoca uma função e é responsável por garantir o contrato de entrada
Guard clause (cláusula de guarda)Verificação antecipada no topo da função que elimina casos inválidos antes da lógica principal
AAA (Arrange, Act, Assert · Arranjar, Agir, Atestar)Estrutura de três fases para testes: contexto, execução e verificação
I/O (Input/Output · entrada/saída)Operações que leem ou escrevem em sistemas externos: banco, rede, disco

Forma

A forma de uma função é o que você avalia antes de ler o corpo. Quando a estrutura está clara, o restante costuma acompanhar.

Escrita em inglês

Código escrito em inglês é universal: funciona em qualquer equipe, repositório ou ferramenta. Nomes em português criam ruído na busca, na leitura de documentação técnica e na integração com código externo. A regra: identificadores em inglês, comentários e documentação no idioma da equipe.

Código narrativo

Um bom código conta uma história. Você lê a função de cima para baixo e entende o que acontece sem comentário nenhum guiando a leitura. Quando um comentário é necessário para explicar o que o código faz, o código pede um nome melhor ou uma decomposição.

Ponto de entrada limpo

O caller (quem chama a função) expressa o quê. O como fica dentro da função: a construção de contexto e a montagem de parâmetros acontecem lá, e a chamada permanece de uma linha, com um argumento claro.

Estilo vertical

Até 3 parâmetros podem ficar na mesma linha. Com 4 ou mais, use um objeto: cada campo ganha nome na chamada e a intenção fica explícita sem consultar a assinatura da função.

Orquestrador no topo

A função que coordena o fluxo fica visível antes das que implementam os detalhes. Você lê a intenção no topo (buscar, transformar, persistir) e encontra os detalhes abaixo, como funções auxiliares. Essa ordem se chama top-down (de cima para baixo).

Detalhes abaixo

Funções auxiliares ficam abaixo do orquestrador, nunca acima. Essa é a step-down rule (regra de descida): você lê o nível mais alto primeiro e desce apenas quando quer entender a implementação, sem interromper a leitura linear.

Sem lógica no retorno

O return nomeia o resultado. Uma variável expressiva antes dele deixa claro o que a função produz, em simetria com o que ela recebeu. Lógica inline no return esconde a intenção e impede inspecionar o valor antes de devolvê-lo.


Legibilidade

Legibilidade é o que você avalia ao ler o corpo da função linha a linha: como o fluxo se move, como o espaço está distribuído e se os nomes carregam significado.

Retorno antecipado

Guard clauses (cláusulas de guarda) no topo da função eliminam os casos inválidos antes de qualquer lógica de negócio, e o fluxo principal fica livre de aninhamento. A leitura fica linear: casos especiais saem cedo, o caminho feliz segue em frente.

Fluxo linear

Condicionais aninhadas em cascata, o chamado arrow antipattern (antipadrão de seta), criam profundidade desnecessária. Guard clauses transformam if (valido) { ... } em if (invalido) return e mantêm o fluxo reto, sem indentar o caminho principal.

Baixa densidade visual

Linhas relacionadas ficam juntas. Grupos distintos são separados por exatamente uma linha em branco. Duas linhas em branco viram ruído, do mesmo modo que a ausência de respiro. O olho identifica os parágrafos do código sem precisar ler cada linha.

Nomes expressivos

Um bom nome dispensa explicação. activeUsers diz mais que list. invoiceTotal diz mais que value. O nome carrega o tipo, a intenção e o contexto: quanto mais preciso, menos o leitor precisa rastrear de onde o valor veio.

Código como documentação

Nomes substituem comentários. Um comentário que explica o quê o código faz indica que o código pode ser renomeado ou decomposto. Comentários que explicam por quê uma decisão foi tomada são válidos. Os que descrevem o óbvio envelhecem mal e mentem assim que o código muda sem o comentário mudar junto.

Sem valores mágicos

Números e strings literais espalhados pelo código não dizem nada sobre a intenção. 0.1 pode ser uma taxa, um limiar ou um fallback, e só o contexto revela qual. Constantes nomeadas transformam valores opacos em intenção explícita e centralizam a manutenção em um único lugar.


Controle de qualidade

Controle de qualidade é o que você avalia nas propriedades de robustez do código: como ele lida com estado, falhas, operações assíncronas e verificação de comportamento.

Funções pequenas

Uma função faz uma coisa. Esse é o SLA (Single Level of Abstraction · Único Nível de Abstração): o orquestrador coordena, a implementação executa, e uma função sozinha nunca acumula os dois papéis. Funções pequenas são fáceis de nomear, testar e reutilizar.

Cálculo e formatação

Computar dados e formatar a saída são responsabilidades distintas. Uma função que calcula totais deixa a montagem da string de exibição para outra. Separar as duas torna cada parte testável de forma independente e reutilizável em contextos diferentes.

Valor fixo por padrão

Variáveis declaradas como constantes (const, readonly) comunicam que o valor não muda: qualquer alteração posterior passa a ser uma exceção explícita. Immutable (valor fixo) por padrão reduz surpresas e torna o fluxo de dados rastreável.

CQS

Uma função ou retorna um valor (query, consulta) ou produz um efeito colateral (command, comando), sempre um dos dois. Funções que mudam estado e retornam dados acoplam leitura e escrita de forma implícita, o que dificulta rastrear e testar.

Dependências explícitas

Dependências recebidas como parâmetros são visíveis, substituíveis e testáveis. Dependências buscadas dentro da função via estado global, singleton (instância única global) ou service locator (localizador de serviços) ficam ocultas e acopladas. Injetar via parâmetros é a forma mais simples de tornar o comportamento previsível.

Falhar rápido

Validar entradas no início da função e interromper o fluxo assim que algo está errado impede que dados inválidos se propaguem. Quanto mais cedo a falha aparece, mais fácil ela é de diagnosticar e menor o efeito colateral que ela gera.

Retorno explícito

Exceções existem para erros inesperados: falhas de infraestrutura, estados impossíveis, bugs. Usá-las para controlar o fluxo de negócio mistura as duas responsabilidades e deixa o fluxo opaco para quem lê. Retornos explícitos com tipos de resultado deixam os caminhos possíveis visíveis na assinatura.

Contratos consistentes

Toda resposta da função segue o mesmo formato, sucesso e erro incluídos. Um contrato previsível elimina verificações defensivas em cada chamada e permite que o caller trate os casos de forma uniforme.

Tratamento centralizado de erros

Erros são capturados nos limites do sistema, nas camadas de entrada da aplicação. Centralizar o tratamento evita duplicação, garante formato consistente nas respostas e mantém o código de domínio livre de preocupações de infraestrutura.

I/O assíncrono

Operações de I/O (leitura de banco, chamadas de rede, acesso a disco) bloqueiam o processamento quando feitas de forma síncrona. async/await (assíncrono/aguardar) torna essas operações não-bloqueantes: a execução continua enquanto aguarda a resposta, e o processo inteiro segue livre.

Testes estruturados

O padrão AAA (Arrange, Act, Assert · Arranjar, Agir, Atestar) divide cada teste em três fases explícitas. A preparação monta o contexto. A execução chama o comportamento. A verificação confirma o resultado com variáveis nomeadas, sem expressões inline no assert. Testes estruturados são legíveis como especificações do comportamento esperado.

DoDocs v3.7.0 · Desenvolvido por @thiagocajadev · Baseado no trabalho de pmndrs/docs · Poimandres.