Nivel inicial11 min de lectura · Publicado el

Números mágicos en programación: qué son y cómo evitarlos

Números mágicos: en la nevera, un táper que solo dice 3 frente a otro con «Lentejas, del lunes», y precio * 0.21 frente a precio * IVA_GENERAL. Número mágico: ¿3 qué?rubentejera.com 3¿días?¿raciones?Lentejas, del lunesprecio * 0.21precio *IVA_GENERAL
Un número suelto es un táper etiquetado «3»: quien lo guardó sabía qué era; tú tienes que adivinarlo. Con nombre no hay duda: precio * IVA_GENERAL se entiende y precio * 0.21, no.

Un número mágico es un número que aparece en medio del código sin que se explique su origen. Falta un nombre que lo describa. Si no conoces la lógica de negocio, te quedas bloqueado pensando qué significa esa cifra salida de la nada.

Es como encontrar en la nevera un táper con una etiqueta que solo dice «3». ¿Tres días? ¿Tres raciones? ¿El tercero de la semana? Quien lo guardó lo sabía; tú tienes que adivinarlo. Con una etiqueta que diga «Lentejas, del lunes» no hay duda. Evitarlos es uno de los principios del código limpio (clean code).

Cómo identificar un número mágico

Mira este código en JavaScript:

const baseImponible = 12.5;
const pvp = baseImponible + baseImponible * 0.21;
console.log(pvp); // 15.125

Aquí se adivina qué es el 0.21 por dos motivos:

  • El nombre de las variables da contexto: baseImponible y pvp (precio de venta al público) suenan a impuestos.
  • No hay mucho código alrededor que nos enturbie la vista.

Pero no siempre es así. En medio de una maraña de código, cuesta saber a qué se refiere cada cifra. Y aparecen más problemas:

  • No se puede buscar. Si buscas 0.21 en el proyecto, encontrarás el IVA, pero quizá también un margen de beneficio o un descuento que casualmente valen lo mismo.
  • Cambiarlo es arriesgado. Si el valor está repetido en diez sitios, tienes que encontrar los diez y no tocar ninguno que signifique otra cosa.

Esto último no es teórico: el IVA general en España pasó del 18 % al 21 % en 2012. Quien tenía el número repartido por el código tuvo que buscarlo a mano.

Cómo eliminar números mágicos: constantes con nombre

Hagamos un refactor (cambiar cómo está escrito el código sin cambiar lo que hace) del ejemplo anterior:

const IVA_GENERAL = 0.21;

function calcularIva(baseImponible) {
  return baseImponible * IVA_GENERAL;
}

function calcularPrecioConIva(baseImponible) {
  return baseImponible + calcularIva(baseImponible);
}

console.log(calcularPrecioConIva(12.5)); // 15.125

El resultado es el mismo, pero ahora el código se entiende sin conocer la lógica de negocio:

  • El 0.21 tiene un nombre que dice qué es, y se puede buscar en todo el proyecto.
  • Si el tipo cambia, se cambia en un solo sitio.
  • Las funciones cuentan qué se calcula, no solo cómo.

Cada número mágico que se queda es un poco de deuda técnica: hoy no rompe nada, pero encarece cada cambio.

Antes

  • baseImponible * 0.21
  • Hay que adivinar qué es el 0.21
  • Para cambiarlo, buscar a mano en todo el código

Después

  • baseImponible * IVA_GENERAL
  • El nombre explica el valor
  • Se cambia en un solo sitio

En JavaScript, Java, Python y muchos otros lenguajes, la convención es escribir las constantes en mayúsculas y con guiones bajos (IVA_GENERAL). Así se distinguen de un vistazo de una variable normal. Elegir bien ese nombre es la mitad del trabajo: tienes más pautas en Nombres con sentido.

Si tienes que adivinar qué es un número, necesita un nombre.

Cuándo no hace falta una constante

Sustituir todos los números por constantes también empeora el código. Estos casos suelen entenderse solos:

Valor Ejemplo ¿Constante?
0 y 1 como punto de partida o paso let total = 0, i + 1 No
Primer elemento de una lista lista[0] No
Mitad, doble, porcentaje ancho / 2, tanto * 100 Normalmente no
Valores que pueden cambiar o se repiten IVA, número de reintentos, tiempo de espera Sí
Valores con significado de negocio edad mínima, límite de un plan Sí
Unidades poco obvias 86400 (segundos de un día) Sí: SEGUNDOS_POR_DIA

La pregunta útil no es «¿hay un número?», sino «¿alguien que lea esto sabrá qué significa y de dónde sale?». Una constante como CERO = 0 no aporta nada: repite el valor sin explicar su sentido.

En los tests es habitual escribir los valores esperados tal cual (expect(precio).toBe(15.125)), porque ahí el número es el dato del ejemplo, no una regla de negocio.

El siguiente paso: enums y objetos de valor

Cuando aparecen varios valores relacionados, una constante suelta se queda corta. En España hay tres tipos de IVA: general (21 %), reducido (10 %) y superreducido (4 %). Puedes agruparlos en un enum, un conjunto cerrado de valores con nombre. JavaScript no tiene enums propios, pero un objeto congelado hace ese papel:

const TipoIva = Object.freeze({
  GENERAL: 0.21,
  REDUCIDO: 0.10,
  SUPERREDUCIDO: 0.04,
});

function calcularPrecioConIva(baseImponible, tipo) {
  return baseImponible * (1 + tipo);
}

console.log(calcularPrecioConIva(12.5, TipoIva.GENERAL));  // 15.125
console.log(calcularPrecioConIva(12.5, TipoIva.REDUCIDO)); // 13.750000000000002
console.log(calcularPrecioConIva(12.5, TipoIva.SUPERRDUCIDO)); // NaN

Al ejecutarlo salen dos problemas que una constante no resuelve:

  • Una errata pasa sin aviso. SUPERRDUCIDO no existe, JavaScript devuelve undefined y el precio acaba en NaN («no es un número»).
  • Los decimales fallan. Los ordenadores guardan los decimales de forma aproximada, y por eso sale 13.750000000000002. Con dinero conviene trabajar en céntimos, que son números enteros.

Ese es el momento de pasar a un objeto de valor (value object): un objeto pequeño que representa un concepto del negocio, valida sus datos y sabe hacer sus propias operaciones:

class TipoIva {
  static GENERAL = new TipoIva(21);
  static REDUCIDO = new TipoIva(10);
  static SUPERREDUCIDO = new TipoIva(4);

  #porcentaje;

  constructor(porcentaje) {
    if (porcentaje < 0 || porcentaje > 100) {
      throw new RangeError(`Tipo de IVA no válido: ${porcentaje}`);
    }
    this.#porcentaje = porcentaje;
  }

  aplicarA(centimos) {
    return Math.round(centimos * (100 + this.#porcentaje) / 100);
  }
}

console.log(TipoIva.GENERAL.aplicarA(1250));  // 1513 céntimos = 15,13 €
console.log(TipoIva.REDUCIDO.aplicarA(1250)); // 1375 céntimos = 13,75 €

Ahora el número 21 vive en un único sitio, junto a la regla que lo usa, y escribir mal TipoIva.SUPERRDUCIDO.aplicarA(…) lanza un error en lugar de devolver NaN en silencio.

No empieces por aquí. Para un script de veinte líneas, una constante basta. El objeto de valor compensa cuando el concepto se usa en muchos sitios o tiene reglas propias. Sus tres rasgos (igualdad por valor, inmutabilidad y validación al crearlo) los tienes explicados en value objects. El redondeo del ejemplo es el más sencillo posible; en una aplicación real hay que aplicar el que exija la normativa.

Números mágicos en código generado por IA

Los asistentes de IA escriben números mágicos con naturalidad: un setTimeout(…, 30000), un retries: 3 o un if (items.length > 50) sin explicar de dónde salen. El código funciona, así que es fácil aceptarlo sin mirar.

Un criterio práctico al revisar código generado: para cada número que no sea obvio, pregúntate «¿de dónde sale este valor?». Si la respuesta es un requisito, dale nombre. Si nadie lo sabe, probablemente la IA lo ha inventado, y conviene confirmarlo antes de que se quede para siempre. Es una revisión rápida y encaja con la idea de IA Limpia: aprovechar la velocidad sin perder lo que hace el código fácil de entender.

Resumen

  1. Busca números y textos sueltos cuyo significado haya que adivinar.
  2. Dales un nombre que explique qué son con una constante.
  3. Deja tranquilos el 0, el 1 y los valores obvios.
  4. Si hay varios valores relacionados o reglas propias, agrúpalos en un enum o un objeto de valor.

Para practicarlo, la kata de los puntos de un equipo deja un 3 y un 1 a los que poner nombre: está en katas para practicar TDD y resuelta paso a paso en la kata Total amount of points con TDD. Si te interesa seguir limpiando código, el siguiente paso natural son las funciones: Funciones, código limpio.

Otros «números mágicos» que no son estos

La expresión se usa en más sitios, y conviene no mezclarlos.

El número mágico de un fichero

Muchos formatos de fichero empiezan siempre por los mismos bytes, como una firma. Se llama también número mágico, y es lo que mira un programa para saber qué tipo de fichero tiene delante, aunque la extensión mienta. Es como el matasellos de una carta: dice de dónde viene sin abrirla.

Puedes verlo con xxd, que muestra los bytes en hexadecimal:

head -c 4 logo.png | xxd -p
head -c 4 datos.zip | xxd -p
head -c 2 nota.txt.gz | xxd -p
89504e47
504b0304
1f8b
Formato Primeros bytes Qué se lee
PNG 89 50 4E 47 .PNG
ZIP (y también .docx, .xlsx o .jar, que son ZIP por dentro) 50 4B 03 04 PK
gzip 1F 8B —

El comando file de Linux se basa en estas firmas para decir qué es cada fichero:

file logo.png datos.zip nota.txt.gz
logo.png:    PNG image data, 192 x 192, 8-bit/color RGB, non-interlaced
datos.zip:   Zip archive data, made by v3.0 UNIX, extract using at least v1.0, last modified Nov 08 2026 20:28:18, uncompressed size 5, method=store
nota.txt.gz: gzip compressed data, was "nota.txt", last modified: Sun Nov  8 20:28:17 2026, from Unix, original size modulo 2^32 5

Fíjate en la diferencia: aquí el número está documentado en la especificación del formato y todo el mundo sabe qué significa. En tu código, 0x89504E47 suelto seguiría siendo un número mágico; const FIRMA_PNG = 0x89504E47 ya no.

Los números mágicos de la física

En física nuclear, los números mágicos son las cantidades de protones o de neutrones que hacen un núcleo atómico especialmente estable: 2, 8, 20, 28, 50, 82 y 126. Maria Goeppert Mayer y J. Hans D. Jensen, cada uno por su lado, los explicaron con el modelo de capas; compartieron la mitad del Nobel de Física de 1963 (la otra mitad fue para Eugene Wigner). No tienen nada que ver con la programación, salvo el nombre.

Y si buscabas los números «mágicos» de la numerología, este no es el sitio: la numerología no tiene nada que ver con ninguno de estos.

Fuentes

  • Maria Goeppert Mayer, The shell model, conferencia Nobel del 12 de diciembre de 1963, apartado «Magic numbers»: da los números 2, 8, 20, 28, 50, 82 y 126, iguales para neutrones y protones. Leída el 8 de noviembre de 2026 en una copia en PDF de la conferencia (Universidad de Atenas).
  • Reparto del Nobel de Física de 1963: la mitad para Eugene Wigner y la otra mitad, compartida, para Maria Goeppert Mayer y J. Hans D. Jensen «por sus descubrimientos sobre la estructura de capas nuclear». Comprobado el 8 de noviembre de 2026 en la página oficial del premio.
  • Firmas de fichero: comprobadas el 8 de noviembre de 2026 con xxd 2025-11-26 y file 5.46 en Linux, sobre un PNG, un ZIP y un gzip creados para la prueba.
  • Martin Fowler, Replace Magic Literal, en el catálogo de Refactoring (2.ª edición). Comprobado el 7 de noviembre de 2026: el nombre y el alias Replace Magic Number with Symbolic Constant figuran en esa página.
  • Tipos de IVA en España (21 %, 10 % y 4 %): administracion.gob.es, actualizada el 20 de marzo de 2026.
  • Subida del IVA general del 18 % al 21 % desde el 1 de septiembre de 2012: Real Decreto-ley 20/2012 (BOE), apartado V del preámbulo.
  • Todos los ejemplos de código se han ejecutado con Node.js 24.21.0; las salidas de los comentarios son las reales.