Convenções de Código
Assim como escrevi sobre estilização em SQL, sigo na busca por melhores práticas. Afinal, prática é justamente aquilo que repetimos. E quanto melhor fazemos, mais evoluímos. Da palavra prática vem a praticidade. E quanto mais prático algo é, mais vantagem traz. A praticidade nos faz ganhar tempo e simplificar a vida.
Quase sempre, aquilo que você ainda não fez, alguém já fez antes. Essa pessoa provavelmente enfrentou dificuldades e encontrou uma solução. A prova está nos fóruns da internet e no próprio Stack Overflow, que vivem cheios de exemplos disso.
Me convença
Toda vez que preciso escrever código, que nada mais é do que uma representação de coisas do mundo real, organizadas em instruções e comandos, me pergunto: “Essa é a melhor forma de fazer?”.
E ai começa uma batalha mental, com diversos questionamentos:
- Está seguro e atualizado?
- Está escrito corretamente?
- Está fácil de entender?
- Será que tem uma forma mais simples de fazer?
Pra resolver esse tipo de problema, encontrei um único caminho — padrões e convenções.
Um trecho de código não tão bom...
// pages/api/buscartime.js
// Exemplo propositalmente RUIM, usando Next.js (antigo)
// e Postgres (driver pg básico)
import { Client } from "pg";
// ❌ tudo hardcoded, nada de variáveis de ambiente
const client = new Client({
user: "postgres",
host: "localhost",
database: "futebol_db",
password: "123456",
port: 5432,
});
// ❌ conexão manual, sem pool, e sem tratar reconexão
client.connect(err => {
if (err) {
console.log("Erro ao conectar no banco: ", err);
} else {
console.log("Conectado no Postgres!");
}
});
// ❌ rota Next.js antiga, lógica toda misturada
export default (req, res) => {
if (req.method !== "GET") {
res.status(405).send("Somente GET é permitido");
return;
}
const { nomeTime } = req.query; // sem validação
if (!nomeTime) {
res.status(400).send("Você precisa informar o nome do time");
return;
}
// ❌ query concatenada → risco de SQL injection
const query = `SELECT * FROM times WHERE nome = '${nomeTime}'`;
client.query(query, (err, result) => {
if (err) {
console.log("Erro na query: ", err);
res.status(500).json({ erro: "Erro ao buscar time" });
} else {
if (result.rows.length > 0) {
let resposta = {
status: "ok",
quantidade: result.rows.length,
times: [],
};
// ❌ loop desnecessário, poderia mandar direto result.rows
for (let i = 0; i < result.rows.length; i++) {
resposta.times.push({
id: result.rows[i].id,
nome: result.rows[i].nome,
cidade: result.rows[i].cidade,
estadio: result.rows[i].estadio,
fundacao: result.rows[i].fundacao,
tecnico: result.rows[i].tecnico,
});
}
// ❌ mistura log e resposta
console.log("Resposta enviada: ", resposta);
res.status(200).json(resposta);
} else {
res.status(404).send("Nenhum time encontrado: " + nomeTime);
}
}
});
};
Nesse exemplo, o código pode até funcionar, porém ele está sensível desde a segurança até qualquer mudança em infraestrutura.
Padrões e Convenções
Esses 2 termos são muito próximos, vamos as definições:
Padrão (Standard) → Regra oficial documentada, servindo como uma norma.
Padrão é algo formalizado, geralmente definido por um órgão, entidade ou pela própria comunidade técnica. Tem caráter de norma ou regra oficial. Costuma ser documentado e aceito amplamente para garantir compatibilidade entre sistemas, ferramentas e equipes.
Exemplos:
- SQL ANSI é um padrão (definido pela ANSI/ISO).
- HTTP/1.1, HTTP/2 são padrões definidos pelo IETF.
- Em código, o ECMAScript é o padrão do JavaScript.
Convenção (Convention) → Acordo com time interno ou comunidade, servindo como boas práticas.
Convenção é algo que surge de um acordo não formal dentro de um time, empresa ou comunidade. Não é obrigatório por norma externa, mas é seguido para manter consistência, legibilidade e entendimento comum. Pode variar entre grupos diferentes.
Exemplos:
- Nomear classes em C# com PascalCase é convenção (não é exigido pelo compilador).
- Usar snake_case para colunas no banco de dados.
- Definir que commits sigam o Conventional Commits (é convenção, não padrão oficial).
Seguir padrões e convenções só trazem vantagens, além de gerar confiança no desenvolvimento e aumentar o nível de DX - Developer eXperience, melhorando o dia a dia do programador ao criar e manter o código.
Menos é mais
Aqui um ponto importante: nada está escrito em pedra. Nem todo o código está em constante mudança, mas quando muda, precisa ser pra melhor e não pra pior.
Colocar 20 camadas de abstrações, 2 bancos de cache, 5 frameworks e 3 nuvens só pra inflar o ego não garante o resultado. Talvez assegure que a aplicação fique refém de um ou mais desenvolvedores narcisistas que querem se perpetuar ali.
Uma boa prática para afastar esse cenário é o uso do pensamento crítico, incluindo no processo de desenvolvimento a entrega baseada em métricas e benchmark. Um cenário com antes e depois documentado e comprovado garante longevidade a operação.
Busque o equilíbrio para todas as situações ⚖️.
Aquele trecho de código com convenções...
// /pages/api/football-teams/[teamName].js
// Exemplo MELHORADO com padrões e convenções.
import { Pool } from "pg";
// ✅ Padrão: uso de variáveis de ambiente (env) em vez de hardcode.
// ✅ Convenção: nomes em MAIÚSCULAS para variáveis de ambiente.
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT || 5432,
});
// ✅ Padrão: funções assíncronas (async/await) no lugar de callbacks.
// ✅ Convenção: early return para erros.
export default async function handler(req, res) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Método não permitido" });
}
// `teamName` vem da rota: /api/football-teams/SaoPauloFutebolClube.
const { teamName } = req.query;
if (!teamName) {
return res.status(400).json({ error: "Informe o nome do time" });
}
try {
// ✅ Padrão: uso de parâmetros ($1, $2, ...) → evita SQL Injection.
// ✅ Convenção: escrita de queries em letras maiúsculas para comandos SQL.
const query = `
SELECT
id,
team_name,
city,
stadium,
coach
FROM
football_teams
WHERE
team_name = $1
;
`;
// O retorno de rows será sempre um array.
const { rows } = await pool.query(query, [teamName]);
if (rows.length === 0) {
return res.status(404).json({ error: `Nenhum time encontrado: ${teamName}` });
}
// ✅ Convenção: nomes claros e consistentes no JSON, retornando apenas 1 time.
const time = rows[0]; // pega o primeiro (único) registro retornado
return res.status(200).json({
status: "ok",
time: {
id: time.id, // ID do time: 6331
teamName: time.team_name, // Nome do time: São Paulo Futebol Clube
city: time.city, // Cidade sede: São Paulo
stadium: time.stadium, // Estádio principal: Morumbi
coach: time.coach, // Técnico atual: Hernán Crespo
},
});
} catch (error) {
// ✅ Mantém o log do erro para debugging
console.error("Erro ao buscar time:", error);
// ✅ Retorna erro genérico para o cliente
return res.status(500).json({ error: "Erro interno no servidor" });
}
}
// Exemplo de chamada ao método.
const foundTeam = fetch("/api/football-teams/SaoPauloFutebolClube")
.then(res => res.json())
.then(data => console.log(data));
// Exemplo do JSON retornado.
{
"status": "ok",
"time": {
"id": 6331,
"teamName": "São Paulo Futebol Clube",
"city": "São Paulo",
"stadium": "Morumbi",
"coach": "Hernán Crespo"
}
}
Opa, o código ganhou clareza, podendo ser aprimorado com alguns níveis de abstração, sem exagero.
Evite repetições, abstraia
Comunicação com banco de dados, chamada e retorno de API, tratamento de erros… essas operações ocorrem a todo instante e tem um comportamento parecido e repetitivo.
Repetição de comportamento em uma aplicação é um candidato a ter sua complexidade abstraída e organizada. Assim fica fácil reaproveitar o código, deixando o foco para trabalhar apenas em novas regras de negócio. Separando as responsabilidades, da rota no navegador até o banco de dados, temos o seguinte fluxo:

Aquele mesmo exemplo, abstraído em camadas...
Organizando e separando a lógica usando o padrão MVC adaptado para APIs:
1️⃣ Handler da API (Rota + Controller), quem definitivamente executa as ações.
pages/api/football-teams/[teamName].js → regras de rota e resposta JSON.
// pages/api/football-teams/[teamName].js
// next-connect, router e outros trechos ocultados para didática.
import { createRouter } from "next-connect";
import controller from "infra/controller.js";
import footballTeam from "models/footballTeam.js";
async function getHandler(request, response) {
const { teamName } = request.query;
// Chamada limpa para o model
const teamFound = await footballTeam.findOneByName(teamName);
return response.status(200).json({
status: "ok",
team: teamFound,
});
const footballTeams = {
getHandler,
};
export default footballTeams;
}
2️⃣ Representando os times de futebol em um Modelo chamado footballTeam.
models/footballTeam.js → define propriedades e métodos de acesso.
// models/footballTeam.js
import database from "infra/database.js";
import { NotFoundError } from "infra/errors.js";
async function findOneByName(teamName) {
const teamFound = await runSelectQuery(teamName);
return teamFound;
async function runSelectQuery(teamName) {
const results = await database.query({
text: `
SELECT
id,
team_name,
city,
stadium,
coach
FROM
football_teams
WHERE
team_name = $1
LIMIT
1
;`,
values: [teamName],
});
if (results.rowCount === 0) {
throw new NotFoundError({
message: `Nenhum time encontrado: ${teamName}`,
action: "Verifique se o nome do time foi digitado corretamente.",
});
}
return results.rows[0];
}
}
const footballTeam = {
findOneByName,
};
export default footballTeam;
3️⃣ Infraestrutura do banco de dados.
infra/database.js → centraliza pool e conexão com Postgres.
// infra/database.js
// Importa todo o pacote "pg"
import pg from "pg";
// Desestrutura a classe Pool do pacote
const { Pool } = pg;
// Pool singleton para toda a aplicação
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT || 5432,
max: process.env.DB_MAX_CONNECTIONS || 20, // Máximo de conexões
idleTimeoutMillis: process.env.DB_MAX_IDLE_TIMEOUT_MILLIS || 30000, // 30s
connectionTimeoutMillis: process.env.DB_MAX_CONNECTION_TIMEOUT_MILLIS || 2000, // 2s
});
// Testa a conexão inicial
pool.connect((err, client, release) => {
if (err) {
console.error("Erro ao conectar no banco de dados:", err.stack);
} else {
console.log("Conectado ao Postgres com sucesso!");
release();
}
});
// Método genérico para executar queries
async function query({ text, values }) {
const client = await pool.connect();
try {
const result = await client.query(text, values);
return result;
} catch (err) {
console.error("Erro na query:", err);
throw err;
} finally {
client.release();
}
}
// Exporta apenas o método query, mantendo pool encapsulado
const database = {
query,
};
export default database;
Detalhando a estrutura do último trecho de código apresentado, separamos as camadas em:
- Handler/Router → cuidam das rotas e da resposta HTTP, mantendo o fluxo limpo (
[teamName].jscomnext-connect). - Controllers → implementam a lógica de negócio e abstraem validações, chamadas aos models (
footballTeams.js,users.js). - Models → encapsulam a lógica de acesso ao banco (
footballTeam.js,user.js). - Infraestrutura → banco de dados, pool e erros centralizados (
database.js,controller.js,errors.js).
Resumindo, o que fizemos até aqui foi uma boa organização, deixando o código flexível para mudanças. Há oportunidade de melhoria? Sim, mas para o contexto do post está bem encaminhado. Futuramente quero escrever sobre linting de código, onde adicionamos um analisador de sintaxe, validando a escrita do software.
Minha dica para o seu coração: ao trabalhar com qualquer linguagem de programação e banco de dados, use os padrões e convenções sempre que possível. Adapte quando necessário.
Quer se aprofundar em padrões, convenções, estilos e abstrações? Fica meu convite pra conferir esse projeto aqui.
Happy coding!