Lo que reveló Sentry: observabilidad, estabilidad y el coste cambiante de monitorizar el código propio
Los errores de producción revelaron una discrepancia de esquema, una recuperación fallida de transacciones y registros poco útiles. Así utilizó un equipo pequeño Sentry para corregir fallos del backend, preparar la monitorización del navegador y controlar el ruido de las alertas.
Antes, la observabilidad era algo en lo que los equipos de ingeniería invertían demasiado —un departamento SRE entero conectaba paneles que nadie consultaba— o que ignoraban por completo —uno se enteraba de que el servicio estaba caído cuando escribía un cliente—. En 2026, ninguno de los extremos es aceptable. Hay que saber qué falla, pero también hacerlo sin pagar tarifas empresariales por un producto independiente.
Este recorrido muestra cómo nuestra integración con Sentry reveló errores de producción, qué cambiamos a raíz de ellos y cómo configuramos la monitorización para controlar el ruido y el consumo. También examina cómo evolucionan las herramientas de observabilidad para equipos pequeños.
Por qué no veíamos los fallos del navegador
El backend está conectado a Sentry desde marzo. El DSN está en Google Secret Manager, el secreto se monta en Cloud Run y sentry_sdk.init se ejecuta en cada inicio. Llegaban alertas por correo. Desde fuera, todo parecía correcto.
Por dentro, no lo estaba. El frontend no estaba conectado a Sentry. Las excepciones del navegador, los errores de hidratación y los fallos al cargar fragmentos eran invisibles. Además, el backend enviaba errores de Python a un proyecto llamado javascript-nextjs, porque era el único proyecto de la organización. Las trazas se acumulaban en el lugar equivocado, la agrupación no servía y el panel mostraba eventos mezclados de distintos lenguajes.
Para corregir esa brecha primero había que separar los fallos del navegador de los errores del backend, antes de abordar errores concretos.
Qué mostraron los datos
Revisamos los eventos de error de los últimos noventa días. Destacaron varios patrones:
Un incidente histórico que ya se había corregido
El error más frecuente del periodo era un StringDataRightTruncation de Postgres: dieciséis eventos en veintidós segundos, procedentes del endpoint de registro social. La causa era una divergencia clásica de esquema: la definición del modelo permitía una biografía de 800 caracteres, pero la columna de la base de datos de producción seguía siendo VARCHAR(500) por una migración anterior. Una persona envió una biografía de algo más de 500 caracteres; la capa de Pydantic la aceptó y la instrucción INSERT falló.
La migración para ampliar la columna a 800 ya se había escrito y se desplegó a la mañana siguiente. Pero el problema de fondo —que el modelo y el esquema de la base de datos pueden divergir sin que nadie lo note hasta que un cliente lo provoca— seguía sin resolverse.
Hicimos dos cambios. Primero, ajustamos el esquema de la solicitud de registro social: especificamos max_length para el correo electrónico, el nombre visible y el token, de modo que las entradas anómalas recibieran un 422 limpio en vez de llegar a la base de datos. Segundo, ampliamos el script de integridad de la base de datos para comparar information_schema.columns.character_maximum_length con las longitudes que espera la aplicación. Si una migración futura reduce una columna por debajo de lo que puede generar el modelo, la siguiente implementación fallará en la comprobación de integridad.
Una sesión que no revertía los cambios
Más abajo aparecía una secuencia: un OperationalError: server closed the connection unexpectedly en POST /v1/upload, seguido de una serie de PendingRollbackError en la misma ruta de código. Postgres había cerrado la conexión en mitad de una consulta, la sesión de SQLAlchemy había quedado en estado inválido y el servicio de subida intentó guardar resultados parciales sobre ella. Cada operación posterior de esa sesión propagó el fallo.
La corrección es pequeña, pero es fácil equivocarse al implementarla. Cuando el flujo de subida captura una excepción, ahora lo primero que hace es db.rollback(), protegido por su propio try/except para que un fallo al revertir no oculte el error original. Después, la lógica de guardado parcial se ejecuta sobre una sesión limpia.
Registros que se quedaban vacíos
El manejador de finalización de WebSocket registraba err=%s junto a una excepción capturada. Para la mayoría de las excepciones eso imprime información útil. Pero para algunos tipos cuya función __str__ devuelve una cadena vacía, solo aparecía err=. Recibíamos eventos de Sentry que indicaban que había fallado un guardado, sin explicar por qué.
Cambiar a logger.exception(...) y err=%r no costó nada y permitió que todos los eventos futuros se expliquen por sí mismos.
Ruido por el que no queríamos pagar
Los elementos restantes de la cola no eran errores del producto. A veces uvloop lanza RuntimeError: unable to perform operation on <TCPTransport closed=True> cuando un cliente se desconecta durante SSL. Es un comportamiento correcto, no algo que podamos arreglar, y con el límite gratuito de 5.000 errores al mes consume presupuesto en vano. Añadimos un filtro before_send que descarta eventos con esos patrones específicos de excepción y valor. Aplicamos la misma idea en el frontend a ResizeObserver loop limit exceeded y otros avisos similares del navegador.
Ajustes para el plan gratuito
Según los datos consultados el 13 de octubre de 2026, el plan Developer de Sentry incluye gratis 5.000 errores, 5 millones de spans de trazas y 50 reproducciones al mes. Las trazas se miden en spans, no con la antigua cuota de unidades de rendimiento. Comprueba el plan vigente y calcula un presupuesto de trazas aparte; actívalas con una tasa de muestreo adecuada. Precios de Sentry
Estos fueron nuestros ajustes:
tracesSampleRate: 0tanto en el backend como en el frontend. El rastreo distribuido es realmente útil, pero el volumen de spans necesita un presupuesto propio basado en el uso medido. Podemos activarlo con una tasa de muestreo baja cuando necesitemos investigar un problema concreto.profilesSampleRate: 0en el backend, por el mismo motivo.- Replay de sesión desactivado en el frontend. Dejamos el código de integración preparado, pero configuramos ambas tasas de muestreo en cero. Si un error crítico requiere el contexto de una reproducción para depurarlo, podemos activar
replaysOnErrorSampleRateen0.05durante un día y volver a desactivarlo. - Filtros
before_senden ambos entornos para descartar eventos que ya sabemos que no requieren acción.
Los cambios del backend redujeron el ruido de errores accionables, mientras que la configuración del SDK del navegador quedó preparada para activarse. Son hitos distintos: el código y los ajustes de muestreo, por sí solos, no demuestran que los eventos del navegador lleguen al proyecto previsto.
Qué está cambiando en el mercado de la observabilidad
Ajustamos la configuración al plan gratuito porque el de Sentry es especialmente generoso para un servicio alojado, pero conviene entender el panorama general: la economía de estas herramientas está cambiando.
En los últimos doce a dieciocho meses han cambiado varias cosas:
- Los estándares abiertos reducen el trabajo de migración, pero no lo eliminan. La instrumentación compatible con OTLP puede reducir la dependencia de un backend de monitorización. Las consultas, paneles, reglas de alerta y funciones de SDK específicas de cada proveedor siguen necesitando planificación y pruebas al cambiar de plataforma.
- El alojamiento propio cambia el cálculo de costes. Opciones como GlitchTip y las pilas nativas de OTLP ofrecen otra vía de monitorización. Hay que comparar almacenamiento, copias de seguridad, actualizaciones y tiempo de ingeniería con el plan alojado, no solo una factura de máquina virtual con el precio de la suscripción.
- La depuración asistida por IA amplía el flujo de trabajo. Seer de Sentry propone causas raíz y parches, mientras que una integración MCP autorizada puede llevar el contexto de los errores a un asistente de programación. Todo cambio propuesto aún necesita reproducción y pruebas de regresión antes de publicarse.
- Los límites del plan merecen tanta atención como sus funciones. El volumen de errores, la retención, el muestreo y el acceso del equipo pueden influir en la factura. Conviene empezar por las señales necesarias y un presupuesto de eventos medido, y después comparar los planes con esa carga de trabajo.
En esta instantánea de la versión, Sentry cubría nuestras necesidades inmediatas de monitorización de errores. Usar interfaces abiertas cuando existen da más opciones al equipo a futuro, pero cambiar de proveedor todavía exigiría comprobar el enrutamiento de proyectos, las alertas, los datos retenidos y las funciones propias de cada proveedor.
Las comprobaciones de despliegue completan la monitorización
En la instantánea de esta publicación, del 17 de mayo de 2026, quedaban dos tareas de despliegue:
- Crear un proyecto de Sentry específico para Python, para que los errores del backend dejaran de registrarse bajo
javascript-nextjs. El DSN se cambiaría en la misma entrada de Secret Manager que ya lee el backend. - Añadir el DSN del frontend a la configuración del proyecto de Vercel para que empezaran a llegar los errores del lado del cliente. El código ya estaba preparado y se inicializaba de forma condicional; solo faltaba la variable de entorno.
En cualquier puesta en marcha de monitorización, hay que enviar un evento de prueba controlado desde cada entorno y confirmar el proyecto, el entorno, la agrupación y el contexto antes de dar la integración por terminada. Crear proyectos separados y añadir una variable de entorno son pasos de configuración, no pruebas de que los eventos hayan llegado.
Por qué importa
Cuando no puedes ver qué se rompe, inviertes demasiado en código defensivo o inviertes demasiado poco y publicas errores. Ambas cosas salen caras. Una observabilidad económica y fiable permite que un equipo pequeño trabaje como uno más grande: detectar regresiones antes de que las noten los clientes, corregirlas sin adivinar y dedicar las horas de ingeniería a construir cosas nuevas.
La lección útil es verificar las dos partes: las correcciones de la aplicación y la ruta de monitorización que debería revelar la próxima regresión.