The matching engine behind a live, still-synthetic marketplace
EduMatch (edumatch.asafarim.com) is a real, deployed app today — accounts, bookings, tutor invites — running in showcase mode on synthetic data. This benchmark exists because the part worth inspecting closely, the ranking logic, is easier to trust in isolation than inside a full stateful app.
benchmarks/edumatch.What was ported, what wasn't
| Stage | Stack | Core idea | Where it hit a wall |
|---|---|---|---|
| Personal project | Next.js · Prisma/Postgres · Stripe Connect · geocoding | A full tutoring marketplace: intake, tutor discovery, quotes, Stripe-split payments, disputes, verification, notifications. | It grew into a real deployed app (edumatch.asafarim.com, Auth.js accounts, Postgres, a conversational Learning Brief flow) — but the matching algorithm's correctness and fairness are hard to see or argue with from inside a full stateful product. |
| This benchmark | Pure JS engine · synthetic fixtures · client-side demo | Extract a simplified version of the matching logic, make it explainable and adjustable, and prove it with deterministic tests you can run without a database. | Deliberately no accounts, payments, or live infrastructure — and its weights are illustrative, not a live mirror of the production engine, which has since evolved to eight factors (see Method on the overview page). |
How the benchmark is built
Hard requirements (subject, level, language, availability, mode/distance) are checked before any scoring happens. A tutor who fails one is never ranked — and the reason is recorded, not discarded.
Every ranked result carries a factor-by-factor breakdown (value × weight = contribution) that sums exactly to its composite score — there is no hidden step between 'why' and 'what.'
The engine is a single ESM module imported by the Node test suite, the fixture generator, and the Showcase's client-side Match Explorer — there is no second implementation to drift out of sync.
The legacy algorithm normalised rating as avg / 5, which let a single five-star review outrank a tutor with forty consistently strong ones. This version damps toward a neutral prior so review count matters.
What we gave up, and why
edumatch.asafarim.com is a live, working app, but every tutor profile is synthetic and checkout is a $0 mock — there is no real tutor supply and no real money moving. This benchmark's fixture-based demo makes the same honesty tradeoff on purpose, rather than dressing up synthetic data as a real marketplace.
Twelve tutors and six needs are enough to demonstrate the method precisely — a deliberate twin pair, a tight-availability case, a no-qualified-tutor case — but they are not a statistically representative population.
Lessons from the legacy system
The legacy scorer produced a single number. Retrofitting an explanation after the fact is much harder than building the factor breakdown as the primary output from day one, which is what this version does.
Saying "the algorithm doesn't use protected attributes" is a claim. A constraint-identical twin pair that must score identically is a test. See the Fairness page.
- Real tutor supply: manually onboard the first verified tutors in a narrow subject/region niche instead of synthetic profiles, to solve the two-sided cold-start problem.
- Live Stripe checkout in place of the $0 mock, and removal of the public 'showcase' disclaimer once real payments are wired up.
- Guardian approval flow for minors — the data model (isMinor, guardianName, guardianEmail) and the safeguarding match filter exist, but the approval gate before payment is not built yet.
- Localisation of the Learning Brief flow beyond English and Dutch (fr/de/lb currently fall back to English).