Hace poco escribí un pequeño fragmento de código que indicaría de una manera amigable para los humanos qué tan antiguo es un evento. Por ejemplo, podría indicar que el evento ocurrió "Hace tres semanas" o "Hace un mes" o "Ayer".
Los requisitos eran relativamente claros y este era un caso perfecto para el desarrollo basado en pruebas. Escribí las pruebas una por una, implementando el código para pasar cada prueba, y todo parecía funcionar perfectamente. Hasta que apareció un error en producción.
Aquí está el código relevante:
now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
return "Today"
yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
return "Yesterday"
delta = (now - event_date).days
if delta < 7:
return _number_to_text(delta) + " days ago"
if delta < 30:
weeks = math.floor(delta / 7)
if weeks == 1:
return "A week ago"
return _number_to_text(weeks) + " weeks ago"
if delta < 365:
... # Handle months and years in similar manner.
Las pruebas comprobaron el caso de un evento que tuvo lugar hoy, ayer, hace cuatro días, hace dos semanas, hace una semana, etc., y el código se creó en consecuencia.
Lo que me perdí es que un evento puede ocurrir un día antes de ayer, mientras que hace un día: por ejemplo, un evento que sucedió hace veintiséis horas sería hace un día, mientras que no exactamente ayer si ahora es a las 1 am Más exactamente, Es un punto algo, pero como el delta
es un entero, será solo uno. En este caso, la aplicación muestra "Hace un día", lo que obviamente es inesperado y no se maneja en el código. Se puede arreglar agregando:
if delta == 1:
return "A day ago"
justo después de calcular el delta
.
Si bien la única consecuencia negativa del error es que desperdicié media hora preguntándome cómo podría ocurrir este caso (y creyendo que tiene que ver con zonas horarias, a pesar del uso uniforme de UTC en el código), su presencia es preocupándome Indica que:
- Es muy fácil cometer un error lógico incluso en un código fuente tan simple.
- El desarrollo basado en pruebas no ayudó.
También me preocupa que no pueda ver cómo podrían evitarse estos errores. Aparte de pensar más antes de escribir código, la única forma en que puedo pensar es agregar muchas afirmaciones para los casos que creo que nunca ocurrirían (como creía que hace un día es necesariamente ayer), y luego hacer un bucle a cada segundo para los últimos diez años, verificando cualquier violación de aserción, lo que parece demasiado complejo.
¿Cómo podría evitar crear este error en primer lugar?