Machine learning case study · 2026
Teaching a Flight Search Engine What “Best” Means with Machine Learning
Cheapest is not always best. Neither is fastest. Flightly is a live flight-search product with an online learning-to-rank engine that learns each traveller's price–convenience trade-offs from a handful of explicit choices. Every answer updates an on-device pairwise logistic model; active learning chooses the next comparison; deterministic safety rules keep the model away from infeasible connections. In observed searches, its self-transfer combinations have cut the total party price by as much as £1,000.
Model
Online pairwise logistic regression
Learning
Active learning · uncertainty sampling
Features
8 bounded, explainable signals
Stack
Next.js · TypeScript · FastAPI · PostgreSQL
Source
GitHub repository ↗LGW → CAG · DIRECT
07:45 to 11:15
2h 30m · easyJet · 3 travellers
£1,437
STN → BGY → CAG · 1 STOP
12:05 to 18:05
6h · Ryanair via BGY · save £999
£438
£1,000
largest observed party saving, route and date dependent
8
explainable preference features
9/9
held-out pair orderings correct
0
preference events uploaded
01 · The problem
Search Finds Flights. It Does Not Know You.
Traditional ranking compresses several competing objectives into one generic answer.
A family might accept a three-hour stop to save hundreds of pounds. A business traveller might pay more to land before dinner. Another person will take a connection, but only with a protected ticket. “Best” is a latent utility function, not a universal sort column.
Flightly generates direct and self-transfer options, applies minimum connection windows, calculates savings and exposes Pareto-efficient choices. The missing layer was preference: learning how one person values money, time, timing and risk without requiring a historical clickstream that did not exist.
The commercial signal is substantial. Across searches run so far, the largest observed saving has been approximately £1,000 for the selected party. That is not a guaranteed fare: it varies by route, date and baggage. It is evidence that explicitly enumerating feasible low-cost combinations can uncover value worth making legible.
The defensible market gap
Skyscanner does support self-transfer, and its 2025 Ryanair partnership gives it direct access to Ryanair content. The narrower gap is coverage and control: Skyscanner says self-transfer is generated for certain routes and quality-filtered, so it does not promise every feasible low-cost pairing. Flightly deliberately synthesises combinations across configured airport groups, including Ryanair and easyJet legs, and exposes the transfer, time penalty and saving behind every candidate. Skyscanner connection guidance ↗ · self-transfer methodology ↗ · Ryanair partnership ↗
The product question
Can a useful ranking model learn from a dozen high-information choices, while staying interpretable and private?
02 · Product proof
The Saving and the Trade-off Are Visible
The current Flightly interface places direct and one-stop candidates on one decision surface. Total party price, price trend, saving, stopover, journey penalty and airline mix are visible before the preference model begins to reorder the results.

One surface for price, time and connection risk
The current product shows the Flightly brand, a direct reference fare, three feasible one-stop journeys, daily price movement and party savings. BGY is present explicitly rather than disappearing inside an opaque result set.

The machine-learning interaction is part of the result
An actively selected pair asks the highest-information question. The answer updates eight weights on-device and immediately reorders eligible flights under “For you”.
03 · System design
A Learning Loop Inside the Search Product
The model does one job: order safe candidates by predicted personal utility.
Retrieve
Direct and 1-stop flight candidates
→Constrain
Hard connection and feasibility rules
→Featurise
Eight bounded, explainable signals
→Rank
Pairwise logistic utility score
→Learn
Choice becomes the next training label
Feedback
Chosen itinerary > rejected itinerary
One explicit answer creates a clean pairwise label.
Update
w ← w + η(1 − σ(w·Δx))Δx
Four small SGD passes make each choice visible without treating it as settled truth.
04 · Feature engineering
Eight Signals a Human Can Inspect
Every feature is bounded from 0–1 and oriented so a larger value means “more of the named benefit.” The sign and magnitude of the learned weight remain readable.
Example learned profile
illustrative weights after repeated choices
05 · Product outcome
The Ranking Changes, Not the Rules
A visible “For you” order appears after the first label and keeps learning across searches.
Before · generic rank
price + convenienceLGW → CAG · direct
STN → BGY → CAG
LTN → MXP → OLB
After · for you
learned rankSTN → BGY → CAG
LTN → MXP → OLB
LGW → CAG · direct
01
Pair selection
Choose the unseen comparison nearest a 50/50 prediction.
02
Immediate feedback
Re-score every option as soon as the user answers.
03
Long-term memory
Reuse the versioned profile on the next route and page load.
06 · Evaluation
A Small but Honest Benchmark
The goal was to verify that the online learner can recover an unseen utility function, not manufacture a production accuracy claim.
Held-out pairwise accuracy
100%
9 / 9
unseen pairs
Seven synthetic itineraries were labelled by a hidden utility function. The model trained on 12 actively selected comparisons, then ordered all nine remaining pairs correctly. This validates implementation and sample efficiency in a controlled case.
What it proves
The update rule, feature orientation and active-learning loop can recover a known preference ordering from sparse pairwise labels.
What it does not prove
It is not real-user accuracy, a statistically powered study or evidence that eight features capture every traveller. Those require consented user testing.
Engineering coverage
154 tests
Model behaviour, UI integration and the broader search experience passed at release.
07 · Responsible ML
The Model Is Deliberately Not in Charge
The safest architecture is one where learned preference and operational validity have different owners.
Cold start without fake certainty
Before the first choice, Flightly keeps its deterministic ordering. The model begins at zero and earns its influence through explicit labels rather than hidden assumptions.
Active learning, not random questions
The trainer selects the unseen pair closest to a 50/50 prediction, breaking ties with feature distance. Every prompt is chosen to reduce uncertainty.
Safety outside the model
Minimum transfer windows, airport feasibility and journey limits are hard filters. Personalisation can reorder safe candidates; it cannot make an unsafe itinerary eligible.
Private by architecture
Weights, comparison count and pair history are versioned in localStorage. No preference profile or behavioural event leaves the browser.
Deterministic layer
Learned layer
08 · Next validation
From Working Model to Measured Product
The production loop is live. The next work is empirical: measuring it with people rather than adding model complexity.
The design lesson
When there is no historical dataset, do not wait for one. Design a product interaction that creates high-quality labels, make uncertainty useful, and let the model earn trust one decision at a time.
Review the implementation
Explore the machine learning system
The source and model design are public; live search stays unlinked while provider costs are controlled.