Pular para o conteúdo

Estrutura e Formatação

Formatação não é maquiagem. Em código limpo, forma e conteúdo trabalham juntos: a lógica resolve o problema, e a estrutura visual reduz o custo de leitura. Como a maior parte do tempo em software é gasta entendendo código existente, a formatação passa a ser uma ferramenta de comunicação técnica.

No dia a dia, isso significa que um arquivo bem organizado ajuda a responder rapidamente perguntas como:

  • onde está a entrada principal desta classe?
  • quais funções dependem umas das outras?
  • onde começa e termina um conceito?
  • este trecho faz parte da mesma ideia ou mudou de assunto?

A principal função da formatação é tornar a intenção do código mais evidente. Mesmo quando regras de negócio mudam, a necessidade de leitura permanece. Por isso, uma boa formatação sobrevive mais tempo do que uma implementação específica.

Quando a estrutura visual é ruim, surgem efeitos comuns:

  • dificuldade para localizar responsabilidades;
  • aumento de erros em manutenção;
  • mais esforço em code review;
  • sensação de que o código é maior e mais complexo do que realmente é.

Uma boa metáfora é a do jornal: primeiro vem a manchete, depois um resumo, e só então os detalhes. Em uma classe ou módulo, o topo deve apresentar o assunto principal; o restante aprofunda a implementação.

Uma ordem de leitura saudável costuma ser:

  1. declaração da classe ou módulo;
  2. métodos públicos principais;
  3. métodos auxiliares privados;
  4. detalhes de apoio.

Isso permite leitura top-down: quem abre o arquivo entende primeiro o comportamento geral e desce aos detalhes só quando precisa.

Arquivos menores tendem a ser mais legíveis porque preservam visão global. Isso não significa fragmentar tudo artificialmente, mas sim evitar arquivos que acumulam responsabilidades demais.

Sinais de arquivo excessivo:

  • contém regras de negócio, acesso a dados e formatação de interface no mesmo lugar;
  • exige muita rolagem para localizar um método específico;
  • precisa de comentários-bandeira para organizar seções gigantes;
  • concentra mudanças frequentes de naturezas diferentes.

Em geral, se um arquivo ficou grande demais para ser “lido como história”, ele provavelmente precisa ser dividido.

Linhas em branco separam ideias. Sem esse respiro visual, tudo parece pertencer ao mesmo bloco conceitual. Com separação excessiva, relações importantes se perdem.

Uma boa regra prática:

  • separe blocos que tratam de conceitos diferentes;
  • mantenha juntos trechos que participam da mesma ideia;
  • evite linhas em branco aleatórias sem função semântica.

Exemplo:

import db from 'db';
const baseUrl = 'https://api.example.com';
function getUser(id) {
const user = db.get(id);
return user;
}
function setUser(user) {
db.set(user);
}

Aqui, imports, constantes e funções ficam visualmente separados porque pertencem a categorias diferentes.

Se conceitos diferentes devem ser separados, conceitos relacionados devem ficar juntos. Essa continuidade ajuda a evitar que o leitor precise “remontar” mentalmente uma operação dispersa pelo arquivo.

Por exemplo, dentro de uma função curta, instruções que fazem parte do mesmo raciocínio devem aparecer sem interrupções arbitrárias. Inserir espaços no meio de uma sequência lógica transmite a falsa impressão de mudança de contexto.

Elementos que se relacionam fortemente devem ficar próximos. Isso vale especialmente para:

  • declaração de variável e seu primeiro uso;
  • método chamador e método auxiliar diretamente relacionado;
  • atributo e métodos que o manipulam;
  • interfaces e implementações locais fortemente associadas.

Variáveis locais devem ser declaradas perto de onde são usadas pela primeira vez. Isso reduz busca visual e diminui o tempo em que a variável precisa ser mantida na memória do leitor.

Ruim:

function createMainElement(tag) {
const element = document.createElement(tag);
const mainElement = document.querySelector('.main-element');
element.classList.add('new-class');
mainElement.appendChild(element);
return mainElement;
}

Melhor:

function createMainElement(tag) {
const element = document.createElement(tag);
element.classList.add('new-class');
const mainElement = document.querySelector('.main-element');
mainElement.appendChild(element);
return mainElement;
}

Em orientação a objetos, atributos devem seguir convenção estável. O mais importante não é uma regra universal, mas previsibilidade: quem lê precisa saber onde procurar o estado da classe.

Em Java, o padrão mais comum é declarar atributos logo no início da classe, antes dos construtores e métodos. Isso reduz dispersão e facilita a leitura estrutural.

Quando uma função chama outra que faz parte do mesmo fluxo de leitura, a declaração da função auxiliar deve aparecer logo abaixo, sempre que possível. Isso preserva a ideia de narrativa top-down.

Exemplo:

function makeRequest(url) {
const response = fetch(url);
return normalizeResponse(response);
}
function fetch(url) {
const resolvedUrl = resolveUrl(url);
return httpClient.get(resolvedUrl);
}
function resolveUrl(url) {
return `/api/${url}`;
}

O leitor acompanha o fluxo sem saltos desnecessários.

Além da proximidade por chamada, existe proximidade por assunto. Funções que tratam autenticação, por exemplo, devem ficar perto umas das outras; o mesmo vale para constantes relacionadas, adaptadores, mapeamentos e validadores.

Essa organização reduz ruído e melhora descoberta. Em alguns contextos, a ordem alfabética ajuda; em outros, a ordem por fluxo de execução é melhor. O importante é escolher um critério coerente.

Há duas estratégias clássicas:

  • top-down: métodos de alto nível primeiro, detalhes abaixo;
  • bottom-up: utilidades primeiro, composições depois.

Para código de aplicação e ensino, top-down costuma funcionar melhor porque favorece leitura progressiva. Já em bibliotecas utilitárias, outra ordem pode fazer sentido. O critério deve servir à compreensão do módulo, não ao gosto pessoal.

Enquanto a formatação vertical organiza o fluxo entre ideias, a horizontal organiza a leitura dentro da linha. Linhas muito longas escondem intenção porque exigem rolagem ou obrigam o leitor a perder contexto visual.

Uma regra prática comum é manter linhas entre 80 e 120 caracteres. O valor exato pode variar conforme a linguagem, o time e as ferramentas, mas o objetivo permanece: permitir leitura confortável sem deformar a estrutura do código.

Quando uma linha fica longa demais, soluções preferíveis incluem:

  • extrair variáveis intermediárias com nomes significativos;
  • quebrar expressões complexas em funções menores;
  • usar encadeamento formatado em múltiplas linhas;
  • evitar nomes excessivamente prolixos em contextos já claros.

Espaço em branco dentro da linha ajuda a indicar relação semântica. Operadores costumam receber espaços ao redor porque conectam operandos; chamadas de função não devem ser separadas do parêntese porque nome e argumento fazem parte da mesma unidade.

Ruim:

function soma (a, b){
return a+b ;
}
soma (1 , 2);

Melhor:

function soma(a, b) {
return a + b;
}
soma(1, 2);

Repare que a regra não é “espalhar espaços”, mas comunicar ligação e separação corretamente.

Alguns times alinham colunas manualmente para variáveis, tipos ou atribuições. Embora visualmente atraente em alguns casos, esse alinhamento costuma deteriorar rápido: basta mudar um nome para todo o bloco perder consistência.

Por isso, em código de manutenção contínua, é melhor privilegiar indentação consistente e regras automáticas de formatter em vez de alinhamentos manuais frágeis.

Indentação explicita hierarquia. Ela mostra onde um bloco começa, onde termina e qual trecho depende de qual condição, laço ou escopo.

Exemplo:

class Pessoa {
nome: string;
idade: number;
altura: number;
constructor(nome: string, idade: number, altura: number) {
this.nome = nome;
this.idade = idade;
if (altura > 0) {
this.altura = altura;
} else {
this.altura = 0;
}
}
}

Sem indentação, a estrutura lógica desaparece. Como regra, não vale a pena discutir “na mão” quantos espaços usar em cada caso; vale a pena padronizar e automatizar.

Alguns blocos são tão simples que cabem naturalmente em uma linha, como lambdas curtas ou retornos diretos. Isso pode melhorar a legibilidade desde que duas condições sejam respeitadas:

  • a expressão continue clara;
  • o limite horizontal não seja sacrificado.

Exemplo:

lista.map(item => item.id);

Se a expressão começar a crescer, é melhor voltar para múltiplas linhas ou extrair uma função nomeada.

Formatação é também acordo social. Um código excelente mas formatado de maneira incompatível com o restante do projeto gera atrito em revisão, histórico de commits e manutenção colaborativa.

Por isso, equipes precisam definir:

  • estilo-base adotado;
  • limites de linha;
  • indentação;
  • convenções de ordem de membros da classe;
  • uso de formatter e linter automáticos.

Nesse ponto, consistência vale mais que preferência individual. O melhor estilo é aquele que todo o time consegue aplicar sem esforço manual.

Antes de considerar um arquivo bem formatado, vale verificar:

  • o topo mostra rapidamente a responsabilidade principal?
  • métodos auxiliares ficam próximos de quem os chama?
  • há separação visual entre conceitos distintos?
  • linhas longas demais foram simplificadas?
  • a indentação revela claramente a hierarquia?
  • a organização segue o padrão do projeto?