Contar mal no es un bug de Python: es un bug de diseño
Cuando un reporte contaba turnos en lugar de días, y cómo corregir un error de modelado de dominio.
Feedback público de un error real en producción
Trabajo como desarrollador contractor para una empresa que brinda servicios. Y cuando diseñé el sistema en el que se apoyan los empleados administrativos para hacer liquidación de sueldos partí de una idea que parecía razonable:
Pocas personas trabajarían un día feriado, y ninguna trabajaría dos turnos durante un mismo día feriado.
Ese supuesto no era arbitrario: se apoyaba en la experiencia previa del negocio, en estadísticas históricas y en una lógica operativa que, hasta ese momento, nunca había sido violada.
Este artículo es un ejercicio deliberado de feedback público en forma de code journal: un error de diseño que cometí, cómo se manifestó en producción y qué aprendí al corregirlo.
El síntoma
RRHH me escribe con una duda simple:
“Generé el Excel del resumen mensual que utiliza el contador de la empresa para liquidar sueldos y a esta persona le figuran tres feriados trabajados, pero este mes solo tuvo dos feriados.”
El sistema estaba en producción. El Excel salía directo del backend. Y no había ediciones manuales.
Algo no cerraba.
El caso real que rompió el supuesto
Al revisar el detalle, apareció esto:
- Día feriado del 08/12/2025:
- Turno de 06:00 a 14:00
- Turno de 22:00 a 06:00
- Día feriado del 25/12/2025
- Turno de 06:00 a 14:00
Resultado humano esperado: 2 feriados trabajados. Resultado del sistema: 3 feriados.
Pero el sistema no estaba “inventando” nada. Estaba haciendo exactamente lo que le pedí.
Cómo estaba modelado el problema
En mi módulo de gestión operativa existe el concepto de Turno como clase. Cada turno cargado recibe un clasificador de horas según el Convenio Colectivo de Trabajo que aplique al Sindicato de la persona que lo efectuó:
- horas normales
- horas al 50%
- horas al 100%
- horas nocturnas
- feriados
A nivel código, los clasificadores devolvían algo así:
return {
"total": total_horas,
"normales": ...,
"50": ...,
"100": ...,
"nocturnas": ...,
"feriados": 1 if feriado else 0
}
Es decir: si este turno ocurrió en un feriado, marcamos feriados = 1. Ese fue el criterio que me solicitaron al diseñar el ERP: sólo necesitamos contar la cantidad de feriados que un empleado trabajó en el mes.
Hasta acá, todo correcto… si “feriado” significa “turno en feriado”.
El problema es que esa palabra ya estaba cargada semánticamente para el negocio: para RRHH, feriados siempre significó días feriados trabajados, no eventos que cayeron en un feriado.
Dónde nació el bug
El problema apareció en el reporte mensual descargado en el Excel, cuando agregué datos por persona.
La lógica que alimentaba la planilla generada en openpyxl era conceptualmente así:
for turno in turnos_del_mes:
if turno.es_feriado:
# Aquí está el error: Si hay 2 turnos el mismo día, suma 2.
resumen["feriados"] += 1
Ese += 1 es el corazón del problema.
Yo estaba sumando feriados por turno, pero el negocio necesitaba contar días feriados distintos.
Con dos turnos el mismo día feriado (por ejemplo, el 08/12/2025):
- el sistema suma 2
- RRHH espera 1
El Excel no tenía la culpa. El cálculo tampoco estaba “mal”. El error estaba en qué estaba contando realmente ese número.
Evento vs. Entidad Lógica
Este bug no es de Python, ni de Django, ni de fechas.
Es un error clásico de modelado:
- Evento: un turno ocurre en un feriado
- Entidad lógica: un día feriado fue trabajado
Yo confundí ambos conceptos y les di el mismo nombre: feriados.
Mientras el supuesto inicial se cumplía (un turno por feriado), el error dormía. Cuando la realidad cambió, el bug apareció.
Un empleado canceló su turno.
Alguien que ya había trabajado ese día se ofreció voluntariamente a cubrirlo.
Salió de su turno anterior, descansó y volvió a trabajar el mismo día.
Por qué este tipo de error es peligroso
Porque:
- no rompe el sistema,
- no lanza excepciones,
- no falla tests simples,
- y produce números plausibles.
El dato parece correcto hasta que alguien del negocio lo mira con atención.
Este tipo de bug erosiona confianza, que es mucho más caro que una excepción.
La corrección
Para contar días feriados trabajados, no hay que sumar eventos. Hay que deduplicar por fecha.
La lógica correcta es:
feriados_trabajados = set()
for turno in turnos_del_mes:
if turno.es_feriado:
feriados_trabajados.add(turno.inicio.date())
cantidad_feriados = len(feriados_trabajados)
El cambio no fue técnico, fue conceptual: dejé de contar eventos y empecé a contar fechas.
Dos turnos el mismo día → una sola fecha → un feriado.
Esto no invalida el concepto de “turnos en feriado”. Simplemente reconoce que son métricas distintas.
La lección que me llevo
Este error me dejó una regla que ahora intento aplicar siempre:
Si un número aparece en un reporte, su significado debe poder explicarse en una sola frase sin ambigüedades.
Yo no podía explicar qué significaba exactamente “feriados” sin mirar el código. Y eso ya era una señal de alerta.
Por qué publico este error
Porque este tipo de fallas:
- no vienen de falta de conocimientos,
- vienen de suposiciones implícitas,
- y aparecen recién cuando el sistema toca la realidad.
Voy a intentar escribir más seguido este tipo de feedback público: errores de diseño propios, reales, ya corregidos. No como acto de humildad performativa, sino como práctica profesional.
Si este artículo evita que alguien cuente mal algo que parecía obvio, ya cumplió su objetivo.
El sistema no se equivocó.
Yo me equivoqué al pedirle que cuente algo distinto de lo que necesitaba.
Y ese tipo de bug —el que nace de un supuesto falso— no se arregla con más código, sino con mejores preguntas.