Magia na programação não é uma metáfora, mas um termo preciso que designa valores (números, strings, flags) cujo significado não é óbvio a partir do contexto e requer conhecimento externo para ser compreendido. O tipo mais comum de magia são os magic numbers: constantes numéricas escritas diretamente no código sem explicação de por que aquele valor específico foi escolhido. De acordo com o Relatório de Qualidade de Código da SonarSource (2025), cerca de 8 por cento de todos os avisos de analisadores estáticos estão relacionados a literais não explicados. Valores mágicos tornam o código frágil: alterá-los requer encontrar todas as ocorrências, e um novo desenvolvedor não sabe se o número pode ser modificado ou se é crítico para o funcionamento do sistema.
Pontos principais
Magia é qualquer valor no código-fonte cujo significado não é óbvio sem conhecimento adicional do domínio. O termo está estabelecido na comunidade: se um desenvolvedor olha para um número e não consegue dizer de onde veio — isso é magia.
A magia se apresenta em vários tipos: numérica (magic numbers), de strings (magic strings), booleana (magic flags) e de configuração (parâmetros hardcoded que deveriam estar nas configurações). Os quatro tipos compartilham um problema: quando um requisito muda, o desenvolvedor deve encontrar cada lugar onde o valor é usado e substituí-los manualmente. Perder uma única ocorrência gera um bug.
De acordo com a Pesquisa de Qualidade de Código da JetBrains (2025), 73 por cento dos desenvolvedores consideram magic numbers um indicador de baixa qualidade de código, enquanto 41 por cento admitem que ocasionalmente os deixam. O principal motivo é a pressa: “Vou colocar a constante depois” — mas esse depois nunca chega, e um mês depois o número 0.85 permanece no meio do corpo de um método sem explicação.
A regra principal: todo valor literal, exceto 0, 1, true, false e string vazia, deve ser extraído para uma constante nomeada. Exceções: incremento de contador (i + 1), zeros matemáticos (verificação de 0) e valores iniciais de acumuladores. Todo o resto é candidato a ser nomeado.
Um magic number é um literal numérico cujo valor não é óbvio a partir do contexto. Um exemplo clássico: 86400 em código relacionado a tempo limite. Um desenvolvedor vê o número e precisa adivinhar que é a quantidade de segundos em um dia. Se ele errar e escrever 84600, o bug será difícil de detectar porque o tempo limite será acionado 18 minutos antes.
Por que os magic numbers são perigosos: primeiro, eles prejudicam a legibilidade. O número 1024 pode significar o tamanho de um kilobyte, um limite de paginação ou o número máximo de itens. Sem contexto — é apenas um número. Segundo, eles criam duplicação: se 1024 é usado em cinco lugares, quando o limite muda para 2048, o desenvolvedor precisa encontrar todos os cinco e substituí-los. Se um lugar for perdido, o sistema funciona incorretamente, mas sem um erro explícito.
// before - magic in its pure form
fun calculateTimeout(base: Int): Int {
return base * 3 + 5000
}
// after - values replaced with constants
private const val RETRY_MULTIPLIER = 3
private const val BASE_TIMEOUT_MS = 5000
fun calculateTimeout(base: Int): Int {
return base * RETRY_MULTIPLIER + BASE_TIMEOUT_MS
}
O terceiro perigo é a impossibilidade de testar. Se um valor limite está hardcoded como literal, o teste não pode sobrescrevê-lo para verificar condições de contorno. Uma constante extraída para um companion object ou arquivo de configuração torna o código testável: o teste substitui um valor diferente e verifica o comportamento do sistema no limite.
Desenvolva um hábito: toda vez que você escrever um número diferente de 0, 1, 100 ou 2 — pare e considere se ele deve ser extraído para uma constante. Se o número está relacionado à lógica de negócios (limite, threshold, timeout, tamanho) — extraia sem hesitar. Se o número é uma constante matemática (pi, e) — use a biblioteca padrão (Math.PI, Math.E).
Strings mágicas são literais de string embutidos no código sem serem extraídos para constantes ou recursos. Exemplos típicos: URLs de endpoints, nomes de chaves SharedPreferences, Intent Actions, chaves de bundle, nomes de arquivos e consultas SQL.
O perigo das strings mágicas é a falta de verificação em tempo de compilação. Um erro de digitação na string “user_prefs” não será detectado até o tempo de execução. Se a string for usada em dez lugares e o desenvolvedor escrever “user_pref” (sem o s) em um deles — o aplicativo não trava, mas os dados não são salvos. Esse bug pode viver em produção por meses porque não causa uma falha.
Para projetos Android, strings mágicas devem ser extraídas para recursos (strings.xml, arrays.xml) ou constantes em um companion object. Para iOS — para recursos de string (Localizable.strings) ou constantes enum. Para backend — para arquivos de configuração (.env, application.properties). Nenhuma chave, URL ou caminho deve aparecer no código como um literal de string.
// before - magic strings across the class
let prefs = UserDefaults.standard
prefs.set(token, forKey: "auth_token")
prefs.set(userId, forKey: "current_user_id")
// after - strings extracted to enum
enum PrefKeys: String {
case authToken = "auth_token"
case currentUserId = "current_user_id"
}
prefs.set(token, forKey: PrefKeys.authToken.rawValue)
prefs.set(userId, forKey: PrefKeys.currentUserId.rawValue)
Preste atenção especial às strings que são duplicadas. Se a mesma chave “user_settings” aparece em três arquivos — 99 por cento das vezes um erro de digitação acabará aparecendo em um deles. Extrair para um enum ou constante garante que todas as referências usem o mesmo valor.
Magic flags são parâmetros booleanos cujo significado não é óbvio a partir do contexto da chamada. Um antipadrão clássico: passar true ou false para um método sem explicar o que exatamente essa flag ativa ou desativa.
Exemplo: userDao.fetch(includeDeleted = false). Um desenvolvedor vê false e não sabe se significa “não incluir excluídos” ou “não incluir ativos”. Um mês depois, false se transforma em true, e registros excluídos começam a aparecer nos resultados. O bug só é descoberto em produção.
A solução é substituir flags booleanas por um enum ou classe selada. Em vez de um parâmetro Boolean, use UserFilter.includeDeleted ou UserFilter.activeOnly. Assim o código documenta sua intenção, e o IDE sugere opções disponíveis durante o autocompletar.
Se uma flag booleana é passada por várias camadas — esse é outro sinal de que a abstração está errada. Em vez de arrastar uma flag por três níveis de chamadas, considere se a escolha do filtro deve ser feita no nível superior e passada como uma configuração pronta. Quanto menos flags booleanas no código — menos magia.
Adote uma regra: nenhum parâmetro booleano é passado para um método sem um argumento nomeado (se a linguagem suportar argumentos nomeados). Em Kotlin e Swift, esse requisito é automático. Em Java, use Builder ou constantes enum em vez de true/false.
A busca por valores mágicos é automatizada por analisadores estáticos configurados para detectar literais em lugares inesperados. Cada linguagem oferece suas próprias ferramentas com exceções personalizáveis.
| Ferramenta | Linguagens | Regra |
|---|---|---|
| SonarQube | Java, Kotlin, Swift, Python, JS | MagicNumber, HardcodedString |
| ESLint | JavaScript, TypeScript | no-magic-numbers, no-hardcoded-strings |
| Detekt | Kotlin | MagicNumber, ComplexCondition |
| SwiftLint | Swift | magic_number (opt-in) |
| PMD | Java, Apex, PLSQL | MagicNumber (lista permitida configurável) |
| Inspeções do PhpStorm | PHP | NumericLiteralWithContext (inspeção incorporada) |
Configurar exceções é fundamental — sem isso, o analisador sinalizará cada incremento (-1, +1) e zero matemático. Para o SonarQube, a lista de números permitidos: 0, 1, -1, 2 (para duplicação), 100 (porcentagens), 60 e 24 (tempo). Para todos os outros valores — exija uma constante nomeada com o modificador public static final (Java) ou const val (Kotlin).
Para análise em nível de CI, adicione uma etapa que verifique a magia como aviso, mas não bloqueie a compilação. A primeira execução mostrará centenas de avisos em código legado. Gradualmente, ticket por ticket, migre o código para constantes e aumente o limite de qualidade. Quando o número de magic numbers ficar abaixo de 10 — ative a regra como erro de compilação.
A refatoração da magia é uma das operações mais seguras: substituir um literal por uma constante não altera o comportamento do código. No entanto, a abordagem deve ser sistemática para não perder dependências ocultas (por exemplo, se o mesmo magic number é usado em contextos não relacionados mas por acaso tem o mesmo valor).
Processo passo a passo: encontre todas as ocorrências do valor mágico, entenda o contexto de cada uma, divida em diferentes constantes (mesmo que os valores coincidam — os contextos são diferentes, e as constantes devem ter nomes diferentes), substitua os literais por constantes, verifique por meio de testes. O erro no passo 2 é o mais comum: dois conceitos diferentes (um timeout em milissegundos e um limite em bytes) podem coincidir numericamente (por exemplo, 5000), mas semanticamente são quantidades diferentes e não podem ser combinados em uma única constante.
// before - same number in different contexts
public class Config {
public void setupCache() {
cache.setMaxSize(5000); // 5 MB
}
public void setupTimeout() {
client.setReadTimeout(5000); // 5 seconds
}
}
// after - different constants for different contexts
public class Config {
private static final int CACHE_MAX_SIZE_MB = 5;
private static final int READ_TIMEOUT_SECONDS = 5;
public void setupCache() {
cache.setMaxSize(CACHE_MAX_SIZE_MB * 1024 * 1024);
}
public void setupTimeout() {
client.setReadTimeout(
READ_TIMEOUT_SECONDS * 1000
);
}
}
Para código novo, a regra é simples: qualquer literal, exceto 0, 1, -1, true, false, null e string vazia, é extraído para uma constante. Exceções: constantes matemáticas (sempre use a biblioteca padrão), dados de teste (literais podem permanecer nos testes, mas com um nome de variável descritivo) e valores limite para incremento (i + 1 em um loop é aceitável).
Perguntas frequentes
Sim, 100 também é um magic number se usado sem contexto. Em vez de 100, escreva MAX_PERCENT ou PROBABILITY_SCALE. Exceção: quando 100 é obviamente uma porcentagem no contexto (por exemplo, em uma fórmula de cálculo de porcentagem), mas mesmo neste caso uma constante melhora a legibilidade.
Em testes, também é melhor usar variáveis nomeadas. Em vez de assertEquals(42, result), escreva val expected = 42; assertEquals(expected, result). Exceção: testes de valores limite (0, null, string vazia) — eles podem permanecer como literais porque são legíveis no contexto do teste.
Sim, números relacionados à UI (tamanhos, margens, duração de animações) devem estar em recursos (dimens.xml, integers.xml). Constantes de negócio (timeouts, limites) — em um companion object ou arquivo de configuração. O critério principal: se um número pode mudar sem alterar a lógica — é um recurso.
Execute o SonarQube com a regra MagicNumber ou o ESLint com no-magic-numbers. Obtenha o relatório, classifique por frequência de uso e comece pelos números que aparecem em três ou mais lugares. Eles são os candidatos mais prováveis para extração em constantes.
Não. Literais aceitáveis: 0, 1, -1 (incremento/decremento, verificação de vazio), true, false, null, string vazia. Todos os outros exigem nomeação. Se o número 0 não é usado como verificação de vazio (por exemplo, 0 é o ID da categoria raiz), então 0 também deve ser uma constante: ROOT_CATEGORY_ID = 0.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também