Votar ya no es solo poner un marcador
Las ultimas 24 horas en Nima Project se concentraron casi por completo en world-cup-porra y en un ajuste de cierre en world-cup-api:
2026-06-13 10:48 +0200-Add upcoming match voting view2026-06-13 10:59 +0200-Show all today matches in voting2026-06-13 11:07 +0200-Fix app subpath asset routing2026-06-13 11:27 +0200-Show match times on vote cards2026-06-13 11:29 +0200-Keep today voting list to upcoming matches2026-06-13 11:56 +0200-Fix today upcoming voting grouping2026-06-13 12:35 +0200-Show browser match time and max points2026-06-13 12:53 +0200-Always show vote points summary2026-06-13 19:47 +0200-Accept any match goalscorer for bonus2026-06-13 19:54 +0200-Accept any match goalscorer in API scoring2026-06-13 20:00 +0200-Show goalscorer difficulty release notice
La lectura superficial diria: se han afinado unas tarjetas de voto y una regla de bonus.
La lectura buena es otra: Ultimate Porra dejo de tratar el voto como un formulario pequeño y empezo a tratarlo como una decision de producto que tiene que explicar contexto, reglas y limites antes de pedir una prediccion.
La frase del chat que cambia el alcance no hablaba del Mundial
La conversacion mas util de este tramo no fue sobre un partido. Fue sobre lo que viene despues:
Tenemos que sacar tres modelos de datos... seguir el modelo que estamos utilizando con el Mundial... y que se pueda añadir en la admin console un creador de competiciones, gestion de reglas y equipos.
Eso cambia el foco.
Ya no se estaba hablando solo de arreglar la porra del Mundial.
Se estaba poniendo a prueba si el modelo actual sirve para:
- Supercopa de Europa,
- Champions,
- Copa de Espana,
- reglas diferentes,
- equipos diferentes,
- y una futura gestion desde admin.
Cuando aparece esa exigencia, deja de valer una UI que solo "parece funcionar" para el torneo actual.
Hay que hacer visibles las reglas que estaban escondidas en el codigo.
El primer bloque de commits ataco una duda basica: que partidos tocan ahora
Los commits de la manana giraron alrededor de una confusion muy concreta:
- vista de proximos partidos,
- todos los partidos de hoy,
- solo los partidos futuros dentro de hoy,
- y agrupacion correcta de esa lista.
Eso suena a detalle de presentacion.
No lo era.
Era una correccion de semantica temporal.
Una porra no necesita ensenar "todos los partidos existentes". Necesita ensenar los partidos sobre los que todavia tiene sentido votar.
Ese matiz cambia la interfaz:
| Antes | Ahora |
|---|---|
| lista generica de partidos del dia | lista pensada para accion pendiente |
| mezcla de partidos ya jugados y votables | solo proximos dentro de la ventana util |
| el usuario interpreta el estado | la interfaz lo filtra y lo explica |
En cuanto una competicion tenga mas densidad, mas jornadas o formatos distintos, ese filtro deja de ser comodidad. Pasa a ser contrato de producto.
La segunda capa hizo visible otra cosa: el tiempo del partido no es un adorno
Despues llegaron los cambios de hora:
Show match times on vote cardsShow browser match time and max points
La leccion aqui es fuerte porque parece obvia demasiado tarde.
Si el voto se bloquea por horario y la puntuacion depende de fase o dificultad, entonces la tarjeta de partido no puede limitarse a:
- escudos,
- equipos,
- y dos inputs de goles.
Necesita ensenar al menos tres cosas:
- cuando empieza el partido para ese navegador,
- cuanto puede llegar a puntuar,
- y bajo que regla se esta puntuando.
Eso empuja la tarjeta de voto desde "formulario minimo" hacia "superficie de decision".
La idea central es sencilla: si el usuario tiene que decidir con reglas, la UI no puede ocultar esas reglas.
El resumen de puntos dejo clara otra frontera: la porra ya no puede vivir de intuicion
Always show vote points summary remata la misma direccion.
Antes podia haber una expectativa implicita:
"yo voto y luego ya vere cuantos puntos salen".
Ahora la app se mueve a otra posicion:
"antes de guardar, te dejo claro que significa este voto en puntos posibles".
Eso no solo mejora UX.
Tambien baja ambiguedad operativa:
- menos sorpresa cuando se recalcula,
- menos discusiones sobre por que algo vale mas o menos,
- y menos dependencia de recordar reglas de memoria.
En una sola competicion eso ya ayuda.
En varias competiciones futuras, es obligatorio.
Porque el problema deja de ser "que regla existe" y pasa a ser "que regla esta activa aqui y ahora".
El bonus de goleador obligo a escribir mejor la regla de negocio
La segunda mitad del dia fue mas de dominio que de UI:
Accept any match goalscorer for bonusAccept any match goalscorer in API scoringShow goalscorer difficulty release notice
El cambio importante no fue solo aceptar otro valor.
Fue reconocer una friccion de producto:
si la regla de bonus por goleador existe, pero el sistema solo la valida bajo una interpretacion demasiado estrecha, la experiencia parece arbitraria.
Por eso hubo que tocar a la vez:
- frontend,
- scoring local,
- scoring en API,
- tests,
- y notificacion visible del cambio.
Ese orden importa.
Una regla de juego no esta cerrada cuando compila. Esta cerrada cuando:
- la interfaz deja introducirla correctamente,
- el backend la calcula igual,
- los tests fijan esa lectura,
- y el usuario entiende que ha cambiado.
El detalle del subpath tambien conto una historia de producto
Fix app subpath asset routing puede parecer el commit mas tecnico y menos relacionado con el resto.
En realidad va en la misma direccion.
Si la app necesita vivir correctamente bajo un subpath, es porque ya no se esta pensando solo como HTML suelto o demo local. Se esta pensando como artefacto desplegable, reusable y encajable dentro de otras superficies.
Eso encaja con el chat de hoy:
- nuevas competiciones,
- modelos reutilizables,
- admin console como superficie de gestion,
- y reglas/teams configurables.
Todo eso exige despliegue y routing menos ingenuos.
La idea fuerte de estas 24 horas: la tarjeta de voto ya es parte del motor
Durante mucho tiempo es tentador tratar una porra como dos capas separadas:
- por un lado las reglas "de verdad",
- por otro lado una UI ligera para recoger predicciones.
Estas 24 horas van en direccion contraria.
La tarjeta de voto ya participa del contrato del sistema porque:
- decide que partidos son accionables,
- traduce el horario a la realidad del usuario,
- adelanta la puntuacion maxima,
- deja ver el resumen de puntos,
- y comunica cambios de criterio en bonus delicados.
Eso no significa meter toda la logica en frontend.
Significa aceptar que el frontend ya no es un teclado bonito delante del scoring. Es parte de la explicacion publica del scoring.
Lo que me llevo de este tramo
- Una porra deja de ser "poner resultados" en cuanto el momento del partido, la fase y los bonus cambian el significado del voto.
- Filtrar por partidos realmente votables es una regla de producto, no un detalle de UX.
- Mostrar hora local y puntos maximos convierte una tarjeta en una superficie de decision, no solo de entrada.
- Una regla de bonus solo esta bien cerrada cuando frontend, API, tests y mensaje al usuario cuentan la misma historia.
- Si el siguiente paso son varias competiciones gestionadas desde admin, las reglas ya no pueden quedarse implicitas en el codigo ni en la memoria del operador.
Estas no fueron 24 horas de "mejorar unas cards".
Fueron 24 horas en las que ultimate-porra acepto algo mas serio: votar no es solo rellenar un marcador. Es interactuar con un modelo de competicion, una ventana temporal y una logica de puntuacion que la interfaz tiene que hacer visible si de verdad quiere escalar.
Parte de mis notas de producto y plataforma. Sigue el blog o escribeme.