Ingeniería
Aplicada
Resultados
samuel-torres.com
Ingeniería de backend, diseño de concurrencia y desarrollo asistido por IA para un sistema de canje con dinero real, multi-tienda y multi-día.
PHP · MySQL · JavaScript · React (app de piso) · Claude Code (programación asistida) · Zonas horarias IANA
Caravana necesitaba correr una campaña de puntos y canje de premios en piso de venta, en tiendas de varias cadenas, durante varios días consecutivos. Había dinero real de por medio: un doble cobro no es un bug en un tablero, es un cliente enojado en el mostrador con gente detrás esperando. El sistema tenía que sostener seis restricciones a la vez — dinero real, WiFi de tienda poco confiable, operadores no técnicos con prisa, servidor compartido, días consecutivos donde cada uno depende del cierre correcto del anterior, y varias zonas horarias con corte a las 5 PM hora local.
Requisitos, diseño visual, programación asistida por IA, presentación, capacitación y producción. La verificación humana en capacitación y producción regresó trabajo a programación más de una vez — el diagrama de abajo muestra el ciclo, no una línea.
Lo que importa aquí no es que hubo juntas — es qué se decidió y qué se descartó. El bono de puntos quedó como captura manual a propósito, para no inventar una regla que el cliente no había definido. La hora de corte pasó por tres valores antes de fijarse en las 5 PM hora local. Encontrar ambigüedades temprano es el trabajo real de esta fase.
Hecho en Claude Design y navegable antes de escribir código de producción. Eso hizo que programación nunca discutiera lo visual — una regla escrita (el diseño ya está aprobado, solo se corrige lo roto) evitó rediseñar bajo presión. Limitación honesta: el mockup usaba datos falsos; era referencia visual, no especificación de comportamiento.
Un commit por paso, prompts como archivo completo, protocolo "reporta y continúa", lista explícita de comandos que requieren autorización puntual, respaldo obligatorio antes de ediciones destructivas, evidencia cruda para cada afirmación, pruebas de concurrencia corridas en paralelo (nunca secuenciales), y arnés de pruebas en verde actualizado en el mismo commit que agrega una pantalla.
Los cambios que pidió el cliente fueron de reportes y visibilidad — no de lógica de negocio. Es señal de que la fase de requisitos funcionó.
El material salió de los modos de falla del sistema, no de una lista de funciones — capacitar sobre lo que más se puede malinterpretar.
Primera activación real sin fallos, uso desde tablet confirmado directamente en los registros del servidor.
Teléfono único, clave de idempotencia, y una restricción de base de datos que garantiza una sola activación en curso en todo el sistema. Una validación en código la brincan dos peticiones casi simultáneas; un UNIQUE de base de datos, no. Los bloqueos de fila siempre se adquieren en el mismo orden (inventario antes que saldo), con reintento ante bloqueo cruzado — invertir ese orden es la receta clásica del deadlock.
No con el intento HTTP. Generarla al tocar el botón hace que un reintento por timeout de WiFi de tienda produzca una clave nueva — y cobre doble, justo en el escenario para el que se construyó esta defensa.
El registro del canje se escribe como el primer statement de la transacción, antes de tocar saldo o inventario. Cuando la clave única choca, la respuesta es 200 con el canje original — un error ahí haría que la pantalla reintentara con clave nueva, y ahí ocurre el doble cobro.
Nunca con las funciones de fecha del motor de base de datos — el servidor de BD no tenía cargado el catálogo de zonas con nombre y fallaba en silencio. El bug real que esto evitó: con el sitio en UTC, el día se cortaba a las 6 PM hora de México en vez de las 5 PM.
El estatus de una activación se deriva de los hechos guardados, nunca se escribe como texto suelto. La columna vieja que sí guardaba el estatus como texto hoy muestra valores que ya no coinciden con la realidad.
El stock inicial se congela en el primer movimiento del día, no al activar — mercancía que se sigue cargando es preparación, no operación. El reporte del día se congela al cerrar y no se recalcula — una corrección posterior no puede cambiar en silencio un número que el cliente ya recibió como definitivo.
Una fila de auditoría o un correo encolado desaparecen limpiamente con un rollback; el envío real del correo y la escritura de archivos ocurren fuera. Un fallo en la bitácora nunca aborta la operación de negocio.
Fuera de la carpeta pública, con token HMAC y expiración, para que los enlaces del reporte abran con un clic desde una hoja de cálculo sin una URL adivinable. El redirect es reducción de superficie, no la frontera de seguridad — la frontera real es el rechazo del servidor a nivel de endpoint, escrito así como comentario en el código.
Se ejecutan cuando la app de piso consulta el estado de la jornada — el único punto del sistema donde una petición está realmente garantizada.
Para que un administrador con la pantalla de catálogo abierta no sobrescriba, con un valor absoluto, los canjes hechos en piso mientras tanto.
Interruptor que no requiere despliegue, tope por hora bajo el límite del servidor compartido, y SPF/DKIM/DMARC alineados.
Monta los archivos reales de producción sobre React y un DOM real, hace login simulando eventos reales de usuario, y falla la corrida ante cualquier advertencia de Hooks de React — no solo ante errores.
No vendo esta metodología como infalible. Tres incidentes, una sola causa raíz de fondo: el agente corre con privilegios elevados, y todo lo que crea nace con esos mismos privilegios.
Que los mensajes no mintieran. "Sin configurar" se mantuvo distinto de "agotado" en requisitos; se diseñó un estado vacío para que no se leyera como pantalla rota; cada texto que describía mal una condición tenía un bug real detrás en programación; capacitación enseñó exactamente eso. Mismo criterio, cuatro momentos distintos — la idea más transferible de todo el proyecto.
54
activaciones programadas
139
tiendas
19
productos (subió de 14, dos días antes del evento)
1,026
filas de inventario
10
zonas horarias soportadas, 2 en uso real
50+
archivos en tres capas, partidos de un archivo de 1,858 líneas
4
días consecutivos encadenados por la restricción de una sola activación activa
10 / 0
peticiones simultáneas en la prueba de concurrencia — una gana, 0 errores de servidor
No cito: clientes atendidos, ingresos, tasa de conversión — no están verificados.
Y con honestidad: nunca se probó sosteniendo una tablet con luz real de tienda y dedos reales con prisa; nunca se verificó la entrega de correo a un proveedor de bandeja específico; y el correo del cliente es un campo opcional — si el operador no lo pide, ese cliente no recibe nada.
Fotos de stock ilustrativas mientras tanto — cada captura real de este sistema muestra el logo del cliente o datos reales de un cliente, y este caso de estudio corre sin ninguno de los dos.
Login del operador en la app de piso (foto ilustrativa).
Alta de cliente — flujo completo, punto C (foto ilustrativa).
Canje exitoso — idempotencia en la práctica, punto 3 (foto ilustrativa).
Reporte congelado y bitácora completa, punto 6 (foto ilustrativa).
Agenda de activaciones multi-tienda y multi-día (foto ilustrativa).
Catálogo con edición de concurrencia optimista, punto 10 (foto ilustrativa).
Estas son fotos de stock que sustituyen capturas reales, no son capturas del sistema real — cada captura real revisada mostraba el logo del cliente incrustado en la interfaz, el nombre y teléfono real de un cliente, o la IP real de un servidor, así que ninguna se pudo publicar tal cual.
Sistema de canje de puntos con dinero real para 139 tiendas en 54 activaciones programadas: candados de concurrencia a nivel de base de datos, clave de idempotencia atada a la operación lógica, y zonas horarias IANA calculadas en la aplicación.