Tarun Upaday.

Thinking in Public

Routing optimization isn't a shortest-path problem

Ask someone to describe flight search and they’ll describe a sorting problem: find every route, rank by price or time, show the top of the list. That’s most of what consumer travel sites do, and it’s a reasonable model when the traveler is optimizing for themselves.

It breaks the moment a second party has a say in what “best” means.

Two different questions wearing one label

A corporate travel policy is not a preference. It’s a set of hard constraints — a fare cap, an allowed cabin class, a list of blocked carriers — that exist whether or not the traveler likes them. A traveler’s preference for nonstop routes, a particular alliance, or avoiding red-eyes is real and worth respecting, but it’s a different kind of thing: negotiable, personal, and secondary to policy when the two conflict.

Collapsing both into a single ranked list hides which one is doing the work. A flight that tops the list because it’s cheap and a flight that tops the list because it’s the traveler’s preferred airline look identical in a plain sorted view. They shouldn’t be treated the same, because overriding one requires an approval and overriding the other just requires clicking a different result.

The fix is structural, not cosmetic: policy is a filter applied first, and preference is a ranking applied only within whatever survives that filter. A flight search built this way doesn’t produce one ranked list. It produces a labeled one — in-policy or out-of-policy, with the reason attached — and only then ranks within the group the traveler is actually allowed to book.

What that looks like built

I built a small MCP server that does exactly this against live Google Flights results: search, label every itinerary in-policy or out-of-policy against a policy file, then rank the survivors by preference — nonstop, alliance, red-eye avoidance, cheapest versus fastest versus balanced. Two separate files drive it. One encodes the hard rules: fare caps, cabin class, blocked airlines. The other encodes personal taste. They’re never merged into a single score, because merging them is exactly the move that hides which constraint is doing the work.

The more deliberate design choice is what the tool refuses to do. It never visits an airline or OTA site, and it never attempts to complete a booking. It hands back a ranked, labeled list and a link to finish the booking yourself. That’s not a technical limitation — a browser-driven variant in the same repo can drive a booking up to the payment screen. It’s a boundary, chosen on purpose: the system optimizes and recommends, a person authorizes the transaction that actually moves money.

Why “optimal” doesn’t generalize

The instinct to build one global ranking function comes from treating routing like a search problem with a single right answer. It isn’t. The same itinerary is optimal for one traveler and out of policy for another, preferred by someone who hates red-eyes and irrelevant to someone who doesn’t care. “Optimal” only means something once you’ve specified whose constraints and whose preferences are in play, and those two things change per person and per trip even when the underlying flight data doesn’t change at all.

The interesting engineering problem was never finding the cheapest path through a graph of routes. It was building a system honest enough to keep “what’s allowed” and “what’s preferred” as separate, inspectable layers instead of quietly averaging them into a single number that looks authoritative and explains nothing.