AF
← Back to work

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

LGW → CAG · DIRECT

07:45 to 11:15

2h 30m · easyJet · 3 travellers

£1,437

or
chosen

STN → BGY → CAG · 1 STOP

12:05 to 18:05

6h · Ryanair via BGY · save £999

£438

One click becomes a labelled training pair: connection preferred over direct.

£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.

Current Flightly decision surface showing a direct flight and three one-stop alternatives with price trends and savings up to £999
01

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.

Flightly preference trainer above ranked one-stop routes with falling price trend graphs and savings percentages
02

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.

01

Retrieve

Direct and 1-stop flight candidates

→
02

Constrain

Hard connection and feasibility rules

→
03

Featurise

Eight bounded, explainable signals

→
04

Rank

Pairwise logistic utility score

→
05

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.

// score one feasible itinerary const utility = features.reduce((sum, key) => sum + profile.weights[key] * option[key], 0 ) // learn from A preferred over B Δx = features(A) − features(B) p = sigmoid(weights · Δx)

Example learned profile

illustrative weights after repeated choices

on-device
Affordability
+0.92
Journey efficiency
+0.63
Direct flight
-0.38
Departure convenience
+0.47
Arrival convenience
+0.21
Transfer comfort
+0.18
Ticket protection
+0.35
Baggage confidence
+0.14

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 + convenience
01

LGW → CAG · direct

£153
02

STN → BGY → CAG

£112
03

LTN → MXP → OLB

£126
12 choices→

After · for you

learned rank
01

STN → BGY → CAG

£112
02

LTN → MXP → OLB

£126
03

LGW → CAG · direct

£153

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.

01

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.

02

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.

03

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.

04

Private by architecture

Weights, comparison count and pair history are versioned in localStorage. No preference profile or behavioural event leaves the browser.

Deterministic layer

✓Connection airport allowed
✓Minimum transfer time met
✓Maximum journey time met
✓Flights occur in valid sequence

Learned layer

↗Predict personal utility
↗Order eligible itineraries
↗Select informative questions
↗Explain strongest preference signals

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.

01CalibrationCompare predicted choice probability with observed choices across real sessions.
02Ranking qualityMeasure held-out pairwise accuracy and NDCG against a consented user test set.
03StabilityTrack rank churn after each label and introduce confidence-aware blending if needed.
04GeneralisationTest whether preferences transfer across routes, seasons and different party sizes.

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.