Hace unos meses empecé a construir Utiligo, una colección
de calculadoras en español: horas extras, IVA, aguinaldo, préstamos, IMC. La
premisa técnica era simple y me la tomé en serio: cero dependencias de
JavaScript en el navegador. Sin React, sin frameworks de UI, sin librerías de
gráficos. Ni una.
Esto es lo que aprendí construyéndolo.
Por qué cero dependencias
La mayoría de estas herramientas hacen aritmética. Sumar horas, aplicar un
porcentaje, dividir un salario entre 30. Enviar 40 KB de framework al navegador
para calcular salario / 30 / 8 es desproporcionado, y en América Latina —donde
está mi audiencia— buena parte del tráfico llega por móvil con conexiones
irregulares.
El stack quedó así:
-
Astro en modo
output: 'static', que genera HTML puro -
<script is:inline>con JavaScript de toda la vida para la interactividad - Cloudflare Pages para servirlo
El resultado: páginas que funcionan antes de que termine de cargar cualquier cosa.
Lo que sí duele de esta decisión
Sería deshonesto contarlo como si no tuviera costes.
Los gráficos hay que dibujarlos a mano. El gráfico de pastel del presupuesto
mensual es SVG generado con trigonometría:
var end = start + pct * 2 * Math.PI;
var x1 = cx + r * Math.cos(start), y1 = cy + r * Math.sin(start);
var x2 = cx + r * Math.cos(end), y2 = cy + r * Math.sin(end);
var large = pct > 0.5 ? 1 : 0;
svg += '<path d="M' + cx + ',' + cy + ' L' + x1 + ',' + y1 +
' A' + r + ',' + r + ' 0 ' + large + ',1 ' + x2 + ',' + y2 + ' Z"/>';
Con una librería serían tres líneas. Aquí son treinta y hay que entender el
arco elíptico de SVG. ¿Vale la pena? Para un gráfico, sí. Para un dashboard
entero, probablemente no.
No hay reactividad. Cada oninput actualiza el DOM a mano. Funciona bien
con diez campos; con cien sería insostenible.
El generador de QR tuve que escribirlo. Codificación Reed-Solomon incluida.
Fue el fin de semana más educativo del proyecto y el que menos recomendaría
repetir.
El bug que me enseñó lo más importante
La calculadora de horas extras aplica un porcentaje sobre el valor de la hora
ordinaria:
var amount = hourlyRate * hours * (rate / 100);
Para Colombia, rate valía 125 — la hora extra diurna se paga al 125% de la
ordinaria. Correcto.
Para México valía 100. Y ahí estaba el problema: el artículo 67 de la Ley
Federal del Trabajo dice que la hora extra se paga "con un cien por ciento
más" del salario ordinario. O sea, al 200%. Yo había codificado el
recargo (100%) en un campo que el cálculo interpretaba como total.
La calculadora llevaba meses pagando la hora extra sencilla en vez de doble.
Exactamente la mitad. Y lo mismo en El Salvador.
Lo encontré auditando cada país contra el texto legal, no leyendo el código. El
código era coherente consigo mismo; lo que estaba mal era la traducción del
dominio. Ninguna prueba unitaria que yo hubiera escrito lo habría detectado,
porque habría afirmado el mismo valor equivocado.
La lección que me llevo
Cuando tu software codifica reglas del mundo real —leyes laborales, tasas de
impuestos, normativa—, el código correcto y los datos correctos son dos
problemas distintos, y el segundo es el difícil.
En esa auditoría encontré también que la jornada nocturna de Colombia empieza a
las 7 p.m. desde diciembre de 2025 (yo tenía las 9 p.m., que era lo válido
hasta la reforma), que Perú no tiene tope legal de horas extra desde 2002
—el artículo que lo fijaba fue derogado, pero medio internet lo sigue citando—
y que el "máximo de 3 horas diarias" que casi todas las fuentes atribuyen a El
Salvador sencillamente no existe en su Código de Trabajo.
Mi conclusión práctica: si tu producto depende de datos normativos, la fuente
tiene que ser el texto legal, y hay que ponerle fecha de verificación. Las
fuentes secundarias se copian unas a otras y arrastran errores durante años.
Si quieres verlo
Está en utiligo.app — gratis y sin registro. Las
calculadoras con implicaciones legales (horas extras, aguinaldo) citan la norma
en la que se basan, precisamente por lo que conté arriba.
¿Alguien más ha construido algo que dependa de datos normativos? Me interesa
mucho cómo resolvéis el problema de mantenerlos al día.
Top comments (0)