Bora desmistificar uma parada que muita gente confunde: Clean Code não é sinônimo de código mais rápido.
Sim, você leu certo! Muita gente acha que seguir as melhores práticas vai automaticamente turbinar a performance da aplicação. Tenha calma meu pequeno gafanhoto não é bem assim que a banda toca.
Clean Code: Pra que serve então?
Pra começo de conversa, Clean Code é sobre deixar seu código legível, fácil de entender e de dar manutenção.
É como organizar seu quarto: não vai te fazer correr mais rápido, mas vai te ajudar a encontrar suas coisas rapidinho e a não tropeçar em nada. No mundo do código, isso significa menos bugs, menos dor de cabeça pra quem pega seu projeto e mais agilidade pra evoluir a aplicação.
A "velocidade" do código
Quando a gente fala em código rápido, estamos falando de performance. E a performance é medida em coisas tipo:
- Tempo de execução: Quanto tempo leva pra sua função rodar?
- Uso de memória: Quanta memória seu código consome?
- Operações por segundo: Quantas operações seu sistema consegue fazer em um segundo?
Aí que tá o X da questão: Clean Code foca na legibilidade e manutenibilidade, não diretamente na performance. Às vezes, pra deixar o código super limpo e fácil de ler, a gente acaba fazendo umas "voltas" que podem, sim, custar uns milissegundos a mais ou usar um pouquinho mais de memória.
Vamos ver alguns exemplos.
GTA V na veia: Exemplos práticos em TypeScript
Vamos pegar o GTA V como nosso campo de testes imaginário. Pensa em algo que acontece o tempo todo no jogo: o sistema de tiro.
Exemplo 1: O código "sujo" e ultra-rápido
Imagina que a gente precisa calcular o dano de um tiro no GTA V. Esse código aqui não é o mais bonito do mundo, mas ele é direto e reto, feito pra ser rápido:
function calculateRawDamage(
t: { x: number; y: number; z: number }, // Posição do alvo
a: { x: number; y: number; z: number }, // Posição do atirador
d: number, // Distância máxima de dano
b: boolean, // É headshot?
m: number // Dano base da arma
): number {
const dx = t.x - a.x;
const dy = t.y - a.y;
const dz = t.z - a.z;
const distSq = dx * dx + dy * dy + dz * dz; // Distância ao quadrado (evita sqrt)
let damage = m;
if (distSq > d * d) { // Compara distâncias ao quadrado
return 0; // Fora do alcance
}
if (b) {
damage *= 2.5; // Modificador de headshot
}
return damage;
}
// Exemplo de uso:
const enemyPosRaw = { x: 100, y: 50, z: 10 };
const playerPosRaw = { x: 105, y: 53, z: 11 };
const maxRangeRaw = 50; // Alcance máximo
const baseDamageRaw = 20; // Dano base
// Calculando o dano de um tiro normal
const normalDamageRaw = calculateRawDamage(
enemyPosRaw,
playerPosRaw,
maxRangeRaw,
false, // Não é headshot
baseDamageRaw
);
console.log(`Dano (código sujo, normal): ${normalDamageRaw}`);
// Saída: Dano (código sujo, normal): 20
// Calculando o dano de um headshot
const headshotDamageRaw = calculateRawDamage(
enemyPosRaw,
playerPosRaw,
maxRangeRaw,
true, // É headshot
baseDamageRaw
);
console.log(`Dano (código sujo, headshot): ${headshotDamageRaw}`);
// Saída: Dano (código sujo, headshot): 50
Por que é mais rápido?
-
Nomes curtos:
t
,a
,d
,b
,m
economizam caracteres. -
Cálculo direto de distância ao quadrado: Evita
Math.sqrt
, uma operação "cara". - Sem funções auxiliares: Menos "overhead" de chamadas de função.
- Parâmetros "pelados": Evita custo de criação de classes/estruturas mais complexas.
Exemplo 2: O código Clean Code com Programação Funcional (e um pouquinho mais lento)
Agora, vamos abordar o mesmo problema de cálculo de dano com uma pegada funcional. A ideia é ter funções puras, que recebem entradas e retornam saídas sem efeitos colaterais, e compor essas funções para construir a lógica. (Eu sei senior em DDD! Com classes ficaria mais simples, mas eu sou team funcional e para explicar fica mais fácil ^^)
// Interfaces para tipagem clara
interface Position {
x: number;
y: number;
z: number;
}
enum DamageType {
Normal = 1,
Headshot = 2.5 // Modificador de dano
}
// 1. Funções Puras e de Únicas Responsabilidades
// Calcula a distância euclidiana(Da um google) entre dois pontos
const calculateDistance = (p1: Position, p2: Position): number => {
const deltaX = p1.x - p2.x;
const deltaY = p1.y - p2.y;
const deltaZ = p1.z - p2.z;
return Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
};
// Aplica o modificador de dano
const applyDamageModifier = (baseDamage: number, damageType: DamageType): number => {
return baseDamage * damageType;
};
// Verifica se o alvo está dentro do alcance
const isInRange = (distance: number, maxRange: number): boolean => {
return distance <= maxRange;
};
// 2. Composição de Funções (a "Orquestração" da Lógica)
// Função que configura e retorna um calculador de dano específico para uma arma
const createWeaponDamageCalculator = (baseDamageWeapon: number, maxRangeWeapon: number) => {
return (attackerPos: Position, targetPos: Position, damageType: DamageType): number => {
const distance = calculateDistance(attackerPos, targetPos);
if (!isInRange(distance, maxRangeWeapon)) {
return 0; // Se fora do alcance, dano é zero
}
// Aplica o modificador de dano
return applyDamageModifier(baseDamageWeapon, damageType);
};
};
// Exemplo de uso:
const ak47DamageCalculator = createWeaponDamageCalculator(30, 500);
// AK-47 com 30 de dano, 500m alcance
const franklinPos = { x: 100, y: 50, z: 10 };
const enemyPos = { x: 120, y: 60, z: 12 };
const headshotDamagePF = ak47DamageCalculator(franklinPos, enemyPos, DamageType.Headshot);
console.log(`Dano (código funcional, headshot): ${headshotDamagePF}`);
// Saída: Dano (código funcional, headshot): 75
const normalDamagePF = ak47DamageCalculator(franklinPos, enemyPos, DamageType.Normal);
console.log(`Dano (código funcional, normal): ${normalDamagePF}`);
// Saída: Dano (código funcional, normal): 30
Por que é (potencialmente) mais lento (em alguns contextos)?
- Múltiplas chamadas de função: Quebrar a lógica em várias funções puras aumenta o "overhead" de chamadas de função.
-
Math.sqrt
: A funçãocalculateDistance
ainda usaMath.sqrt
, que é mais custosa. -
Criação de funções em tempo de execução:
createWeaponDamageCalculator
retorna uma nova função, o que pode ter um pequeno custo de alocação ou otimização JIT. - Imutabilidade: Embora implícita aqui, em cenários mais complexos da PF onde cópias imutáveis são feitas, o custo de memória e processamento pode aumentar. ## Moral da história
O Clean Code, seja ele orientado a objetos, funcional ou misto, foca na experiência do desenvolvedor, tornando o código mais agradável de trabalhar, entender e manter. Isso, a longo prazo, acelera o desenvolvimento e a manutenção do projeto. O código funcional, por sua natureza de funções puras e composição, facilita muito o teste unitário e a raciocínio sobre o fluxo de dados.
A performance, por outro lado, é uma preocupação mais específica, que geralmente é resolvida com otimizações pontuais (como o truque do distSq
no primeiro exemplo) e um bom entendimento de como as coisas funcionam "por baixo dos panos".
Não se esqueça: o tempo do desenvolvedor é caro! Escrever código legível e fácil de dar manutenção geralmente vale muito mais a pena do que otimizar cada milissegundo, a menos que você esteja trabalhando em um contexto super específico onde cada ciclo de CPU conta (tipo um motor de jogo de alta performance ou sistemas embarcados). Na maioria dos casos, o Clean Code vai te poupar muitas horas de depuração e estresse.
Resumão pra não esquecer:
- Clean Code = Legibilidade e Manutenibilidade. Pense em quem vai ler seu código.
- Programação Funcional (PF) = Funções puras, imutabilidade, composição. Ótima para código testável e modular.
- Performance = Velocidade e Eficiência. Medido em tempo de execução, uso de memória, etc.
- Não são a mesma coisa! Código mais "limpo" e modular (como na PF) pode ser marginalmente mais lento devido a abstrações.
- Priorize a legibilidade, sempre. Só otimize performance quando tiver um problema real e mensurável.
- O tempo do dev é valioso. Código fácil de manter acelera o desenvolvimento.
Top comments (0)