Travel & Flight SearchField engagement

Every extra hub multiplies the live price calls, so the engineering is a budget for how far each search fans out.

Architecture and delivery lead.
4.5 s
average search
8.7 s
p95
1.3%
timeouts
35%
cache hit
candidate hubsgenerated from the route graphsurvive the cached gatefiltered on a price nobody paid forpriced livethe only calls that cost moneybeat the nonstopwhat the user is shown
One request expands into many candidate hubs, and the cost of the search is set by how many of them reach a paid call. The cheap gate removes most of them, so the budget holds per search rather than per hub.
What was at stake

A self-transfer search only earns its place on the screen when it finds a combination cheaper than flying direct. To find one, every query fans out across many intermediate airports, and each extra hub multiplies the number of live price calls. Every one of those calls costs money and adds waiting time. The product exists only if a search can be wide enough to find the cheap combination and cheap enough to run on every query, including the many that never become a booking.

How this works

A flight price does not exist until someone asks for it.

It is natural to imagine flight prices sitting in a table somewhere, waiting to be read. They are not. An airline publishes fares, each with rules about when it can be sold, how far ahead, for which days and under which conditions. Separately, it controls how many seats it releases at each fare level, and it changes that allocation as the flight fills. The price a traveller sees is worked out at the moment of the question: find the cheapest fare whose rules are satisfied and whose seats are still open on that flight, then add taxes and charges. That calculation is what a live pricing call buys. It is slow because it is genuinely computed, and it is metered because most searches never turn into bookings, so providers charge for questions or ration them.

Airlines also price each market on its own terms. The fare between two cities reflects who else flies the route and what travellers will pay. Distance has little to do with it. A route served by a single carrier can cost more than two legs through a hub where several low-cost airlines compete. Many of those airlines sell only point to point and will not issue one ticket that connects onto another carrier. So a cheaper journey exists, and no airline will sell it as a single trip. A self-transfer product assembles it from two separate tickets. That gap between how markets are priced and how tickets are sold is the reason the product category exists.

Finding the cheaper journey is a combinatorial problem. For one origin and one destination, every plausible intermediate airport adds two more legs to price: origin to hub, and hub to destination. Flexible dates multiply that again. A search across even a modest set of hubs becomes a large number of live calls, and every extra hub adds cost and waiting time for every user, including people who are only browsing. Caches help, because a recent price is a good hint. A cached price is still a snapshot of a seat allocation that keeps moving. It can tell you where to look. It cannot tell you what the traveller will pay.

One origin, one destination
what the traveller typed
Plausible hubs between them
each one a possible saving
Two legs per hub
origin to hub, hub to destination
Dates multiply it again
flexible travellers widen every leg
Live calls per search
each one computed, billed and waited for
The fan-out is set by geography and dates. The budget has to be set by design.
FigureHow one question from a traveller becomes many paid questions to providers. Each candidate hub turns into two legs that have to be priced live, and flexible dates multiply the total again. The width that makes a self-transfer search useful is the same width that makes it expensive.
The constraint

The brief read as flight search. In practice it was a spending problem. One origin and destination has to expand into a bounded set of candidate intermediate airports, price only the ones worth pricing, and answer inside the patience of someone holding a phone. Every paid call spent on a hub that was never going to win is budget taken from the hub that might.

Where it usually breaks

Wide searches fail on cost long before they fail with an error.

The first way a fan-out search breaks is financial, and it is invisible in testing. A search that prices every hub live returns good results on a developer's machine and on a quiet afternoon. At real traffic the bill for live calls outgrows what bookings earn, because most searches are browsing and every one of them was charged in full. Nothing crashes. The product just stops making sense.

The second is latency in the tail. A fan-out cannot answer until its last call returns, so the slowest provider response sets the pace. The more calls a search makes, the more likely one of them is slow, which means the worst searches get worse faster than the average does. Fire all those calls at once and a provider's rate limit starts rejecting them, retries add more load, and the widest searches begin timing out at exactly the moment they were most likely to find something good.

01

The unbounded fan-out

Every hub is priced live on every search. Cost and waiting time rise with width, so the product becomes unaffordable at the moment it becomes useful.

02

The slowest call sets the pace

A fan-out returns when its last answer lands. More calls mean more chances that one is slow, so the tail grows faster than the average.

03

The rate-limit cliff

Every call fires at once, a provider starts refusing them, and the retries make the next burst worse. Searches go from fast to failing with little in between.

04

The cached price shown as a quote

A cached figure appears on the results page. The traveller clicks through, the live price is higher, and the product reads as bait and switch.

05

The saving nobody checked

A self-transfer result is ranked without the nonstop priced alongside it, so a combination that saves nothing is presented as a deal.

Every hub considered
including ones with no sensible routing
Two live legs each
priced whether or not they could win
All calls fired together
no ceiling on concurrency
Wait for the last answer
the slowest provider sets the pace
The failure point
Slow, rate limited and over budget on the same search
every call is billed, and the widest searches time out first
The fix is to decide what to ask before asking anything.
FigureThe search that asks everything. Each piece looks reasonable on its own. Put together, every hub in the network turns into paid calls fired at the same moment, and the search ends up slow, rate limited and over budget, all at once.
The fork

The reflex, and the fix.

Road not taken

Query every provider for every hub

Pull

It is complete, and it is the most literal reading of the brief. Nothing can be missed if everything is asked.

Why not

Cost and latency both grow with the number of hubs. A search wide enough to be useful becomes too slow to wait for and too expensive to serve.

Road taken

Generate, filter cheaply, then spend

Accepted

Two stages in front of live pricing: a step that generates candidate hubs and a gate that checks them against cached prices. That is more machinery than calling the provider directly.

Bought

A wide search that stays inside a per-request cost budget, because the expensive call is only made for hubs that survived a cheap filter.

Decision

The expensive call goes last. The cheap filter removes most of the candidates, and the budget belongs to the search as a whole, so every hub has to earn its share of it.

How it was built
01Candidate hubs
›
02Cached pre-filter
›
03Bounded fan-out
›
04Live pricing
›
05Rank
01

Candidate hubs before any pricing

A route graph and the corridor rules produce the intermediate airports worth considering for this origin and destination. The fan-out starts from a reasoned shortlist, so an airport with no sensible routing between the two ends never costs a call.

02

A cached-price gate

Hubs on the shortlist are checked against a cached price feed first. The cached figure is a gate and never a quote. Its one job is to decide which hubs justify a paid call.

03

A ceiling on concurrent calls

The orchestrator holds a concurrency ceiling. A very wide search therefore gets slower. It does not trip a provider's rate limit, and it does not run up a bill nobody budgeted for.

04

Pricing the nonstop in the same pass

A self-transfer result that does not beat the nonstop is not a result. The nonstop baseline is priced in the same search, so the comparison is like for like, two prices taken at the same moment.

Inside the key decisions

What each choice cost, and what it bought.

AGenerating candidates before pricing anything

The cheapest live call is the one never made, and the surest way not to make it is to never consider the hub. A route graph and corridor rules turn an open question into a shortlist before any money is spent. The cost is a route graph to maintain, and a hub left off the list can never be found, even on the day it would have won. What it buys is control: the width of the fan-out becomes a design decision. Searching everything pays for impossible hubs on every query, forever.

BUsing the cache as a gate and never as a quote

Cached prices were given exactly one job, deciding which hubs deserve a paid call. That has a cost. A hub whose cached price is stale on the high side can be filtered out when a fresh price would have won, and a hub that passes can re-price upward on the live call. What it buys is that staleness can never mislead a traveller, because no number on the results page ever comes from the cache. Showing cached prices directly would have been faster and cheaper, and it would have put out-of-date figures in front of people at the exact moment they decide.

CAccepting slower wide searches through a concurrency ceiling

Capping how many calls run at once means the widest searches take longer than they would with everything fired together. In return the system degrades gradually and predictably. A wide search gets slower. It does not trip a provider's limit, start a retry spiral, or produce a bill nobody planned for. Unbounded parallelism looks faster in a demo and falls off a cliff in production, and a cliff is the one shape a consumer product cannot recover from mid-search.

DPricing the nonstop in the same pass

Every search pays for one more live call, the direct flight, on top of the hubs. That call is what keeps the product honest. A self-transfer result is only shown as a saving when it beats a nonstop priced in the same search, at the same moment. Comparing against a typical or cached nonstop price would make savings look larger on the page and then disappear at checkout, which does more damage to trust than showing no saving at all.

The gate
Does the cached price make this hub worth a paid live call?
Spend a live pricing call
the live answer is the only price a traveller sees
Drop the hub before any call
no charge, no waiting, no share of the rate limit used
The cached price decides where to look. Only the live price is ever shown.
FigureThe decision every candidate hub has to pass before it costs anything. The cached price is used only to answer this question. It is never shown to a traveller, so its staleness can cost a missed saving but can never produce a misleading price.
How it was measured

A single headline number hides where a system fails. This work was scored on the dimensions that actually decide whether it holds in production, measured on real, held-out cases rather than the demo path.

Search latency at the median and the tailCost per search against the API budgetCache hit rate on the pre-filterTimeout rate under a wide fan-out
What every search has to do
Fan out across candidate hubs
wide enough to find the cheap combination
Price the nonstop alongside
so the saving is like for like
Stay inside a per-search budget
cost held per request, not per hub
Measured on the delivered system
Average search
4.5 s
Ninety-fifth percentile
8.7 s
Timeouts
1.3%
Cache hit on the pre-filter
35%
a bounded fan-out
Every figure was measured on the delivered system.
FigureWhat every search had to do, and how the delivered system measured against it. Average and tail latency show how long a person waits. The timeout rate shows how often a search ran out of time, and the cache hit rate shows how often the cheap filter had a cached price to work with.
figures

Latency, timeout and cache-hit figures are measured on the delivered system.

What it produces
Without this discipline

A search that asks every provider about every hub, returns correct results eventually, and costs more per search than the booking earns.

This system

A bounded fan-out that generates candidates, filters them against cached prices and spends live calls only on the survivors. It returns in 4.5 seconds on average and 8.7 seconds at the ninety-fifth percentile, with a 1.3 percent timeout rate and a 35 percent cache hit rate on the pre-filter.

expand, then filter cheaplive calls on survivors onlycost budget per searchno model in it
The operating envelope

What it owns, and what it hands to a person.

Handled with confidence
Origin and destination pairs inside the corridor set
Wide hub fan-out within the per-search budget
Flagged for review
Hubs whose cached price is stale past its window
Searches approaching the concurrency ceiling
Out of scope by design
Guaranteeing a cached price as a bookable quote
Inventory a provider does not expose
The honest limit

Results are only as fresh as the cached pre-filter. A hub that passes the gate can still come back more expensive on the live call. The live number is authoritative and the cached one only decides what to ask about. The product is built to treat them that way and to show the live price, even when it is the less flattering of the two.

What it generalizes to

Any fan-out across paid providers has this shape. Expand into candidates, filter with something cheap, spend the expensive call only on what survived, and hold the budget per request so a wide request cannot quietly cost more than it earns.

What to take from this

A flight price is calculated when someone asks for it, from fare rules and the seats still open, which is why live pricing is slow, metered and rationed.

Self-transfer journeys exist because airlines price each market separately and many will not sell a connection onto another carrier. The saving is genuine and so is the cost of finding it.

In any fan-out, cost and waiting time grow with width. Set the budget per request and make each candidate earn its share before it is called.

A cached price is a good hint about where to look and a poor promise about what someone will pay. Use it to filter and never to quote.

Cap concurrency so that a wide request gets slower gradually. An uncapped fan-out is fast until it hits a provider's limit, and then it fails all at once.

How we engage

You have a system like this one.
Tell us where it stands.

Whether it is failing, not yet built, or about to meet a scale it has never seen, we can tell you what we see.

Start a conversation
mostafa@opulion.dev · Response within 24 hours · By inquiry