lunes, octubre 12, 2026
Guia

Evita errores en código con reglas externas: una guía para desarrolladores

En el mundo del desarrollo de software, es común que los programas deban interactuar con números y reglas que no son definidos por el propio equipo de desarrollo. Esto es particularmente cierto en dominios como finanzas, impuestos, nóminas o beneficios, donde organismos reguladores establecen cifras y pueden modificarlas sin previo aviso. Lo que en un proyecto tradicional es una constante definida por el desarrollador, en estos casos es una ley impuesta por terceros, sujeta a cambios inesperados y con un valor legalmente correcto que debe interpretarse con precisión.

La experiencia demuestra que ignorar esta distinción puede llevar a errores sutiles, pero costosos. Estos fallos no suelen manifestarse como un error de ejecución o un bloqueo evidente, sino como un cálculo plausible, pero incorrecto, que pasa desapercibido en las pruebas. A continuación, exploraremos tres tipos de errores comunes y las prácticas esenciales para evitarlos al codificar reglas externas.

El error que devuelve un número plausible

Uno de los problemas más insidiosos es aquel que produce un resultado que parece correcto a primera vista, pero no lo es. Un ejemplo clásico se da al calcular intereses por pagos tardíos de impuestos. La lógica aritmética básica podría sugerir multiplicar el impuesto adeudado por la tasa anual y escalarlo por los días de retraso.

const interest = taxOwed * (ratePct / 100) * (days / 365);

Aunque esta fórmula es matemáticamente impecable, en muchos escenarios reales puede resultar en un interés varias veces superior al correcto. La razón reside en la definición de «impuesto adeudado». En el contexto del GST (Impuesto sobre Bienes y Servicios) de la India, por ejemplo, el interés se calcula sobre la parte del impuesto pagada en efectivo, no sobre la obligación bruta total, que puede reducirse significativamente con créditos fiscales por insumos. Si se usa la obligación bruta en lugar del efectivo pagado, el resultado puede ser seis veces mayor.

El problema es que ambos números son válidos aritméticamente y no generan errores en el código. Si las pruebas se diseñan con la misma interpretación errónea, el bug persistirá silenciosamente. La solución no radica en pruebas más sofisticadas, sino en una documentación clara y nombres de parámetros precisos. Al definir una función, es crucial especificar qué cantidad representa cada parámetro y de dónde proviene ese requisito, como en el siguiente ejemplo:

// GSTInterest calcula el interés sobre un pago tardío de GST. // Bajo la Regla 88B(1), el interés se calcula sobre el impuesto realmente debitado del // libro mayor de caja electrónico, NO sobre la obligación bruta. Un error aquí // sobreestima el interés, a menudo varias veces. Pase cashTaxPaid como la porción del libro // mayor de caja, no la factura bruta. // Pase annualRatePct como DefaultGSTInterestRate a menos que la tasa notificada // haya cambiado; 18% p.a. está actualmente notificado bajo la Sección 50(1). func GSTInterest(cashTaxPaid float64, days int, annualRatePct float64) float64

Es fundamental que el nombre del parámetro, como cashTaxPaid, haga imposible la confusión con cantidades similares. Un nombre genérico como amount invita al error, mientras que uno específico lo previene.

La constante que cambia mientras duermes

El segundo tipo de problema surge cuando los valores regulatorios, como los límites de exención o las tasas impositivas, cambian sin previo aviso y sin ajustarse a los ciclos de lanzamiento de software. La implementación inicial suele ser una constante fija en el código:

const gratuityCeiling = 2000000

Esta solución es problemática. No solo el usuario final verá un número obsoleto, sino que el desarrollador que detecte el cambio no podrá corregirlo de inmediato si el valor está incrustado en una dependencia. Además, las cifras regulatorias a menudo no son un valor único para todos; pueden variar para grupos específicos de usuarios (por ejemplo, empleados gubernamentales o personal con contrato fijo). Una constante oculta no solo puede quedar obsoleta, sino que también puede ser incorrecta para una clase entera de usuarios.

La solución es tratar los valores estatutarios como opciones configurables con valores predeterminados bien documentados, en lugar de constantes ocultas. Esto permite que el valor cero de la estructura de opciones mantenga el comportamiento predeterminado para el caso común, sin añadir complejidad innecesaria. Pero, el día que la ley cambie, el usuario o un desarrollador externo puede anular el valor de inmediato, sin depender del ciclo de lanzamiento del software.

// GratuityOptions contiene las cifras estatutarias que cambian por // notificación. El valor cero significa «usar los valores predeterminados». type GratuityOptions struct { Ceiling float64 // 0 -> 20,00,000. Use 25,00,000 para empleados civiles del Gobierno Central. MinYears float64 // 0 -> 5. Use 1 para empleados con contrato fijo. }

La clave es proporcionar un mecanismo de anulación que no esté atado al ritmo de las actualizaciones del equipo de desarrollo.

La validación que vivía en el formulario

El tercer error común ocurre al refactorizar o extraer lógica de negocio de una aplicación a una biblioteca. En una aplicación con interfaz de usuario, es probable que los formularios restrinjan los valores de entrada (por ejemplo, la duración máxima de un préstamo). Si esta lógica de validación no se traslada a la función o biblioteca extraída, un valor de entrada absurdo puede causar problemas graves, como un consumo excesivo de memoria y un bloqueo del proceso del llamador.

rows, _ := Amortisation(principal, rate, months) // ¿de dónde vino months?

Una función de cálculo de tabla de amortización, por ejemplo, podría generar filas indefinidamente si se le pasa un número de meses irrealmente alto. El problema no es un error de cálculo, sino un agotamiento de recursos. La solución es sencilla, aunque poco glamurosa: incorporar las validaciones de entrada directamente en la función.

const MaxMonths = 1200 // 100 años rows, err := Amortisation(principal, rate, 2000) // err: months debe ser como máximo 1200, se obtuvo 2000

El límite debe ser deliberadamente alto, como 100 años para un préstamo, para que nunca se alcance accidentalmente y solo se active con entradas claramente erróneas. La lección es clara: al extraer una biblioteca de una aplicación, se heredan todas las validaciones que la interfaz de usuario realizaba silenciosamente, y su ausencia se convierte en un posible fallo para el nuevo usuario de la biblioteca.

Los tres hábitos esenciales

Para todo desarrollador que escribe código que implementa reglas ajenas, es crucial adoptar estas tres prácticas:

  1. Nombrar la autoridad en el comentario de la documentación: No basta con decir «la tasa». Se debe especificar la regla, la sección y la advertencia de que puede cambiar. Esto permite que quien revise el código sepa contra qué verificarlo y que un lector en desacuerdo pueda consultar la fuente original.
  2. Hacer de los valores estatutarios opciones con valores predeterminados, no constantes: El valor cero debe manejar el caso común, mientras que la capacidad de anulación permite adaptarse a los cambios legales y a las excepciones para clases específicas de usuarios.
  3. Mover la validación del formulario a la función: Cualquier validación que la interfaz de usuario realice debe ser replicada en la biblioteca. Siempre se debe asumir que el próximo llamador no tendrá una interfaz de usuario.

Estas prácticas no son exóticas. Son la disciplina ordinaria de no confiar en las entradas, aplicada a un dominio donde las definiciones de las entradas son establecidas por otros y revisadas según su propio calendario. La razón por la que vale la pena destacarlas es la naturaleza particular de los fallos en este dominio: no hay excepciones, ni rastros de pila, ni resultados obviamente incorrectos. Solo un número con la forma correcta, pero seis veces mayor, esperando en un informe sobre el que alguien está a punto de actuar.

Conclusión

En resumen, al desarrollar software que interactúa con reglas externas, es fundamental reconocer que las «constantes» en el código son, en realidad, leyes de terceros. Adoptar una mentalidad de precaución, documentar meticulosamente las fuentes de las reglas, ofrecer flexibilidad para los valores cambiantes y validar las entradas de forma robusta, son pasos esenciales para construir sistemas fiables y evitar errores costosos y difíciles de detectar.

📝 Basado en información de freecodecamp.org