AirHelp, Tecnología para el líder en defensa de los derechos de los pasajeros
Una frase sobre cuánta compensación le corresponde a un pasajero no es una frase traducida veinticuatro veces. Son un par de docenas de afirmaciones jurídicas independientes, y muchas de ellas solo son ciertas dentro de una frontera. Un vuelo de Varsovia a Londres queda bajo un régimen distinto tras el Brexit que ese mismo vuelo dentro de la Unión, y un vuelo desde São Paulo se rige por normas que no tienen relación con ninguno de los dos. Ese hecho condicionó el modelo de contenido de este proyecto más que cualquier elección de framework, así que encabeza el texto en lugar de quedar enterrado bajo una lista de tecnologías.
AirHelp fue fundada en enero de 2013 por Henrik Zillmer y tiene su sede en Berlín. Su base legal en Europa es el Reglamento 261/2004, y fuera de la Unión trabaja con UK261, el Convenio de Montreal, la ANAC 400 brasileña y varios marcos nacionales más. Ha ayudado a más de 13 millones de personas a entender sus derechos y reclamar compensación por vuelos retrasados, cancelados o con overbooking, las representa en litigios con aerolíneas y presiona a nivel gubernamental por una regulación justa.
Diseñé e implementé el sitio de AirHelp dirigiendo un equipo de cinco personas como Team Pilot. La implementación duró alrededor de seis semanas desde el análisis de alcance hasta la publicación, y la maquetación y colocación de elementos las aportó el cliente.
La arquitectura multilingüe y su regla por defecto
La consecuencia directa de lo anterior es que cada variante de idioma tiene que ser un documento de pleno derecho con su propio ciclo de aprobación, y no un campo de traducción colgado del original. Una variante que no ha pasado por la revisión de un abogado de esa jurisdicción no puede caer en silencio al inglés, porque entonces el sitio publica una situación jurídica ajena como si fuera la propia. Tiene que desaparecer de la navegación y del hreflang. Ese es el único comportamiento por defecto seguro, y es precisamente el que los plugins multilingües genéricos resuelven mal, porque su preferencia siempre es mostrar algo antes que no mostrar nada.
La arquitectura del frontend se apoya en Next.js con renderizado en servidor y cubre 24 idiomas mediante i18n, con WCAG 2.1 presente en las revisiones y optimización para dispositivos móviles. El renderizado en servidor no es aquí una preferencia estética. Con contenido jurídico que debe posicionar en decenas de mercados a la vez, y con móviles en redes pobres, trasladar el ensamblado de la página al navegador del visitante significa que el teléfono más débil en la peor red carga con el mayor trabajo.
Hay un segundo efecto menos evidente. Veinticuatro variantes que se aprueban por separado avanzan a ritmos distintos, así que en cualquier momento el sitio contiene versiones en estados diferentes de la misma página. El sistema de rutas tiene que tratar eso como normalidad y no como error, porque la alternativa es bloquear la publicación de un mercado hasta que los otros veintitrés estén listos, y eso en la práctica significa no publicar nunca.
El propósito de AirHelp y su audiencia
Dos públicos usan este sitio y casi no tienen nada en común.
El primero es una persona en una situación inusual: sentada en una terminal después de una cancelación, con el móvil, a menudo en itinerancia, con mala cobertura, molesta, y sin más documento que una fotografía de la tarjeta de embarque. No vino a leer sobre la empresa. Vino a saber si le corresponde algo, y quiere la respuesta en dos pasos.
El segundo llega desde un buscador meses después del vuelo, buscando un punto legal concreto. Ese tráfico es estable, muy cacheable, lee textos largos y aterriza en páginas en veinticuatro idiomas.
Una sola plataforma atiende ambos patrones bajo el mismo dominio, y de ahí procede casi toda la tensión arquitectónica del proyecto. Lo que es correcto para el primer público, marcado mínimo, renderizado en servidor, nada que bloquee el formulario, se desperdicia en el segundo. Lo que es correcto para el segundo, caché agresiva en el borde de la red, resulta inservible para el primero, porque el estado de una reclamación es personal por definición.
Detrás del contenido hay colaboración con despachos en 30 países y un equipo de 700 empleados, incluido el mayor grupo de abogados del mundo especializado en derecho aéreo. Para el sitio eso significa que el texto jurídico tiene un propietario real en el lado del cliente y pasa por aprobación antes de llegar a producción. El modelo de contenido tenía que sostener ese flujo, no esquivarlo.
Características técnicas de AirHelp
El proceso de reclamación es un formulario de varios pasos que carga datos de vuelo por GraphQL, se conecta con las API de las aerolíneas y guarda la solicitud en PostgreSQL con cifrado AES-256. Elegir GraphQL frente a un conjunto de llamadas REST tiene un motivo concreto: cada paso del formulario necesita una porción distinta del mismo registro de vuelo. Con REST eso termina o bien trayendo todo por adelantado, o bien en un número de peticiones que crece con el número de pasos. Pedir exactamente los campos que dibuja el paso actual lo reduce a un viaje de red por paso, y en un teléfono en itinerancia esa es una diferencia que se nota, no que se mide.
La sección informativa con artículos jurídicos se carga por REST API con caché en Redis y se renderiza en React. La separación entre ambas interfaces es deliberada. El contenido editorial es idéntico para cualquier visitante y admite caché agresiva. Los datos de una reclamación no admiten caché en absoluto. Mantener los dos detrás de una sola interfaz habría ahorrado algo de código y costado la posibilidad de fijar un tiempo de vida distinto para cada uno.
El panel de seguimiento muestra el estado en tiempo real por WebSocket, con caché en Memcached. Conviene nombrar el compromiso. Una conexión persistente consume recursos de servidor mientras la pestaña siga abierta, y el estado de una reclamación cambia quizá una vez cada varias semanas. Consultar una vez por minuto sería más barato de operar y suficiente para la información en sí. El argumento a favor del canal abierto es lo que ocurre el día en que el estado sí cambia, porque ese día el usuario está en esta página recargándola a mano, y cada recarga manual cuesta más que mantener el canal.
Las copias de seguridad van automáticamente a Amazon S3, con replicación entre regiones, versionado y compresión Zstandard. Aquí el versionado pesa más que la replicación. La replicación protege frente a perder un centro de datos, algo poco frecuente. El versionado protege frente a sobrescribir datos por un error propio en un despliegue, algo nada infrecuente, y una copia replicada a tres regiones es entonces el mismo error por triplicado.
El SEO técnico cubre expresiones como «compensación por vuelo retrasado», sitemaps XML dinámicos e indexación acelerada. Con contenido jurídico que cambia después de cada resolución relevante, un sitemap generado una sola vez en el build deja de describir el sitio en cuestión de una semana, así que se construye a partir del estado actual de la base de datos.
Accesibilidad y resistencia del formulario
WCAG 2.1 en un sitio de reclamaciones no trata principalmente de contraste y textos alternativos. El componente accesible crítico es el formulario de varios pasos, y lo que decide si funciona es justo aquello que una auditoría automática no detecta: si el mensaje de error está asociado por código al campo que describe, si avanzar de paso mueve el foco al encabezado del nuevo paso, si el progreso se anuncia siquiera. Un formulario que vuelve arriba tras un error de validación sin mover el foco es un bucle sin salida para quien navega con teclado, y aprueba todas las pruebas automáticas.
Sobrevivir a una caída de conexión se trató como requisito funcional, no como comodidad. El estado del formulario se guarda localmente tras cada paso, de modo que perder la sesión en una sala de embarque no borra quince minutos de trabajo. El coste es real y conviene decirlo: los datos de la reclamación incluyen número de vuelo y datos personales, y guardarlos en el navegador exige su propia rutina de borrado tras el envío y una regla clara sobre cuándo caduca un borrador abandonado. Sin esa regla, una comodidad se convierte en un pasivo de cumplimiento.
La validación sigue la misma lógica. Tienta rechazar de inmediato, en el navegador, un número de vuelo que no encaja con el patrón. En la práctica los patrones varían entre aerolíneas, y el pasajero copia el número de una foto torcida de la tarjeta de embarque. Un aviso que no bloquea el envío acepta la reclamación de quien confundió un cero con una letra. Una validación dura la descarta, y nadie se entera.
Desafíos de rendimiento y soluciones
La carga de base de datos recaía sobre PostgreSQL. La respuesta fue Redis con persistencia para las consultas más frecuentes y sharding con réplicas de lectura en Amazon RDS. Las réplicas de lectura tienen un precio fácil de olvidar: la replicación es asíncrona, así que una lectura inmediatamente posterior a un envío puede no ver todavía la reclamación. El usuario la presenta y aterriza en una lista vacía, que se parece exactamente a una pérdida de datos. Por eso las lecturas inmediatamente posteriores a una escritura van a la instancia primaria, y solo se distribuyen las rutas que toleran unos cientos de milisegundos de retraso.
La lentitud del formulario venía de la integración con las API de aerolíneas, sobre todo tras cancelaciones masivas. Las llamadas pasaron a procesamiento asíncrono con RabbitMQ, con repliegue a datos estáticos cacheados en Elasticsearch cuando expira el tiempo de espera. El repliegue es la mitad importante. Los sistemas de las aerolíneas suelen estar caídos justo cuando más falta hacen, porque la misma incidencia que canceló los vuelos está castigando su infraestructura. Un formulario que muestra un error en ese momento pierde la reclamación de alguien con pleno derecho a compensación.
La latencia alta de imágenes castigaba a los móviles en regiones con mala conectividad. Fastly con compresión Brotli, WebP y carga diferida mediante Intersection Observer API acortó la entrega, y la geo-optimización acortó la distancia. Aun así, la ganancia del formato de imagen es secundaria frente a la de que las imágenes bajo la línea de plegado dejen de competir por ancho de banda con el texto que se está leyendo.
Los retrasos del panel en tiempo real aparecieron a escala de 13 millones de usuarios. Kafka asumió el streaming, con limitación de ritmo en servidor y un AWS ALB repartiendo el tráfico. La limitación de ritmo es el elemento menos vistoso del conjunto y el más necesario, porque sin ella un solo cliente atrapado en un bucle de error ocupa un canal que pertenece a todos los demás.
La caché obsoleta se resuelve con Varnish, un VCL propio, purgas por webhook y Edge Side Includes para las secciones dinámicas, más versionado de URL. Los ESI responden a una tensión concreta: una página de artículo jurídico es idéntica para todos en la mayor parte de su superficie y solo depende, en un fragmento pequeño, de si quien lee tiene una reclamación abierta. Sin esa división, el fragmento pequeño invalida la caché de la página entera.
La demanda de recursos en horas punta la cubre el auto-escalado en AWS EC2 con CloudWatch, más Cloudflare Rate Limiting contra tráfico excesivo de bots. El auto-escalado arrastra una debilidad que se deriva directamente de la forma del tráfico de este negocio: reacciona a una carga que ya ocurrió, y una ola posterior a una cancelación masiva se forma en minutos. Por eso los umbrales están más bajos de lo que sugeriría un cálculo de costes en un día tranquilo, y ese margen está comprado a propósito.
Tecnologías utilizadas
Yoast SEO se ocupa de los metadatos, los sitemaps XML dinámicos y los avisos de actualización a los buscadores. UpdraftPlus ejecuta las copias a Amazon S3 con replicación y cifrado AES-256. Cloudflare aporta la capa de borde con Argo Smart Routing, compresión Brotli y protección DDoS mediante limitación de ritmo. Redis cachea en memoria, separado entre sesiones, formularios y panel. Varnish cachea en servidor con VCL propio, modo grace y ESI para bloques dinámicos. El modo grace merece frase aparte, porque sirve una página ligeramente desactualizada en el instante en que el backend deja de responder, en lugar de servir un error, y frente a tráfico que llega en oleadas esa es la diferencia entre un sitio más lento y un sitio caído.
Lighthouse ejecuta auditorías de Core Web Vitals dentro del proceso CI/CD en Jenkins. RabbitMQ encola el procesamiento de API y el envío de correo con reintentos y cola de mensajes muertos. Elasticsearch sostiene la búsqueda de vuelos y contenidos con coincidencia difusa y agregación, y esa tolerancia al error no es un refinamiento sino un requisito: los números de vuelo se copian de fotos de tarjetas de embarque, y los nombres de aeropuerto se escriben de veinte maneras en veinticuatro idiomas. Fastly reparte los medios geográficamente y Kafka transporta el flujo en tiempo real con particionamiento.
Gestión y soporte técnico
AirHelp requiere optimización y soporte continuos. Actualizo el núcleo y los plugins con regularidad, probando en un entorno de pruebas con copias en Amazon S3. Ese entorno es una copia de producción y no una instalación limpia, y esa distinción es lo que hace que la prueba valga algo. Una consulta que responde al instante contra doscientas reclamaciones puede crecer dos órdenes de magnitud contra millones de filas, y en una base vacía no lo ve nadie. Lo mismo ocurre con la tasa de acierto de caché, medible solo contra una distribución real de páginas de entrada.
Cloudflare, Redis y Fastly sostienen el rendimiento bajo tráfico global, mientras Varnish, RabbitMQ y Kafka estabilizan los procesos dinámicos. La monitorización corre sobre Elasticsearch y CloudWatch, las consultas SQL y NoSQL se revisan contra sus índices y la caché se invalida al cambiar el contenido. La señal que más importa ahí no es el tiempo de respuesta sino la tasa de acierto de caché, porque en un sitio con esta forma un fallo lleva la petición al origen por muy bien afinado que esté el origen.
La plataforma puede ampliarse con integraciones ERP, un módulo de análisis de vuelos o una sección de informes jurídicos. Cada una de esas extensiones choca con el mismo conflicto que recorre todo el proyecto: cuanto más contenido depende de quién lo mira, menos puede servirse desde el borde de la red, y a partir de ahí las decisiones tomadas aquí hay que recalcularlas en lugar de arrastrarlas.
Preguntas frecuentes
Respuestas prácticas para aplicar el tema en la ejecución real.
¿Qué alcance tuvo el proyecto Airhelp?
#¿Cómo fue la entrega de Airhelp?
#¿Qué fue lo más difícil técnicamente en Airhelp?
#¿Qué parte de Airhelp se puede reutilizar en otro proyecto?
#¿Necesitas un FAQ adaptado a tu sector y mercado? Preparamos una versión alineada con tus objetivos de negocio.
Hablemos