All Articles
Engineering Tariq Mansour

How We Designed the Matching Algorithm for Letswork

Whiteboard diagram of matching algorithm decision tree with workspace and member signals

Matching is the word that sits at the center of what Letswork does. A member needs a workspace. Operators have available space. The platform should surface the right option for the right person at the right moment. The problem sounds simple when you describe it that way. The actual design decisions are considerably messier.

This post is a behind-the-scenes account of how we designed the matching algorithm. Not a technical spec, not a research paper, but an honest account of the choices we made, why we made them, and what we got wrong in the first version. We are sharing this because we think the reasoning behind these decisions is more useful to other builders than a surface-level description of the algorithm's output.

The first question: what does a good match actually mean?

Before you can build a matching algorithm, you have to define what you are optimizing for. That sounds obvious but it is not. A "good match" for a marketplace has at least three possible definitions:

A good match for the member means the member finds a space that meets their specific needs for that session: right environment, right price, right time slot, right location. A good match for the operator means the booking fills a slot that would otherwise be empty, ideally during a high-value time window, at a price the operator is satisfied with. A good match for the platform means repeated successful bookings on both sides, which means the match needs to create satisfaction that brings both parties back.

These are correlated but not identical. An algorithm that is purely member-centric will route too much demand to popular spaces and undersell less-visible operators who have quality inventory. An algorithm that is purely operator-centric will push inventory the operator needs to move but that may not be what the member actually wants. We are not claiming to have solved the three-way optimization perfectly. But being explicit about the tension shaped the design decisions we made.

Why we weight time-slot fit over proximity

The first design decision that generated the most internal debate was how to weight time-slot availability against proximity. A naive matching algorithm would rank results primarily by distance from the member's stated or inferred location. The closest available desk that meets basic criteria rises to the top.

We chose to weight time-slot fit more heavily than proximity, and here is the reasoning. In Dubai Internet City, the clusters of coworking spaces are close enough that proximity differences within DIC are rarely the deciding factor for a knowledge worker booking a desk. A five-minute walk difference in a climate-controlled environment does not have the same weight as a 30-minute availability gap. A member who needs a desk from 10am to 2pm and there is only one space with confirmed availability for that window should see that space ranked first, even if a slightly closer space has an uncertain slot that might open up.

The edge case that exposed the limits of our first proximity-heavy model was what we started calling the "crowded corridor" problem. Three or four operators along the main DIC walkway were capturing a disproportionate share of bookings simply because they were physically closer to the pedestrian entry point, while equally good spaces a three-minute walk further were sitting empty. The proximity ranking was creating a self-reinforcing concentration that was bad for operators and, we suspected, not actually serving members better, since the popular spaces were often near capacity while the slightly further ones had comfortable availability.

Reweighting toward time-slot fit distributed demand more evenly and, based on early feedback, did not produce member complaints about being sent to "less convenient" spaces. The member's primary need is a confirmed desk for the time they need it. Proximity matters for tiebreaking, not for primary ranking.

Handling ambiguous member signals

Members interact with the search interface in ways that do not always reflect their actual preferences clearly. A member who searches for "quiet focus space, 9am to 1pm" and then books a lively social coworking space sends a conflicting signal. Do we update their preference profile toward social environments, or do we treat it as a one-time choice driven by availability at that moment?

Our first approach tried to infer preference updates from every booking. After a few weeks of data, we noticed that the inferred profiles were becoming increasingly noisy for members who used the platform under varied circumstances: sometimes needing focus, sometimes meeting with clients, sometimes just needing a desk for a short slot before a flight. The algorithm was averaging across these contexts and producing a generic profile that was less useful than no profile at all.

We shifted to session-context weighting. When a member specifies a session type explicitly (focus work, client meeting, casual), that session type anchors the matching for that booking and does not contaminate the inferred profile. Bookings where the member uses only generic search terms contribute more weakly to the preference model. The explicit signal the member sends is treated as truth for that session; the implicit signal from the booking choice is treated as evidence, weighted by how strongly it matches or contradicts prior behavior.

This is still imperfect. Members who never explicitly state session type generate slow-converging profiles. But it is significantly better than the first version, which would confidently infer a preference after two or three data points and lock the member into a ranking pattern that stopped reflecting their actual behavior within a few weeks.

The operator side of the signal

Matching is bidirectional. We also had to decide how to represent operator inventory quality in the ranking signal. The obvious input is the booking history and ratings, but we are early enough that most operators have thin rating histories. A space with five ratings and a 4.8 average and a space with two ratings and a 4.5 average are not meaningfully distinguishable on that basis alone.

The inputs we found more stable at small sample sizes were: check-in data quality (does the operator have consistent check-in logging, which we discussed in the occupancy model post), no-show recovery rate (what proportion of flagged no-shows does the operator act on within 30 minutes), and response latency for booking confirmations. These are signals about how reliable the operator is to work with, not just how good the space is aesthetically.

A member who books with a high-reliability operator and has a smooth experience is more likely to rebook than a member who books with a lower-reliability operator whose check-in process is inconsistent. The match quality is a function of the whole interaction, not just the initial search result. We weight operator reliability signals in the ranking because the match fails if the booking confirmation or check-in experience is poor, regardless of how well the space matched the member's stated preferences.

What the algorithm does not do

It is worth being direct about the limits of our current approach. The algorithm does not predict what kind of space a member will find valuable before they have used the platform. It learns from behavior, which means new members see a generic ranking until they have made three to five bookings. We surface a simplified preference questionnaire at onboarding to seed the profile, but the questionnaire is imprecise and the algorithm learns to discount self-reported preferences relative to observed behavior within a few bookings.

The algorithm also does not account for social dynamics within spaces. A coworking space where a member knows several regulars is a different experience than one where they know no one, and that difference affects whether the booking creates a return habit. We have no way to model that from booking data alone. It is a meaningful gap, and it is one of the reasons we think operator-level member community features, events, and the relationship quality the operator maintains with their members will remain important factors that no matching algorithm fully captures.

Where we are and what comes next

The current version of the matching algorithm performs reasonably well on the core task: surfacing confirmed-available options that fit the member's time window, weighted by operator reliability and the member's session context. It is not sophisticated enough to feel like it "knows" the member in the way a personalized recommendation system might after hundreds of data points. At the typical member usage frequency (four to eight sessions per month for active members), it takes two to three months to build a profile that meaningfully personalizes the ranking.

The next iteration we are working on is better handling of first-time or low-frequency member sessions, specifically improving the cold-start ranking for members who are using the platform irregularly. For the DIC market, where a significant portion of potential users are transient professionals who may only need workspace for a few days during a visit, a ranking that requires ten prior bookings to personalize is not fast enough to create a good first impression.

Building a matching algorithm for a two-sided marketplace is incremental in a way that most product work is not. Each decision changes what data you collect, which changes what you can optimize in the next iteration. You cannot predict what the right algorithm looks like from the outside. You have to build it, watch what it does to member and operator behavior, and let those patterns tell you what to change. That is the part of this work we find genuinely interesting, and it is the part that does not end.