melhores práticas para escrever código limpo em JavaScript
Compartilhar:

JavaScript é uma das linguagens de programação mais populares do mundo. Com sua ampla utilização em diversas aplicações, é importante garantir que o código seja bem escrito, legível e fácil de manter. Escrever código limpo em JavaScript pode parecer um desafio, mas é uma prática essencial para melhorar a qualidade do seu trabalho e a eficiência da equipe de desenvolvimento. Neste artigo, exploraremos as melhores práticas para escrever código limpo em JavaScript.

🚨DICA: Crie um projeto de programação web com HTML5CSS3 JavaScript do zero em 3 aulas: MiniCurso Gratuito! 🚀

Leia o livro: Código limpo: Habilidades práticas do Agile Software

Edição Português

1. Atribua nomes a variáveis ​​e funções.

Os nomes são importantes e as variáveis ​​e funções com nomes descritivos podem ajudar os leitores a entender seu código sem entrar em detalhes.

fullstack javascript onebitcode
// Ruim 😕
const CorAlvo = "roxo";
const fnd = true;
const lista = ["azul", "verde", "amarelo"];
const obj = { cor: "azul", hex: "#800080" };

if (validarSalvar(obj)) {
bancoDeDados.salvar(obj);
}

if (existe) {
// ...
}

// Melhor 😀
const corAlvo = "roxo"; /* notação camelCase para definir variáveis, funções e
métodos em JavaScript */
const corEncontrada = false;
const cores = ["azul", "verde", "amarelo"];
const cor = { nome: "azul", hex: "#800080" };
const ehCorValida = validarCor(cor.hex);

if (ehCorValida) {
bancoDeDados.salvar(cor);
}

if (existeCor) {
// ...
}
// ...

Evite gírias, abreviações e falhas de comunicação.

// Ruim 😕
user.goAway(); // gíria
save(n); // abreviação comum
allBooks = books.filter(book => {...}); // falsa representação

// Melhor 😀
user.remover();
save(user);
livrosFiltrados = books.filter(livro => {...}); /* Alternativamente, um nome
diferente com base nos critérios de filtragem */

Não inclua informações redundantes no nome. Só ocupa mais espaço e faz mais barulho.

// Ruim 😕
const colorWithHexCode = Color("red", "#ff0000");

const book = {
bookTitle: "Misery",
bookId: 23423,
bookAuthor: "Stephen King",
bookISBN: "978-0-670-81364-3",
};

// Melhor 😀
const cor = Color("red", "#ff0000"); /* É simples inferir que essa cor tem um nome
e um código hexadeciamal, mesmo sem conhecer a descrição da classe. */

const livro = {
titulo: "Misery",
id: 23423,
autor: "Stephen King",
isbn: "978-0-670-81364-3",
}; /* Utilizar nomes mais simples e claros para os atributos do objeto, sem
repetições desnecessárias de palavras, torna o código mais legível e fácil de
entender. */

Devemos nomear coisas relacionadas ao domínio da solução. Qualquer coisa que um desenvolvedor entenda, como um banco de dados, um MessageBus ou algo que descreva o problema que estamos tentando resolver.

Leia também: As 5 melhores placas de vídeo em 2023

2. Funções e métodos devem ser concisos em todos os aspectos

Chamar ou interagir com funções deve ser simples e compreensível. O número, a ordem e o comprimento dos corpos funcionais são importantes! Mantenha o número de parâmetros pequeno. Uma função preferencialmente tem dois ou menos parâmetros.

// Ruim 😕
const getEmployeeRecord = (id, sector, startDate, endDate) => {
// ...
};
// Melhor 😀
// Opção 1:
const getEmployeeRecord = ({ id, sector, startDate, endDate }) => {
// passando como um objeto
// ...
};
// Opção 2:
/* Podemos enviar um número infinito de parâmetros para nossa função usando o
parâmetro rest (...), que tem a mesma sintaxe do operador spread. */

function getEmployeeRecord(...args) {
[id, sector, startDate, endDate] = args; // args é um array
console.log(sector); // logs 'IT'
}

// Caso de uso:
// Opção 1:
const registro = {
id: 1213,
setor: "TI",
dataInicio: "01-01-2020",
dataFim: "10-01-2021",
};
getEmployeeRecord(registro);

// Opção 2:
getEmployeeRecord(1213, "TI", "01-01-2020", "10-01-2021"); /* Utilizar
parâmetros com nomes descritivos e coesos torna o código mais legível e fácil de
entender. Além disso, a segunda opção exemplifica o uso do parâmetro rest, que
pode ser útil em casos em que o número de parâmetros pode variar. */

Em vez de condições, use parâmetros padrão.

Banner Amazon Notebook 300x300
// Ruim 😕
const logError = (err) => {
const error = err || "Algo deu errado";
console.log(error);
};

// Melhor 😀
const logError = (err = "Algo deu errado") => console.log(err);

// Caso de uso
logError(); // logs 'Algo deu errado'

const mensagemTeste = "usuário não encontrado";
logError(mensagemTeste); // logs "usuário não encontrado"

/* Utilizar valores padrão para parâmetros de função pode simplificar o código e
torná-lo mais legível. Na primeira opção, é necessário fazer a verificação do
valor passado para o parâmetro err antes de utilizá-lo, o que pode ser um pouco
confuso. Já na segunda opção, o valor padrão é definido diretamente na
assinatura da função, o que torna o código mais simples e fácil de entender. */

Níveis de abstração importam

Uma função só deve executar um trabalho que seja um nível menos abstrato que seu nome. Ajuda a reduzir o tamanho e a complexidade das funções, o que simplifica o teste e a depuração.

Abstração de alto nível: operações em que pedimos para fazer algo sem nos importar como é feito, tornando-as mais fáceis de entender e usar. Por exemplo, “isValidPassword(senha)”.

Abstração de baixo nível: detalhar as operações para definir os detalhes de implementação de como algo funciona. Por exemplo, “password. length > 8” é usado diretamente.

O objetivo é introduzir operações de baixo nível onde a interpretação é adicionada ao nome da função.

// Ruim 😕
/* Este método deveria armazenar um livro, mas o nome não acrescenta
nenhum significado para a validação de baixo nível que segue. */

function saveBook({isbn, title}){
if(isbn.length != 13 || title.trim() === ''){
console.log("Não foi possível adicionar o livro, entrada inválida");
}
else {
const book = new Book(book);
book.save();
}
}

// Melhor 😀
function isValidBook(title,isbn){
return isbn.length === 13 && title.trim() !== '';
}

function validateBook({title,isbn}){
if(!isValidBook(isbn,title)){
throw new Error('Não foi possível adicionar o livro, entrada inválida');
}
}

function saveBook(book){
validateBook(book);
const book = new Book(book);
book.save();
}

Fique SECO (não se repita)

// Ruim 😕
async function getPostsForUser(userId) {
const userResponse = await fetch(/api/user/${userId}, {
token: token,
});
const user = userResponse.json();
const postResponse = await fetch(/api/posts/${user.authorId});
return postResponse.json();
}

async function getCommentsForUser(userId) {
const userResponse = await fetch(/api/users/${userId}, {
token: token,
});
const user = userResponse.json();
const commentResponse = await fetch(/api/comments/${user.commentId});
return commentResponse.json();
}

// Melhor 😀
async function getUser(userId) {
const response = await fetch(/api/v2/user/${userId}, {
token: token,
});
return response.json();
}

async function getPostsForUser(userId) {
const user = await getUser(userId);
const response = await fetch(/api/posts/${user.authorId});
return response.json();
}

async function getCommentsForUser(userId) {
const user = await getUser(userId);
const response = await fetch(/api/comments/${user.commentId});
return response.json();
}

Alguns conselhos de divisão de código: Não divida seu código se: Encontrar novas funções leva mais tempo do que ler funções extraídas. Não foi possível encontrar um nome adequado para a função extraída.

3. Estrutura do código – a aparência é importante

Substituir condições aninhadas por cláusulas de guarda. Uma cláusula de guarda é uma verificação que causa uma saída imediata de uma função, uma instrução de retorno ou uma exceção.

// Ruim 😕
function getTicketPrices() {
let prices;
if (isTravelByAir) {
prices = airFares();
} else {
if (isTravelByRail) {
prices = railFares();
} else {
if (isTravelByShip) {
prices = shipFares();
} else {
prices = carFares();
}
}
}
return prices;
}

// Melhor 😀
function getTicketPrices() {
if (isTravelByAir) return airFares();
if (isTravelByRail) return railFares();
if (isTravelByShip) return shipFares();
return carFares();
}

Limpe as instruções switch/if com o Maps

Se você tiver quaisquer instruções switch/if futuras que devam crescer, considere convertê-las em pares chave/valor.

Banner Amazon Livros de Programação 300x300
// Ruim 😕
const getServerUrl = (env) => {
switch (env) {
case "prod":
return "prod.myweb.com";
case "test":
return "test.myweb.com";
case "staging":
return "staging.myweb.com";
default:
return "https://localhost:3000";
}
};

// Melhor 😀
const serverUrls = {
prod: "prod.myweb.com",
test: "test.myweb.com",
staging: "staging.myweb.com",
};

const getServerUrl = (env) => serverUrls[env] || "https://localhost:3000";

4. Use os recursos do ES6

Destruição de objetos

const employee = {
name: "Jack",
age: 25,
department: {
team: "alpha",
role: "Desenvolvedor",
},
};
// ES5 😄
const name = employee.name;
const age = employee.age;
const role = employee.department.role;
const team = employee.department.team;
let manager = employee.department.manager;

if (!manager) { // undefined
manager = "haley"; //setando o valor padrão;
}
console.log(name, age, team, role, manager); // logs: Jack 25 alpha Desenvolvedor haley

// ES6 😊
const { name, age, department: { team, role, manager = 'haley' }} = employee;
console.log(name, age, team, role, manager); // logs: Jack 25 alpha Desenvolvedor haley

Modelo Literais

// ES5 😄
const email = "[email protected]";
const error = "usuário com o email" + " " + email + "não existe";

// ES6 😊
const email = "[email protected]";
const error = usuário com o email ${email} não existe;

Variedade

//Antes
if (mode === "crédito" || mode === "débito" || mode === "dinheiro")
return algo;
// Depois
if (["crédito", "débito", "dinheiro"].includes(payment)) return algo;

//Antes
let flores = ["🌹", "🌻", "🍁"];
let rosa = flores[0];
let girassol = flores[1];
let folhaDeMaple = flores[2];
//Depois
let [rosa, girassol, folhaDeMaple] = flores;

5. Comentários e Formatação

A formatação do código melhora a legibilidade e transmite significado. Existem dois formatos:

Formatação vertical: incluindo espaçamento entre linhas e agrupamento de código.

Duas diretrizes gerais: conceitos relacionados devem ser mantidos juntos e conceitos diferentes devem ser mantidos separados.

// Ruim 😕
const user = {id: 23231242,agentId: "cwwesf4f"};
const today = Date.now();
const bookSpaAndNotifyUser = async (spaInfo) => {
if (!findUser(user.id)) {
showErrorMessage('Cant find the user');
}
try {
const confirmationId = await processSpaBooking(user, tripInfo);
sendSpaConfirmationEmail(user, confirmationId);
}
catch (error) {
showErrorMessage(Failed to book a spa ${error});
}
}
const caclulateTotalPrice = () => {
// ...
}
const showErrorMessage = (error = 'something went wrong') => {
throw new Error(error);
}
const processSpaBooking = (user, tripInfo) => {
// ...
findStaff();
calculateTotalPrice();
}
const sendSpaConfirmationEmail = (user, confirmationId) => {
// ...
}
const findStaff = () => {
// ...
}
const caclulateTotalPrice = () => {
// ...
}
const sendSpaConfirmationEmail = (user, confirmationId) => {
// ...
}

// Melhorado 😀
const user = { id: 23231242, agentId: "cwwesf4f" };
const today = Date.now();

const bookSpaAndNotifyUser = async (spaInfo) => {
if (!findUser(user.id)) {
showErrorMessage("Cant find the user");
}
try {
const confirmationId = await processSpaBooking(user, tripInfo);
sendSpaConfirmationEmail(user, confirmationId);
} catch (error) {
showErrorMessage(Failed to book a spa ${error});
}
};

const showErrorMessage = (error = "something went wrong") => {
throw new Error(error);
};

const processSpaBooking = (user, tripInfo) => {
// ...
findStaff();
calculateTotalPrice();
};

const findStaff = () => {
// ...
};

const calculateTotalPrice = () => {
// ...
};

const sendSpaConfirmationEmail = (user, confirmationId) => {
// ...
};

Layout horizontal: incluindo recuo, espaçamento de código, largura de código

// Ruim 😕
const printUserData = ()=>{if(users.length > 0){for(let user of users){console.log(user)}}};

async function getconfigFile() {
try { // o nome da variável é muito longo, e a linha está sendo truncada
const lastYearConfigFileFromFolder = await fs.readFile('/Users/admin/folder/year/2021/config.txt', { encoding: 'utf8' });
} catch (err) {
console.log(err);
}
}

// Melhor 😀
const printUserData = () => {
if (users.length > 0) {
for (let user of users) {
console.log(user);
}
}
};

async function getconfigFile() {
try {
const arquivoDeConfiguracao = await fs.readFile(
"/admin/pasta/ano/2021/config.txt",
{ encoding: "utf8" }
);
} catch (err) {
console.log(err);
}
}

É recomendado evitar ao máximo fazer comentários desnecessários no código. O código deve ser autoexplicativo, exceto em situações onde sejam necessários comentários para informações legais ou explicações que não possam ser substituídas por nomes adequados.

Por exemplo, no código abaixo, é desnecessário o comentário explicando que a lista é de frutas e a obtenção do comprimento do array de frutas:

Banner Amazon Celular 300x300
// Frutas const ruins = ['🍇', '🍌', '🍉']; 
// esta é a lista de frutas const basketSize = fruit.length // obtendo o comprimento do array de frutas.

Já no exemplo abaixo, é válido utilizar um comentário para informar que o endereço só funciona em ambiente de preparação:

const serverUrl = '[email protected]'; // só funciona no ambiente de preparação.

Conclusão

A maioria dessas ideias pode ser estendida e aplicada a várias linguagens de programação. Adotar essas estratégias exige esforço, especialmente com bases de código maiores, mas garantirá que seu código seja limpo, escalável e mantenha a qualidade do código em sua equipe.

Portanto, a escrita de código limpo em JavaScript é uma prática essencial para garantir a qualidade do seu trabalho e da equipe de desenvolvimento. Então, com a adoção de boas práticas, como a utilização de nomes descritivos de variáveis, funções bem definidas e documentação clara, você pode garantir que seu código seja fácil de entender, manter e compartilhar. Dessa forma, ao seguir essas práticas, você pode melhorar a eficiência do processo de desenvolvimento, minimizar erros e garantir a satisfação do usuário final. Lembre-se de que a escrita de código limpo é um processo contínuo e deve ser aplicada em todas as etapas do seu trabalho. Com esforço e dedicação, você pode se tornar um programador de JavaScript mais eficiente e confiável.

Postagens Semelhantes