Portfolio

Airhelp - WordPress Project | WPPoland

AirHelp was founded in 2013 as a start-up and has grown to become the global leader in defending air passenger rights, helping over 13 million people underst...

#Websites
Airhelp - WordPress Project | WPPoland

#AirHelp, technology for the leading advocate of passenger rights

A sentence about how much compensation a passenger is owed is not one sentence translated twenty-four times. It is two dozen separate legal claims, and most of them are true only inside one border. A flight from Warsaw to London falls under a different regime after Brexit than the same flight inside the Union. A flight out of Sao Paulo is governed by rules that have nothing to do with either. That single fact shaped the content model of this project more than any framework choice did, so it belongs at the top rather than buried under a list of technologies.

AirHelp was founded in January 2013 by Henrik Zillmer and is headquartered in Berlin. Its legal footing in Europe is Regulation 261/2004, and outside it the company works through UK261, the Montreal Convention, Brazil’s ANAC 400 and several further national frameworks. It has helped over 13 million people understand their rights and claim compensation for delayed, cancelled or overbooked flights, represents them in disputes with airlines, and lobbies for fair rules at government level.

I designed and implemented the AirHelp website, leading a five-person team as Team Pilot. What follows is the reasoning behind the build, the places where it met resistance, and what each resolution cost. The implementation ran about six weeks from scope analysis to launch, and the layout and element placement came from the client.

#The purpose of AirHelp and its audience

Two audiences use this site and they have almost nothing in common.

The first is a passenger in an unusual state: sitting in a terminal after a cancellation, on a phone, often roaming, on a weak network, annoyed, holding nothing but a photograph of a boarding pass. This person did not come to read about the company. They came to find out whether they are owed anything, and they want the answer in two steps.

The second arrives from a search engine months after the flight, looking for a specific point of law. That traffic is steady, highly cacheable, reads long-form text, and lands on pages in twenty-four languages.

One platform has to serve both patterns under one domain, and most of the architectural tension in this project comes from exactly that. What is right for the first audience, minimal markup, server-rendered, nothing blocking the form, is wasted on the second. What is right for the second, aggressive edge caching of legal articles, is unusable for the first, because a claim status is by definition personal.

A network of law firms in 30 countries and a team of 700 employees, including the world’s largest group of lawyers specialising in air law, stands behind the content. For the site that means legal copy has a real owner on the client side and passes review before it reaches production. The content model had to carry that workflow rather than work around it.

The common mistake on a project like this is treating multilingual support as a translation layer bolted onto one site. Here that fails, because the difference between markets is jurisdictional, not linguistic.

The consequence for the content model is that each language variant has to be a first-class document with its own approval cycle, not a translation field attached to an original. A variant that has not been reviewed by a lawyer from that jurisdiction must not silently fall back to English, because then the site publishes a foreign legal position as its own. It has to disappear from navigation and from hreflang instead. That is the only safe default, and it is also the one that generic multilingual plugins get wrong, because their default is always to show something rather than nothing.

The frontend runs on Next.js with server-side rendering, covering 24 languages through i18n, with WCAG 2.1 checks in mind and optimisation for mobile devices. Server rendering is not an aesthetic preference here. With legal content that needs to rank across dozens of markets, and with mobile devices on poor connections, moving page assembly into the visitor’s browser means the weakest phone on the worst network does the most work.

#Technical features of AirHelp

The compensation flow is a multi-step form that pulls flight data through GraphQL, connects to airline APIs, and writes the claim to PostgreSQL with AES-256 encryption. Choosing GraphQL over a set of REST calls has a concrete reason. Each step of the form needs a different slice of the same flight record. Under REST that ends either in fetching everything up front or in a request count that grows with the number of steps. Asking for exactly the fields the current step renders reduces it to one network round trip per step, which on a roaming phone is a difference you feel rather than a difference you measure.

The education section, with its legal articles, loads over a REST API cached in Redis and renders in React. The split between the two interfaces is deliberate. Editorial content is identical for every visitor and suits aggressive caching. Claim data suits no caching at all. Keeping both behind one interface would have saved a little code and cost the ability to set a separate lifetime for each.

The claim dashboard shows status in real time over WebSocket, cached in Memcached for low latency. The trade-off deserves naming. A persistent connection consumes server resources for as long as a tab stays open, and a claim status changes perhaps once every few weeks. Polling once a minute would be cheaper to run and sufficient for the information itself. The case for the open channel is the behaviour on the day the status actually changes, because that is the day the user sits on this page and reloads it by hand, and every manual reload costs more than keeping the channel.

Backups go to Amazon S3 automatically, with cross-region replication, versioning and Zstandard compression. Versioning matters more here than replication. Replication protects against losing a data centre, which is rare. Versioning protects against overwriting data through your own deployment mistake, which is not rare at all, and a copy replicated into three regions is then simply the same error in three places.

Technical SEO covers phrases such as “compensation for delayed flight”, dynamic XML sitemaps and accelerated indexing. With legal content that changes after every significant ruling, a sitemap generated once at build time stops describing the site within a week, so it is built from current database state instead.

#Accessibility and form resilience

WCAG 2.1 on a claims site is not mainly about contrast and alt text. The critical accessible component is the multi-step form, and the things that decide whether it works are the ones an automated audit will not catch: whether an error message is programmatically tied to the field it describes, whether advancing a step moves focus to the new step’s heading, whether progress is announced at all. A form that scrolls back to the top after a validation error without moving focus is a loop with no exit for a keyboard user, and it passes every automated test.

Surviving a dropped connection was treated as a functional requirement rather than a nicety. Form state persists locally after each step, so losing the session in a departure hall does not delete fifteen minutes of work. The cost is real and worth stating: claim data contains a flight number and personal details, and holding it in the browser demands its own clearing routine after submission and a clear rule for when an abandoned draft expires. Without that rule, a convenience turns into a compliance liability.

#Performance challenges and solutions

Database load under millions of users landed on PostgreSQL. The answer was Redis with persistence for the hottest queries, plus sharding with read replicas on Amazon RDS. Read replicas carry a price that is easy to forget: replication is asynchronous, so a read taken right after a submission may not see it yet. The user files a claim and lands on an empty list, which looks exactly like data loss. So reads immediately following a write go to the primary, and only paths that tolerate a few hundred milliseconds of lag are distributed.

Slow form loading came from the airline API integration, especially after mass cancellations. API calls moved to asynchronous processing through RabbitMQ, with a fallback to static data cached in Elasticsearch on timeout. The fallback is the important half. Airline systems tend to be unavailable exactly when they are most needed, because the same disruption that cancelled the flights is hammering their infrastructure. A form that shows an error at that moment loses a claim from someone who is fully entitled to compensation. A form that accepts the flight number without immediate confirmation and verifies it later does not.

High media latency hurt phones in regions with weak connectivity. Fastly with Brotli compression, WebP and lazy loading through the Intersection Observer API shortened delivery, and geo-optimisation shortened the distance. The gain from the image format is secondary, though, to the gain from below-the-fold images no longer competing for bandwidth with the text being read.

Real-time dashboard delays appeared at the scale of 13 million users. Kafka took over data streaming, with server-side throttling and an AWS ALB distributing traffic. Throttling is the least impressive item in that set and the most necessary, because without it one client stuck in an error loop occupies a channel that belongs to everybody else.

Stale cache after content changes was handled with Varnish, a custom VCL, webhook-driven purges and Edge Side Includes for dynamic sections, plus URL versioning for cache busting. Edge Side Includes answer a specific tension: a legal article page is identical for everyone in most of its area and depends on whether the reader has an open claim in a small fragment of it. Without that split, the small fragment invalidates caching for the whole page.

Peak resource demand is covered by auto-scaling on AWS EC2 with CloudWatch, and Cloudflare Rate Limiting against excess bot traffic. Auto-scaling has a weakness that follows directly from the traffic shape of this business: it reacts to load that has already happened, and a wave after a mass cancellation builds within minutes. Thresholds therefore sit lower than a quiet-day cost calculation would suggest, and that headroom is bought on purpose rather than left there by oversight.

#Technologies used

Yoast SEO handles metadata, dynamic XML sitemaps and update notifications to search engines. UpdraftPlus runs backups to Amazon S3 with replication and AES-256 encryption. Cloudflare provides the edge, with Argo Smart Routing, Brotli compression and DDoS protection through rate limiting. Redis caches in memory, sharded across sessions, forms and dashboards. Varnish caches server-side with a custom VCL, grace mode and ESI for dynamic blocks. Grace mode earns its own sentence, because it serves a slightly stale page at the moment the backend stops answering instead of serving an error, and against traffic that arrives in waves that is the difference between a slower site and an unavailable one.

Lighthouse runs Core Web Vitals audits inside the Jenkins CI/CD pipeline. RabbitMQ queues API processing and mail delivery with retries and a dead letter queue. Elasticsearch powers flight and content search with fuzzy matching and aggregation, and the fuzziness is a requirement rather than a refinement: flight numbers get copied from photographs of boarding passes, and airport names are spelled twenty ways across twenty-four languages. Fastly distributes media geographically, and Kafka carries the real-time stream with partitioning.

#Management and technical support

AirHelp needs continuous optimisation and support. I update the core and plugins regularly, testing on a staging environment with backups on Amazon S3. Staging is a copy of production rather than a clean install, and that distinction is what makes the test worth running. A query that returns instantly against two hundred claims can grow by two orders of magnitude against millions of rows, and on an empty database nobody sees it. The same applies to cache hit rate, which is only measurable against a real distribution of entry points.

Cloudflare, Redis and Fastly carry performance under global traffic, while Varnish, RabbitMQ and Kafka stabilise the dynamic processes. Monitoring runs on Elasticsearch and CloudWatch, SQL and NoSQL queries are reviewed against their indexes, and caches are invalidated on content change. The signal that matters most in that monitoring is not response time but cache hit rate, because with a site shaped like this one a miss sends the request to origin no matter how well origin has been tuned.

The platform can be extended with ERP integrations, a flight analysis module or a legal reporting section. Each of those runs into the same conflict that runs through the whole project: the more content depends on who is looking at it, the less can be served from the edge, and at that point the decisions made here have to be recalculated rather than carried over.

Article FAQ

Frequently asked questions

Practical answers to apply the topic in real execution.

SEO-readyGEO-readyAEO-ready4 Q&A
What scope did the Airhelp project cover?#
Airhelp sits in the Websites category and was first delivered in 2019. The entities behind it are React, Next.js, GraphQL, Redis and PostgreSQL.
How did delivery run for Airhelp?#
The build ran about six weeks and went live in 2019. It sits on React, Next.js, GraphQL, Redis and PostgreSQL. The layout came from the client. I built the templates and the content model against it, then checked the paths that carry traffic on a copy of production rather than on an empty install.
What was the hardest technical part of Airhelp?#
Holding React, Next.js, GraphQL, Redis and PostgreSQL together took the most care. Content, configuration and code stay in separate layers, so a rollback after launch moves one of them and not all three. Edge cases surface on a production copy, which is why the checks run there.
What part of Airhelp could be reused on another build?#
React, Next.js, GraphQL, Redis and PostgreSQL is the part that carries over; that layer looks much the same on the next build. What does not carry over is this project's content model and its integrations, written against one client's data for a Websites brief. A second build starts from a scope review, and the quote follows it.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss