Saltar al contenido principal

Educación superior técnico-profesional · 26 sedes en Chile · ~6.000 trabajadores y miles de estudiantes · Desarrollo Web

Desafío PEI — AIEP

"Desafío PEI" es un juego tipo Kahoot construido a medida para que AIEP evaluara —en vivo, simultáneamente y en todo Chile— el entendimiento de su nuevo Plan Educativo Institucional. Llegó a 600-700 trabajadores conectados al mismo tiempo desde 26 sedes, sin WebSockets y con un admin que orquestaba el flujo desde una pantalla.

Next.js 15React 19TypeScriptTailwind CSS 4shadcn/uiPrisma 7PostgreSQLBetter AuthTanStack FormZod 4MotionRechartsReact ConfettiSonnerk6 (load testing)Vercel

Cliente

AIEP — Instituto Profesional

Duración

≈2 meses entre kickoff y evento oficial (octubre–diciembre 2025), con teaser previo de validación en noviembre. Proyecto cerrado tras el evento.

Lanzamiento

17 de diciembre 2025 — evento oficial en vivo a nivel nacional.

Mi rol

Desarrollo end-to-end + acompañamiento durante el evento en vivo

Usuarios

Multi-rol: Participantes (trabajadores AIEP) + Administrador del evento (control central)

Mockup ilustrativo. El sistema real es privado por acuerdo con el cliente — sin capturas ni datos reales.

El problema

Comunicar un PEI a 6.000 personas no es lo mismo que enviar un PDF.

AIEP estaba lanzando su nuevo Plan Educativo Institucional 2025-2029 y necesitaba que todos sus trabajadores —repartidos en 26 sedes a lo largo de Chile— lo conocieran y, sobre todo, lo entendieran. Mandar un correo con un adjunto y rezar no era opción: necesitaban un mecanismo que generara atención real y permitiera medir el entendimiento.

Un evento corporativo nacional pide una sola toma sin segunda oportunidad.

La activación se planeó como un evento en vivo único, transmitido a todas las sedes en paralelo, en dos sesiones (mañana y tarde). Sin repeticiones: si algo fallaba durante una sesión, esa sesión no se podía volver a hacer. La capacidad esperada era de 600 a 700 personas concurrentes, pero el techo había que dejarlo más alto.

Kahoot resolvía el formato, pero no el control.

Los productos genéricos de quiz en vivo (Kahoot, Mentimeter) cubrían el formato base, pero no permitían el grado de control narrativo, branding, segmentación por sede ni el flujo específico que AIEP necesitaba: pausas de revisión, ranking por sede, sets distintos por sesión, sorteo con criterios propios y exportes para RRHH posteriores.

Y el tiempo era el tiempo.

Desde la decisión hasta el evento había ≈2 meses. Suficiente para construir bien, pero no para experimentar con infraestructura cara o riesgosa. La decisión técnica tenía que privilegiar lo que más probabilidades tenía de funcionar el día D.

La solución

Un Kahoot a medida, con la operación que el cliente realmente necesitaba.

Construimos una plataforma propia con dos roles: Participante (vista de juego) y Administrador (control central). El admin orquesta el evento en vivo desde un dashboard: activa cada pregunta cuando corresponde, ve la distribución de respuestas en tiempo real, muestra el ranking por sede en las pausas y dispara el sorteo final entre quienes obtuvieron 10/10.

Polling cada 2-4 segundos en lugar de WebSockets — y funcionó.

La decisión técnica más importante del proyecto fue NO usar WebSockets ni Ably. Para una trivia con preguntas de 60 segundos, una latencia de sincronización de 2-4 segundos es invisible para el usuario, y reduce drásticamente la complejidad operativa: cero infraestructura adicional, menos puntos de falla, costo cercano a cero, debugging trivial. Lo validamos con un load test de k6 simulando 1.000 usuarios concurrentes antes del evento.

UX pensada para evento corporativo real, no para una demo.

Tres decisiones de UX deliberadas: (1) Sin feedback inmediato — el participante no sabe si acertó hasta el final, evitando el abandono temprano que mata el engagement; (2) Entrada tardía permitida — quienes se conectan en la pregunta 5 pueden responder de la 1 a la 5 y sincronizarse, sin penalizar atrasos típicos de eventos corporativos; (3) Pregunta bonus para los que no llegaron a 10/10, manteniendo el juego activo hasta el final para todos.

Dos sets de preguntas, dos sesiones, mismo sistema.

El evento se hizo en dos turnos (mañana y tarde) con sets de preguntas distintos para evitar contaminación entre sesiones. El sistema soporta múltiples GameSession activas en paralelo, cada una con su questionSet (AM/PM) y su propio flujo de control, sin lógica especial.

El día del evento: lo que falló y lo que aprendí.

La sesión de la mañana no pudo partir: el firewall de la red bloqueó el acceso de los participantes a la plataforma. No había validado con anticipación que la red del cliente permitiera la conexión. Resuelto el acceso, la sesión de la tarde corrió con normalidad, con 600-700 personas conectadas y sin problemas de sincronización, y el admin manejó el evento completo desde un único panel.

El load test validó la capacidad, no la red.

La prueba con k6 confirmó que el sistema aguantaba la carga, pero se corrió desde fuera de la red de AIEP. Desde entonces, antes de cualquier evento en vivo hago una prueba de conexión desde la red real del cliente y reviso los accesos con su equipo de TI.

Cómo corre el evento

Un solo administrador dirige el evento y cientos de pantallas lo siguen consultando el estado cada pocos segundos. Así avanza una sesión del Desafío PEI.

  1. 1. Participante

    Ingreso

    Entra con su correo @aiep.cl y queda en espera.

  2. 2. Administrador

    Activa la pregunta

  3. 3. Plataforma

    Estado del juego

  4. 4. Participante

    Respuesta

  5. 5. Administrador

    Ranking por sede

  6. 6. Administrador

    Sorteo 10/10

  7. 7. Participantes

    Pregunta bonus

Paso 1 de 7 · Participante

Entra con su correo @aiep.cl y queda en espera.

En números

600-700

participantes en vivo

Conectados simultáneamente desde 26 sedes en todo Chile.

1.000 VUs

validados con load test

k6 simuló 1.000 usuarios concurrentes con p(95) < 500ms antes del evento.

Arquitectura

Aplicación Next.js 15 (App Router, Turbopack) con React 19 y TypeScript estricto, desplegada en Vercel. Persistencia en PostgreSQL vía Prisma 7 con el driver adapter de pg. Autenticación con Better Auth (validación de correos @aiep.cl). La sincronización de estado de juego se hace por polling: cada cliente consulta `getActiveGameSession()` cada 2-4 segundos vía Server Actions, recuperando la pregunta activa, el tiempo restante y el estado de la sesión. El admin tiene acciones server-side dedicadas (`activateQuestion`, `moveToNextQuestion`, `resumeFromReview`, `moveToRaffle`) que actualizan la GameSession y disparan la actualización implícita en todos los clientes via su próximo poll. Validación end-to-end con Zod compartida entre cliente y server. Stats por sede calculadas server-side con queries Prisma agregadas y renderizadas con Recharts. Carga validada con k6 antes del evento: 1.000 VUs, p(95) bajo 500ms.

Cada elección técnica respondió al constraint dominante: el evento era una sola toma, sin segunda oportunidad. Privilegiamos lo conocido y validado sobre lo novedoso.

Decisiones técnicas

Next.js 15 + Server Actions

Backend liviano

Sin API REST separada: cada acción del admin (activar pregunta, mover al siguiente, sortear) y del participante (responder, refetchar estado) corre como Server Action tipada end-to-end. Para un evento puntual es lo más simple posible — sin servicio aparte, sin contratos duplicados.

Restricción
Necesitábamos lógica server-side robusta pero sin la sobrecarga de mantener una API REST aparte para un evento puntual.
Decisión
Server Actions de Next.js para todo lo transaccional: control del juego, registro de respuestas, cálculo de stats. La validación corre con Zod en el mismo schema que usa el formulario del cliente.
Resultado
Una sola base de código, tipos compartidos sin esfuerzo, deploy atómico de cliente + server en cada push.

Polling 2-4s en lugar de WebSockets

Pragmatismo

La decisión técnica más importante del proyecto. Para un quiz con preguntas de 60s, una latencia de 2-4s es invisible. WebSockets habría sumado infraestructura, costo y puntos de falla sin beneficio perceptible.

Restricción
Sincronizar el estado del juego entre admin y 600-1.000 participantes en un evento de una sola toma, sin margen para fallas.
Decisión
Polling con `setInterval` cada 2-4 segundos contra Server Actions. Cero servidores de WebSockets, cero servicio de tiempo real externo, cero estado en memoria que pueda perderse.
Resultado
Con el acceso habilitado, la sincronización funcionó sin problemas con 600-700 personas conectadas. Costo operacional cercano a cero y capacidad validada con k6 antes del evento.

Prisma 7 + PostgreSQL

Integridad

Las respuestas no podían duplicarse ni perderse. Constraints únicos en (sessionId, userId, questionId) garantizan en base que una persona no responde dos veces la misma pregunta — la app no tiene que adivinar.

Restricción
Bajo carga concurrente, dos clics rápidos del mismo usuario, doble submit por reconexión, o respuestas tardías post-cierre podían contaminar las stats.
Decisión
Modelo relacional en Postgres con UNIQUE constraint en (sessionId, userId, questionId) para OfficialAnswer y en (sessionId, questionId) para QuestionActivation. La integridad la garantiza la DB, no el código de aplicación.
Resultado
Stats limpias en tiempo real durante el evento, sin necesidad de deduplicar a posteriori.

Better Auth

Identidad propia

Validación de correos institucionales @aiep.cl + esquema de roles propio sin lock-in a un SaaS de identidad. Para un cliente educativo grande, los datos de usuarios no se mandan a terceros.

k6 load testing

Validación previa

Antes del evento corrimos un load test simulando 1.000 VUs durante 10 minutos contra los endpoints críticos. Validó que el polling escalaba sin sobrecargar Postgres y que el p(95) quedaba bajo 500ms. Sin ese test, el polling era una apuesta — con ese test, fue una decisión informada.

Funcionalidades

Polling vs WebSockets — y por qué ganó

Sincronización del estado del juego mediante polling cada 2-4 segundos a Server Actions de Next.js. Sin infraestructura de WebSockets, sin servicio externo, sin estado en memoria. Validado con k6 hasta 1.000 usuarios concurrentes con p(95) bajo 500ms.

Admin con control narrativo del evento

Dashboard que orquesta el evento en vivo: activa cada pregunta manualmente, ve la distribución de respuestas en tiempo real, controla las pausas de revisión (después de P4 y P8), muestra ranking por sede y dispara el sorteo final. Una sola persona maneja todo el evento desde una pantalla.

Sin feedback inmediato (anti-abandono)

El participante no sabe si acertó hasta el final del juego. Decisión deliberada para evitar que las personas que fallan temprano se desconecten — todos llegan al final con la sensación de poder ganar. Aumenta el engagement de punta a punta.

Entrada tardía inteligente

Quienes se conectan después del inicio pueden responder desde la pregunta 1 hasta la que está activa. El sistema los sincroniza automáticamente. En eventos corporativos las llegadas tarde son inevitables — penalizarlas era penalizar la participación.

Ranking por sede en pausas

Después de las preguntas 4 y 8 el evento pausa para mostrar el ranking de las 26 sedes según % de respuestas correctas. Genera competencia interna entre campus y mantiene la atención cuando podría caer.

Sorteo final + pregunta bonus

Quienes obtienen 10/10 entran al sorteo aleatorio del premio principal. Quienes no llegaron a 10/10 acceden a una pregunta bonus de consolación, evitando que el juego termine como derrota para la mayoría.

Línea de tiempo

  1. Octubre 2025

    Kickoff

    Definición de scope, arquitectura sobre Next.js + Postgres y decisión clave de polling vs WebSockets. Primeras vistas y modelo de datos.

  2. Noviembre 2025

    Teaser de validación

    Lanzamiento de un teaser previo con 3 preguntas y reintentos para validar UX, copy y comportamiento bajo concurrencia real antes del evento oficial.

  3. Diciembre 2025

    Sprint final + load testing

    Admin dashboard completo, ranking por sede, sistema de sorteo, dos sets de preguntas. Load test con k6 simulando 1.000 usuarios concurrentes.

  4. 17 Dic 2025

    Evento oficial en vivo

    La sesión de la mañana se vio bloqueada por el firewall de la red. Resuelto el acceso, la sesión de la tarde corrió con normalidad, con 600-700 trabajadores conectados desde 26 sedes.

¿Tienes un proyecto similar?

Construyo sistemas a medida de extremo a extremo. Conversemos.