Escreva Código Para Ser Lido

Edward Loveall
Traduzido por Matheus Sales
Este artigo também está disponível em: English

O código é lido mais vezes que escrito. Nós o escrevemos uma vez e depois o lemos novamente. Ele passa por revisão, se estamos em uma equipe, e é lido novamente quando outra pessoa precisa entender, adicionar ou modificar esse código. Isso inclui nós mesmos, semanas ou meses depois.

Apesar disso, tendemos a focar na escrita como a principal “ação”. A escrita é muito importante, mas antes de escrever, precisamos entender o contexto. Devemos ler antes de escrever. É muito mais fácil entender o código se ele for bem escrito. Mesmo em situações em que escrevemos muito código, como uma nova codebase, eventualmente precisamos voltar e ler nossos primeiros passos. Devemos otimizar o código para ser lido.

Nomes

Nomes descrevem o que variáveis, métodos e classes são ou o que fazem. Eles esboçam o sistema com o qual estamos trabalhando. É mais fácil escrever nomes concisos. Eles mantêm nossas linhas pequenas e facilitam a digitação desses nomes repetidamente. Por exemplo, cc = CreditCard.find em vez de primary_card, ou def set_attr em vez de set_user_profile_attribute.

O problema é que nomes não descritivos como cc ou set_attr requerem mais investigação para descobrir o que são e como devem ser usados. Esses exemplos favorecem a escrita fácil, não a leitura.

Considere os conceitos que você aprendeu para escrever código e tente utilizar isso nos nomes. Considere o porquê ou como algo é usado em vez do que ele é. initial_sign_up_profile diz muito mais do que profile, e lock_stats_table_for_data_export diz mais do que lock_db.

A legibilidade é o objetivo, não o tamanho do nome. Você pode criar código ilegível com nomes longos, especialmente muitos nomes longos bastante semelhantes. Favoreça a legibilidade, não alguma métrica de comprimento arbitrária.

Abstraindo Lógica Procedural

O código que escrevemos para o funcionamento de um sistema é diferente da maneira como descrevemos seu funcionamento. Imagine o processo de “mostrar dialog modal”. É assim que o descreveríamos, mesmo para colegas proficientes em código. Não costumamos descrever isso como “encontrar o elemento DOM relacionado apropriado e definir as classes CSS para que ele seja visível”, mas é nesse nível de abstração que o código funciona. É nosso trabalho traduzir entre esses níveis de abstração.

Quando você tem um método longo, a correção clássica é o Extract Method. O Extract Method funciona dividindo nosso código não otimizado em abstrações nomeadas que representam a lógica subjacente. Novamente, estamos voltando aos nomes, mas com um objetivo um pouco diferente. Um bom nome permite que você descreva a funcionalidade de uma forma que não exija que o usuário conheça cada parte interna do sistema. Isso permite que eles aprendam (ou reaprendam) os detalhes mais complexos conforme necessário.

Aqui está um exemplo de como mostrar um modal com JavaScript:

function async showModal(event) {
  const target = event.target;
  const modal = document.querySelector(
    event.target.dataset.relatedModalSelector
  );
  if (!modal || !modal.classList.contains("modal")) {
    return;
  }

  for (const element of document.querySelectorAll("modal")) {
    element.classList.add("hidden");
  }

  const data = modal.dataset;
  const modalTitle = JSON.parse(data.display)["title"];
  const modalContent = await fetchModalData(data.remoteUrl);
  modal.innerHtml = modalContent;
  modal.classList.remove("hidden");
}

Se você já está familiarizado com o funcionamento do sistema de modais, ler o código apresentado não será um problema. No entanto, é importante lembrar que a maioria das pessoas não possui essas informações sempre em mente. Abstrair a lógica procedural ajudará qualquer pessoa que esteja visualizando esse código pela primeira vez a entender onde precisará fazer alterações.

function async showModal(event) {
  const modal = this.findPossibleModal(event);
  if (!this.isValidModal(modal)) {
    return;
  }

  await this.setModalContent(modal);
  this.hideEveryModal();
  this.revealModal(modal)
}

A refatoração faz com que os passos para exibir o modal sejam claros e facilmente compreensíveis. Se precisarmos, podemos encontrar os detalhes da implementação em métodos extraídos, e fica imediatamente claro o que cada método está fazendo. Todos os metodos tem um nível de abstração semelhante; neste caso, manipulando elementos DOM relacionados. O método showModal é tambem uma abstração, que existe com abstrações em um nível similar. É fácil imaginar outras possiveis interações como submitForm, syncUserProgress ou enableFocusMode.

Testes

Ao testar, é relativamente comum isolar a fase de setup do restante do teste usando abstrações como o let ou before. Muitos testes no mesmo arquivo exigem contextos semelhantes (ou iguais) para serem executados, então consolidar essa configuração parece ser uma maneira natural de evitar repetições em um teste (DRY). Agrupar código relacionado também pode se assemelhar a criar abstrações.

No entanto, isso torna os testes mais difíceis de ler. O setup do código define o estado do sistema. Na maioria das vezes, não trabalhamos com esses testes recentemente ou talvez nunca tenhamos trabalhado com eles antes. Essas partes do setup são fundamentais para entender como corrigir testes existentes ou adicionar mais. Um teste separado de seu contexto nos obriga a memorizar esse contexto, o que distrai nossas habilidades de resolução de problemas. Um bom teste conta uma história.

A maioria dos testes também testa um sistema em vários estados; nenhum setup único pode representar todos os cenários. Na melhor das hipóteses, o setup compartilhado terá que ser redefinido para testes individuais, espalhando esse contexto. Na pior das hipóteses, o setup é totalmente desperdiçado, já que o setup global não é utilizado. Quando colocamos o setup compartilhado no topo, estamos assumindo que todos os testes futuros precisarão desse mesmo setup. Escreva mais alguns testes e essa suposição provavelmente será falsa, fazendo com que tenhamos que reorganizar todo o arquivo ou simplesmente lidar com o desperdício.

Manter todo esse setup no próprio teste torna-o muito mais legível, apesar de não obedecer ao DRY, mas DRY não é muito útil nos testes. Não precisamos que os testes sejam baseados em abstrações reutilizáveis e tenham poucas linhas. Precisamos que os testes nos proporcionem confiança previsível em nosso sistema e nos ajudem a refatorar.

Objetivos mais amplos

Vale lembrar que métricas específicas como complexidade de código, cobertura de teste e DRY não são objetivos por si só. O objetivo é ter um código que possamos entender facilmente e alterar com confiança para fornecer aos usuários o melhor software possível. Embora “legibilidade” seja mais difícil de medir, tê-la como um princípio orientador pode nos ajudar a saber quando flexibilizar ou quebrar essas regras quantitativas e construir um software melhor.