La magia en programación no es una metáfora, sino un término preciso que designa valores (números, cadenas, banderas) cuyo significado no es obvio en el contexto y requiere conocimiento externo para entenderlo. El tipo más común de magia son los magic numbers: constantes numéricas escritas directamente en el código sin explicación de por qué se eligió ese valor en particular. Según el Informe de Calidad de Código de SonarSource (2025), alrededor del 8 por ciento de todas las advertencias de los analizadores estáticos están relacionadas con literales sin explicación. Los valores mágicos hacen que el código sea frágil: cambiarlos requiere encontrar todas las ocurrencias, y un nuevo desarrollador no sabe si se puede modificar un número o si es crítico para el funcionamiento del sistema.
Puntos clave
Magia es cualquier valor en el código fuente cuyo significado no es obvio sin conocimiento adicional del dominio. El término está establecido en la comunidad: si un desarrollador mira un número y no sabe de dónde salió — eso es magia.
La magia se presenta en varios tipos: numérica (magic numbers), de cadenas (magic strings), booleana (magic flags) y de configuración (parámetros hardcodeados que deberían estar en la configuración). Los cuatro tipos comparten un problema: cuando un requisito cambia, el desarrollador debe encontrar cada lugar donde se usa el valor y reemplazarlos manualmente. Omitir una sola ocurrencia genera un error.
Según la Encuesta de Calidad de Código de JetBrains (2025), el 73 por ciento de los desarrolladores considera que los magic numbers son un indicador de baja calidad del código, mientras que el 41 por ciento admite que a veces los deja. La razón principal es la prisa: “Pondré la constante después” — pero ese después nunca llega, y un mes después el número 0.85 sigue en medio del cuerpo de un método sin explicación.
La regla clave: todo valor literal excepto 0, 1, true, false y cadena vacía debe extraerse en una constante con nombre. Excepciones: incremento de contador (i + 1), ceros matemáticos (comprobación de 0) y valores iniciales de acumuladores. Todo lo demás es candidato a ser nombrado.
Un magic number es un literal numérico cuyo valor no es obvio en el contexto. Un ejemplo clásico: 86400 en código relacionado con tiempo de espera. Un desarrollador ve el número y debe adivinar que es la cantidad de segundos en un día. Si se equivoca y escribe 84600, el error será difícil de detectar porque el tiempo de espera se disparará 18 minutos antes.
Por qué los magic numbers son peligrosos: primero, dañan la legibilidad. El número 1024 podría significar el tamaño de un kilobyte, un umbral de paginación o el número máximo de elementos. Sin contexto — es solo un número. Segundo, crean duplicación: si 1024 se usa en cinco lugares, cuando el umbral cambie a 2048, el desarrollador debe encontrar los cinco y reemplazarlos. Si se omite un lugar, el sistema funciona incorrectamente pero sin un error 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
}
El tercer peligro es la imposibilidad de probar. Si un valor umbral está hardcodeado como literal, la prueba no puede sobrescribirlo para verificar condiciones límite. Una constante extraída a un companion object o archivo de configuración hace que el código sea comprobable: la prueba sustituye un valor diferente y verifica el comportamiento del sistema en el límite.
Desarrolle un hábito: cada vez que escriba un número que no sea 0, 1, 100 o 2 — deténgase y considere si debería extraerse en una constante. Si el número está relacionado con la lógica de negocio (límite, umbral, tiempo de espera, tamaño) — extráigalo sin dudar. Si el número es una constante matemática (pi, e) — use la biblioteca estándar (Math.PI, Math.E).
Las cadenas mágicas son literales de cadena incrustados en el código sin extraerlos a constantes o recursos. Ejemplos típicos: URL de endpoints, nombres de claves de SharedPreferences, Intent Actions, claves de bundle, nombres de archivos y consultas SQL.
El peligro de las cadenas mágicas es la falta de verificación en tiempo de compilación. Un error tipográfico en la cadena “user_prefs” no se detectará hasta el tiempo de ejecución. Si la cadena se usa en diez lugares y el desarrollador escribe “user_pref” (sin la s) en uno de ellos — la aplicación no falla, pero los datos no se guardan. Este error puede vivir en producción durante meses porque no provoca un fallo.
Para proyectos Android, las cadenas mágicas deben extraerse a recursos (strings.xml, arrays.xml) o constantes en un companion object. Para iOS — a recursos de cadena (Localizable.strings) o constantes enum. Para backend — a archivos de configuración (.env, application.properties). Ninguna clave, URL o ruta debe aparecer en el código como literal de cadena.
// 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 especial atención a las cadenas que se duplican. Si la misma clave “user_settings” aparece en tres archivos — el 99 por ciento de las veces eventualmente aparecerá un error tipográfico en uno de ellos. Extraer a un enum o constante garantiza que todas las referencias usen el mismo valor.
Los magic flags son parámetros booleanos cuyo significado no es obvio en el contexto de la llamada. Un antipatrón clásico: pasar true o false a un método sin explicar qué activa o desactiva exactamente esa bandera.
Ejemplo: userDao.fetch(includeDeleted = false). Un desarrollador ve false y no sabe si significa “no incluir eliminados” o “no incluir activos”. Un mes después, false se convierte en true, y los registros eliminados comienzan a aparecer en los resultados. El error solo se descubre en producción.
La solución es reemplazar las banderas booleanas con un enum o clase sellada. En lugar de un parámetro Boolean, use UserFilter.includeDeleted o UserFilter.activeOnly. Así el código documenta su intención y el IDE sugiere opciones disponibles durante el autocompletado.
Si una bandera booleana se pasa a través de varias capas — esa es otra señal de que la abstracción es incorrecta. En lugar de arrastrar una bandera a través de tres niveles de llamadas, considere si la elección del filtro debería tomarse en el nivel superior y pasarse como una configuración ya preparada. Cuantas menos banderas booleanas haya en el código — menos magia.
Adopte una regla: ningún parámetro booleano se pasa a un método sin un argumento con nombre (si el lenguaje admite argumentos con nombre). En Kotlin y Swift, este requisito es automático. En Java, use Builder o constantes enum en lugar de true/false.
La búsqueda de valores mágicos se automatiza con analizadores estáticos configurados para detectar literales en lugares inesperados. Cada lenguaje ofrece sus propias herramientas con excepciones personalizables.
| Herramienta | Lenguajes | Regla |
|---|---|---|
| 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 configurable) |
| Inspecciones de PhpStorm | PHP | NumericLiteralWithContext (inspección incorporada) |
Configurar las excepciones es crítico — sin ello, el analizador marcará cada incremento (-1, +1) y cero matemático. Para SonarQube, la lista de números permitidos: 0, 1, -1, 2 (para duplicación), 100 (porcentajes), 60 y 24 (tiempo). Para todos los demás valores — exija una constante con nombre con el modificador public static final (Java) o const val (Kotlin).
Para el análisis a nivel de CI, agregue un paso que verifique la magia como advertencia pero no bloquee la compilación. La primera ejecución mostrará cientos de advertencias en código heredado. Gradualmente, ticket por ticket, migre el código a constantes y eleve el umbral de calidad. Cuando el número de magic numbers sea inferior a 10 — active la regla como error de compilación.
La refactorización de la magia es una de las operaciones más seguras: reemplazar un literal por una constante no cambia el comportamiento del código. Sin embargo, el enfoque debe ser sistemático para no pasar por alto dependencias ocultas (por ejemplo, si el mismo magic number se usa en contextos no relacionados pero casualmente tiene el mismo valor).
Proceso paso a paso: encuentre todas las ocurrencias del valor mágico, comprenda el contexto de cada una, divídalas en diferentes constantes (incluso si los valores coinciden — los contextos son diferentes y las constantes deben tener nombres diferentes), reemplace los literales con constantes, verifique mediante pruebas. El error en el paso 2 es el más común: dos conceptos diferentes (un tiempo de espera en milisegundos y un umbral en bytes) pueden coincidir numéricamente (por ejemplo, 5000), pero semánticamente son cantidades diferentes y no pueden combinarse en una sola 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 nuevo, la regla es simple: cualquier literal excepto 0, 1, -1, true, false, null y cadena vacía se extrae en una constante. Excepciones: constantes matemáticas (siempre use la biblioteca estándar), datos de prueba (los literales pueden permanecer en las pruebas pero con un nombre de variable descriptivo) y valores límite para incremento (i + 1 en un bucle está bien).
Preguntas frecuentes
Sí, 100 también es un magic number si se usa sin contexto. En lugar de 100, escriba MAX_PERCENT o PROBABILITY_SCALE. Excepción: cuando 100 es obviamente un porcentaje en el contexto (por ejemplo, en una fórmula de cálculo de porcentaje), pero incluso en este caso una constante mejora la legibilidad.
En las pruebas, también es mejor usar variables con nombre. En lugar de assertEquals(42, result), escriba val expected = 42; assertEquals(expected, result). Excepción: pruebas de valores límite (0, null, cadena vacía) — pueden permanecer como literales porque son legibles en el contexto de la prueba.
Sí, los números relacionados con la UI (tamaños, márgenes, duración de animaciones) deben estar en recursos (dimens.xml, integers.xml). Las constantes de negocio (tiempos de espera, límites) — en un companion object o archivo de configuración. El criterio principal: si un número puede cambiar sin cambiar la lógica — es un recurso.
Ejecute SonarQube con la regla MagicNumber o ESLint con no-magic-numbers. Obtenga el informe, ordene por frecuencia de uso y comience con los números que aparecen en tres o más lugares. Son los candidatos más probables para extraerlos a constantes.
No. Los literales aceptables son: 0, 1, -1 (incremento/decremento, comprobación de vacío), true, false, null, cadena vacía. Todos los demás requieren nombre. Si el número 0 no se usa como comprobación de vacío (por ejemplo, 0 es el ID de categoría raíz), entonces 0 también debe ser una constante: ROOT_CATEGORY_ID = 0.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también