Fernando Oviedo
Productos que construí
1 de septiembre de 2025cases.boardcork.com

Casos Criminales - Juego de detectives en tiempo real

Producto digital de Boardcork: hasta 10 dispositivos resolviendo el mismo caso criminal sobre un tablero compartido. En producción, con cuentas y pagos.

Vista en vivo
Tu equipo recibe un caso, un tablero compartido y un expediente completo. Tienen entre 2 y 3 horas para resolverlo. Hasta 10 dispositivos trabajan sobre el mismo tablero, al mismo tiempo. Es el producto digital de Boardcork. Está en producción, con cuentas de usuario y pagos, y lo diseñé y construí de punta a punta.
La primera versión funcionaba perfecto en mi máquina. Con dos pestañas abiertas, impecable. Con un equipo real de seis personas, no. Aparecieron dos síntomas, y los dos eran el mismo problema disfrazado:
  1. Delays. Movías una pista en el tablero y el resto del equipo la veía medio segundo tarde. En un juego cooperativo contra reloj, medio segundo se siente como una app rota.
  2. Conflictos. Dos jugadores tocaban el mismo elemento casi al mismo tiempo y el estado quedaba inconsistente entre dispositivos. Cada uno veía un tablero distinto. En un juego de deducción, eso no es un bug de UI: destruye la partida.
La reacción instintiva era culpar al transporte y ponerse a optimizar los eventos. Ahí me equivoqué al principio, y perdí tiempo. El problema real estaba una capa más abajo: cada evento del tablero escribía en la base de datos. Eso significaba que el camino crítico de una acción de juego era:
La DB estaba dentro del bucle de tiempo real. Con seis jugadores generando eventos en paralelo, esa escritura se volvía el punto de serialización de todo el sistema: sumaba latencia a cada acción y, cuando dos escrituras competían por el mismo registro, generaba exactamente los conflictos de estado que veíamos. Optimizar el socket no iba a arreglar nada. El transporte no era el problema. La arquitectura de persistencia sí.
Cambié la estrategia de persistencia por completo.
  • El estado de la partida vive en memoria mientras la sesión está activa. Es la única fuente de verdad durante el juego, y es autoritativa: resuelve el orden de las acciones en un solo lugar, así que dos jugadores tocando lo mismo ya no pueden divergir.
  • Los eventos se propagan directo desde ese estado, sin esperar a ningún I/O.
  • La escritura a la base de datos ocurre al finalizar la sesión, fuera del bucle de juego, donde una latencia de cientos de milisegundos no le importa a nadie.
La DB pasó de ser un cuello de botella por acción a ser lo que tenía que haber sido siempre: almacenamiento de resultados, no de estado en vivo.
  • Los delays desaparecieron: sin I/O en el camino crítico, el broadcast es inmediato.
  • Los conflictos de estado desaparecieron: con una sola fuente de verdad autoritativa por partida, no hay dos escritores compitiendo.
  • El sistema sostiene los 10 dispositivos simultáneos por sesión que el producto necesita.
Esta decisión tiene un costo real y hay que decirlo: si el proceso se cae a mitad de partida, se pierde el progreso de esa sesión. Lo acepté conscientemente, y la razón es de producto, no de ingeniería. Una sesión dura 2 o 3 horas y se juega de una sentada. La probabilidad de un crash en esa ventana es baja; la probabilidad de que un delay o un conflicto de estado arruine la partida era del 100% con la arquitectura anterior. Cambié un riesgo raro y recuperable por un fallo constante y fatal. En otro dominio - un editor colaborativo donde el usuario invierte semanas - la decisión correcta sería la opuesta: snapshots periódicos o un CRDT. Eso es lo que hace que sea una decisión de arquitectura y no una optimización: depende del dominio, y hay que poder defenderla.
En producción, con cuentas de usuario y pagos. Resolvé tu primer caso en cases.boardcork.com.